mirror of
https://gitlab.freedesktop.org/mesa/drm.git
synced 2026-06-09 16:18:24 +02:00
nv50/kms: merge nv50_kms_crtc and nv50_crtc
This commit is contained in:
parent
1b6ff5a4db
commit
ef42af67ce
8 changed files with 551 additions and 662 deletions
|
|
@ -28,6 +28,9 @@
|
|||
#include "nv50_cursor.h"
|
||||
#include "nv50_lut.h"
|
||||
#include "nv50_fb.h"
|
||||
#include "nv50_kms_wrapper.h"
|
||||
extern struct nouveau_hw_mode *nv50_kms_to_hw_mode(struct drm_display_mode *);
|
||||
extern void nv50_kms_mirror_routing(struct drm_device *dev);
|
||||
|
||||
static int nv50_crtc_validate_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode)
|
||||
{
|
||||
|
|
@ -67,7 +70,7 @@ static int nv50_crtc_set_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mo
|
|||
|
||||
static int nv50_crtc_execute_mode(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
struct nouveau_hw_mode *hw_mode;
|
||||
uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end;
|
||||
uint32_t hunk1, vunk1, vunk2a, vunk2b;
|
||||
|
|
@ -134,7 +137,7 @@ static int nv50_crtc_execute_mode(struct nv50_crtc *crtc)
|
|||
|
||||
static int nv50_crtc_set_fb(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
uint32_t offset = crtc->index * 0x400;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
|
@ -167,7 +170,7 @@ static int nv50_crtc_set_fb(struct nv50_crtc *crtc)
|
|||
|
||||
static int nv50_crtc_blank(struct nv50_crtc *crtc, bool blanked)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
struct nouveau_gem_object *ngem = nouveau_gem_object(crtc->fb->gem);
|
||||
if (!ngem || !ngem->bo) {
|
||||
DRM_ERROR("no ngem/bo %p %p\n", ngem, ngem ? ngem->bo : NULL);
|
||||
|
|
@ -226,7 +229,7 @@ static int nv50_crtc_blank(struct nv50_crtc *crtc, bool blanked)
|
|||
|
||||
static int nv50_crtc_set_dither(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
uint32_t offset = crtc->index * 0x400;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
|
@ -256,7 +259,7 @@ static void nv50_crtc_calc_scale(struct nv50_crtc *crtc, uint32_t *outX, uint32_
|
|||
|
||||
static int nv50_crtc_set_scale(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
uint32_t offset = crtc->index * 0x400;
|
||||
uint32_t outX, outY;
|
||||
|
||||
|
|
@ -320,7 +323,7 @@ static int nv50_crtc_calc_clock(struct nv50_crtc *crtc,
|
|||
clk = hw_mode->clock;
|
||||
|
||||
/* These are in the g80 bios tables, at least in mine. */
|
||||
if (!get_pll_limits(crtc->dev, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index), &limits))
|
||||
if (!get_pll_limits(crtc->base.dev, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index), &limits))
|
||||
return -EINVAL;
|
||||
|
||||
minvco1 = limits.vco1.minfreq, maxvco1 = limits.vco1.maxfreq;
|
||||
|
|
@ -402,7 +405,7 @@ static int nv50_crtc_calc_clock(struct nv50_crtc *crtc,
|
|||
|
||||
static int nv50_crtc_set_clock(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
|
||||
uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index);
|
||||
|
||||
|
|
@ -435,7 +438,7 @@ static int nv50_crtc_set_clock(struct nv50_crtc *crtc)
|
|||
|
||||
static int nv50_crtc_set_clock_mode(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
||||
|
|
@ -445,16 +448,16 @@ static int nv50_crtc_set_clock_mode(struct nv50_crtc *crtc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int nv50_crtc_destroy(struct nv50_crtc *crtc)
|
||||
static void nv50_crtc_destroy(struct drm_crtc *drm_crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nv50_display *display = nv50_get_display(dev);
|
||||
struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
||||
if (!display || !crtc)
|
||||
return -EINVAL;
|
||||
if (!crtc)
|
||||
return;
|
||||
|
||||
drm_crtc_cleanup(&crtc->base);
|
||||
|
||||
list_del(&crtc->item);
|
||||
|
||||
|
|
@ -464,56 +467,539 @@ static int nv50_crtc_destroy(struct nv50_crtc *crtc)
|
|||
|
||||
kfree(crtc->mode);
|
||||
kfree(crtc->native_mode);
|
||||
|
||||
if (dev_priv->free_crtc)
|
||||
dev_priv->free_crtc(crtc);
|
||||
|
||||
return 0;
|
||||
kfree(crtc);
|
||||
}
|
||||
|
||||
int nv50_crtc_create(struct drm_device *dev, int index)
|
||||
static int nv50_kms_crtc_cursor_set(struct drm_crtc *drm_crtc,
|
||||
struct drm_file *file_priv,
|
||||
uint32_t buffer_handle,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nv50_crtc *crtc = NULL;
|
||||
struct drm_device *dev = drm_crtc->dev;
|
||||
struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
|
||||
struct nv50_display *display = nv50_get_display(crtc->base.dev);
|
||||
struct drm_gem_object *gem = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (width != 64 || height != 64)
|
||||
return -EINVAL;
|
||||
|
||||
if (buffer_handle) {
|
||||
gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
|
||||
if (!gem)
|
||||
return -EINVAL;
|
||||
|
||||
ret = nouveau_gem_pin(gem, NOUVEAU_GEM_DOMAIN_VRAM);
|
||||
if (ret) {
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
drm_gem_object_unreference(gem);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
crtc->cursor->set_bo(crtc, gem);
|
||||
crtc->cursor->set_offset(crtc);
|
||||
ret = crtc->cursor->show(crtc);
|
||||
} else {
|
||||
crtc->cursor->set_bo(crtc, NULL);
|
||||
crtc->cursor->hide(crtc);
|
||||
}
|
||||
|
||||
display->update(display);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nv50_kms_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y)
|
||||
{
|
||||
struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
|
||||
|
||||
return crtc->cursor->set_pos(crtc, x, y);
|
||||
}
|
||||
|
||||
void nv50_kms_crtc_gamma_set(struct drm_crtc *drm_crtc, u16 *r, u16 *g, u16 *b,
|
||||
uint32_t size)
|
||||
{
|
||||
struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
|
||||
|
||||
if (size != 256)
|
||||
return;
|
||||
|
||||
crtc->lut->set(crtc, (uint16_t *)r, (uint16_t *)g, (uint16_t *)b);
|
||||
}
|
||||
|
||||
int nv50_kms_crtc_set_config(struct drm_mode_set *set)
|
||||
{
|
||||
int rval = 0, i;
|
||||
uint32_t crtc_mask = 0;
|
||||
struct drm_device *dev = NULL;
|
||||
struct drm_nouveau_private *dev_priv = NULL;
|
||||
struct nv50_display *display = NULL;
|
||||
int rval = 0;
|
||||
struct drm_connector *drm_connector = NULL;
|
||||
struct drm_encoder *drm_encoder = NULL;
|
||||
struct drm_crtc *drm_crtc = NULL;
|
||||
|
||||
struct nv50_crtc *crtc = NULL;
|
||||
struct nv50_output *output = NULL;
|
||||
struct nv50_connector *connector = NULL;
|
||||
struct nouveau_hw_mode *hw_mode = NULL;
|
||||
struct nv50_fb_info fb_info;
|
||||
|
||||
bool blank = false;
|
||||
bool switch_fb = false;
|
||||
bool modeset = false;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
||||
/* This allows the public layer to do it's thing. */
|
||||
if (dev_priv->alloc_crtc)
|
||||
crtc = dev_priv->alloc_crtc(dev);
|
||||
/*
|
||||
* Supported operations:
|
||||
* - Switch mode.
|
||||
* - Switch framebuffer.
|
||||
* - Blank screen.
|
||||
*/
|
||||
|
||||
/* Sanity checking */
|
||||
if (!set) {
|
||||
DRM_ERROR("Sanity check failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!set->crtc) {
|
||||
DRM_ERROR("Sanity check failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (set->mode) {
|
||||
if (set->fb) {
|
||||
if (!drm_mode_equal(set->mode, &set->crtc->mode))
|
||||
modeset = true;
|
||||
|
||||
if (set->fb != set->crtc->fb)
|
||||
switch_fb = true;
|
||||
|
||||
if (set->x != set->crtc->x || set->y != set->crtc->y)
|
||||
switch_fb = true;
|
||||
}
|
||||
} else {
|
||||
blank = true;
|
||||
}
|
||||
|
||||
if (!set->connectors && !blank) {
|
||||
DRM_ERROR("Sanity check failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Basic variable setting */
|
||||
dev = set->crtc->dev;
|
||||
dev_priv = dev->dev_private;
|
||||
display = nv50_get_display(dev);
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
/**
|
||||
* Wiring up the encoders and connectors.
|
||||
*/
|
||||
|
||||
/* for switch_fb we verify if any important changes happened */
|
||||
if (!blank) {
|
||||
/* Mode validation */
|
||||
hw_mode = nv50_kms_to_hw_mode(set->mode);
|
||||
|
||||
rval = crtc->validate_mode(crtc, hw_mode);
|
||||
|
||||
if (rval != MODE_OK) {
|
||||
DRM_ERROR("Mode not ok\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
drm_connector = set->connectors[i];
|
||||
if (!drm_connector) {
|
||||
DRM_ERROR("No connector\n");
|
||||
goto out;
|
||||
}
|
||||
connector = to_nv50_connector(drm_connector);
|
||||
|
||||
/* This is to ensure it knows the connector subtype. */
|
||||
drm_connector->funcs->fill_modes(drm_connector, 0, 0);
|
||||
|
||||
output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
|
||||
if (!output) {
|
||||
DRM_ERROR("No output\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rval = output->validate_mode(output, hw_mode);
|
||||
if (rval != MODE_OK) {
|
||||
DRM_ERROR("Mode not ok\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* verify if any "sneaky" changes happened */
|
||||
if (output != connector->output)
|
||||
modeset = true;
|
||||
|
||||
if (output->crtc != crtc)
|
||||
modeset = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we verified if anything changed, fail if nothing has. */
|
||||
if (!modeset && !switch_fb && !blank)
|
||||
DRM_INFO("A seemingly empty modeset encountered, this could be a bug.\n");
|
||||
|
||||
/* Validation done, move on to cleaning of existing structures. */
|
||||
if (modeset) {
|
||||
/* find encoders that use this crtc. */
|
||||
list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) {
|
||||
if (drm_encoder->crtc == set->crtc) {
|
||||
/* find the connector that goes with it */
|
||||
list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
|
||||
if (drm_connector->encoder == drm_encoder) {
|
||||
drm_connector->encoder = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
drm_encoder->crtc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* now find if our desired encoders or connectors are in use already. */
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
drm_connector = set->connectors[i];
|
||||
if (!drm_connector) {
|
||||
DRM_ERROR("No connector\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!drm_connector->encoder)
|
||||
continue;
|
||||
|
||||
drm_encoder = drm_connector->encoder;
|
||||
drm_connector->encoder = NULL;
|
||||
|
||||
if (!drm_encoder->crtc)
|
||||
continue;
|
||||
|
||||
drm_crtc = drm_encoder->crtc;
|
||||
drm_encoder->crtc = NULL;
|
||||
|
||||
drm_crtc->enabled = false;
|
||||
}
|
||||
|
||||
/* Time to wire up the public encoder, the private one will be handled later. */
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
drm_connector = set->connectors[i];
|
||||
if (!drm_connector) {
|
||||
DRM_ERROR("No connector\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
|
||||
if (!output) {
|
||||
DRM_ERROR("No output\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
output->base.crtc = set->crtc;
|
||||
set->crtc->enabled = true;
|
||||
drm_connector->encoder = &output->base;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable crtc.
|
||||
*/
|
||||
|
||||
if (blank) {
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
set->crtc->enabled = false;
|
||||
|
||||
/* disconnect encoders and connectors */
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
drm_connector = set->connectors[i];
|
||||
|
||||
if (!drm_connector->encoder)
|
||||
continue;
|
||||
|
||||
drm_connector->encoder->crtc = NULL;
|
||||
drm_connector->encoder = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All state should now be updated, now onto the real work.
|
||||
*/
|
||||
|
||||
/* mirror everything to the private structs */
|
||||
nv50_kms_mirror_routing(dev);
|
||||
|
||||
/**
|
||||
* Bind framebuffer.
|
||||
*/
|
||||
|
||||
if (switch_fb) {
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
/* set framebuffer */
|
||||
set->crtc->fb = set->fb;
|
||||
|
||||
/* set private framebuffer */
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
fb_info.gem = nv50_framebuffer(set->fb)->gem;
|
||||
fb_info.width = set->fb->width;
|
||||
fb_info.height = set->fb->height;
|
||||
fb_info.depth = set->fb->depth;
|
||||
fb_info.bpp = set->fb->bits_per_pixel;
|
||||
fb_info.pitch = set->fb->pitch;
|
||||
fb_info.x = set->x;
|
||||
fb_info.y = set->y;
|
||||
|
||||
rval = crtc->fb->bind(crtc, &fb_info);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("fb_bind failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* this is !cursor_show */
|
||||
if (!crtc->cursor->enabled) {
|
||||
rval = crtc->cursor->enable(crtc);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("cursor_enable failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blanking.
|
||||
*/
|
||||
|
||||
if (blank) {
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
rval = crtc->blank(crtc, true);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("blanking failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* detach any outputs that are currently unused */
|
||||
list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) {
|
||||
if (!drm_encoder->crtc) {
|
||||
output = to_nv50_output(drm_encoder);
|
||||
|
||||
rval = output->execute_mode(output, true);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("detaching output failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change framebuffer, without changing mode.
|
||||
*/
|
||||
|
||||
if (switch_fb && !modeset && !blank) {
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
rval = crtc->set_fb(crtc);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("set_fb failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* this also sets the fb offset */
|
||||
rval = crtc->blank(crtc, false);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("unblanking failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal modesetting.
|
||||
*/
|
||||
|
||||
if (modeset) {
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
/* disconnect unused outputs */
|
||||
list_for_each_entry(output, &display->outputs, item) {
|
||||
if (output->crtc) {
|
||||
crtc_mask |= 1 << output->crtc->index;
|
||||
} else {
|
||||
rval = output->execute_mode(output, true);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("detaching output failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* blank any unused crtcs */
|
||||
list_for_each_entry(crtc, &display->crtcs, item) {
|
||||
if (!(crtc_mask & (1 << crtc->index)))
|
||||
crtc->blank(crtc, true);
|
||||
}
|
||||
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
rval = crtc->set_mode(crtc, hw_mode);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("crtc mode set failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* find native mode. */
|
||||
list_for_each_entry(output, &display->outputs, item) {
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
*crtc->native_mode = *output->native_mode;
|
||||
list_for_each_entry(connector, &display->connectors, item) {
|
||||
if (connector->output != output)
|
||||
continue;
|
||||
|
||||
crtc->requested_scaling_mode = connector->requested_scaling_mode;
|
||||
crtc->use_dithering = connector->use_dithering;
|
||||
break;
|
||||
}
|
||||
|
||||
if (crtc->requested_scaling_mode == SCALE_NON_GPU)
|
||||
crtc->use_native_mode = false;
|
||||
else
|
||||
crtc->use_native_mode = true;
|
||||
|
||||
break; /* no use in finding more than one mode */
|
||||
}
|
||||
|
||||
rval = crtc->execute_mode(crtc);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("crtc execute mode failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(output, &display->outputs, item) {
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
rval = output->execute_mode(output, false);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("output execute mode failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rval = crtc->set_scale(crtc);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("crtc set scale failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* next line changes crtc, so putting it here is important */
|
||||
display->last_crtc = crtc->index;
|
||||
}
|
||||
|
||||
/* always reset dpms, regardless if any other modesetting is done. */
|
||||
if (!blank) {
|
||||
/* this is executed immediately */
|
||||
list_for_each_entry(output, &display->outputs, item) {
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
rval = output->set_power_mode(output, DRM_MODE_DPMS_ON);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("output set power mode failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* update dpms state to DPMSModeOn */
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
drm_connector = set->connectors[i];
|
||||
if (!drm_connector) {
|
||||
DRM_ERROR("No connector\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rval = drm_connector_property_set_value(drm_connector,
|
||||
dev->mode_config.dpms_property,
|
||||
DRM_MODE_DPMS_ON);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("failed to update dpms state\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
display->update(display);
|
||||
|
||||
/* Update the current mode, now that all has gone well. */
|
||||
if (modeset) {
|
||||
set->crtc->mode = *(set->mode);
|
||||
set->crtc->x = set->x;
|
||||
set->crtc->y = set->y;
|
||||
}
|
||||
|
||||
kfree(hw_mode);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
kfree(hw_mode);
|
||||
|
||||
if (rval != 0)
|
||||
return rval;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs nv50_kms_crtc_funcs = {
|
||||
.save = NULL,
|
||||
.restore = NULL,
|
||||
.cursor_set = nv50_kms_crtc_cursor_set,
|
||||
.cursor_move = nv50_kms_crtc_cursor_move,
|
||||
.gamma_set = nv50_kms_crtc_gamma_set,
|
||||
.set_config = nv50_kms_crtc_set_config,
|
||||
.destroy = nv50_crtc_destroy,
|
||||
};
|
||||
|
||||
int nv50_crtc_create(struct drm_device *dev, int index)
|
||||
{
|
||||
struct nv50_crtc *crtc = NULL;
|
||||
struct nv50_display *display = NULL;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
||||
display = nv50_get_display(dev);
|
||||
if (!display)
|
||||
return -EINVAL;
|
||||
|
||||
crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
|
||||
if (!crtc)
|
||||
return -ENOMEM;
|
||||
|
||||
crtc->dev = dev;
|
||||
crtc->mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
|
||||
if (!crtc->mode) {
|
||||
kfree(crtc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
display = nv50_get_display(dev);
|
||||
if (!display) {
|
||||
rval = -EINVAL;
|
||||
goto out;
|
||||
crtc->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
|
||||
if (!crtc->native_mode) {
|
||||
kfree(crtc->mode);
|
||||
kfree(crtc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
list_add_tail(&crtc->item, &display->crtcs);
|
||||
|
||||
crtc->index = index;
|
||||
|
||||
crtc->mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
|
||||
crtc->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
|
||||
|
||||
crtc->requested_scaling_mode = SCALE_INVALID;
|
||||
crtc->scaling_mode = SCALE_INVALID;
|
||||
|
||||
if (!crtc->mode || !crtc->native_mode) {
|
||||
rval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nv50_fb_create(crtc);
|
||||
nv50_lut_create(crtc);
|
||||
nv50_cursor_create(crtc);
|
||||
|
||||
/* set function pointers */
|
||||
crtc->validate_mode = nv50_crtc_validate_mode;
|
||||
crtc->set_mode = nv50_crtc_set_mode;
|
||||
|
|
@ -524,17 +1010,12 @@ int nv50_crtc_create(struct drm_device *dev, int index)
|
|||
crtc->set_scale = nv50_crtc_set_scale;
|
||||
crtc->set_clock = nv50_crtc_set_clock;
|
||||
crtc->set_clock_mode = nv50_crtc_set_clock_mode;
|
||||
crtc->destroy = nv50_crtc_destroy;
|
||||
|
||||
drm_crtc_init(dev, &crtc->base, &nv50_kms_crtc_funcs);
|
||||
drm_mode_crtc_set_gamma_size(&crtc->base, 256);
|
||||
|
||||
nv50_fb_create(crtc);
|
||||
nv50_lut_create(crtc);
|
||||
nv50_cursor_create(crtc);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (crtc->mode)
|
||||
kfree(crtc->mode);
|
||||
if (crtc->native_mode)
|
||||
kfree(crtc->native_mode);
|
||||
if (dev_priv->free_crtc)
|
||||
dev_priv->free_crtc(crtc);
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,11 +34,12 @@ struct nv50_lut;
|
|||
struct nv50_fb;
|
||||
|
||||
struct nv50_crtc {
|
||||
struct drm_crtc base;
|
||||
struct list_head item;
|
||||
|
||||
struct drm_device *dev;
|
||||
/* struct drm_device *dev; IN BASE */
|
||||
int index;
|
||||
bool enabled;
|
||||
/* bool enabled; IN BASE */
|
||||
bool blanked;
|
||||
|
||||
struct nouveau_hw_mode *mode;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
static int nv50_cursor_enable(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ static int nv50_cursor_enable(struct nv50_crtc *crtc)
|
|||
|
||||
static int nv50_cursor_disable(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ static int nv50_cursor_disable(struct nv50_crtc *crtc)
|
|||
/* Calling update or changing the stored cursor state is left to the higher level ioctl's. */
|
||||
static int nv50_cursor_show(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
uint32_t offset = crtc->index * 0x400;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
|
@ -83,7 +83,7 @@ static int nv50_cursor_show(struct nv50_crtc *crtc)
|
|||
|
||||
static int nv50_cursor_hide(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
uint32_t offset = crtc->index * 0x400;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
|
@ -96,7 +96,7 @@ static int nv50_cursor_hide(struct nv50_crtc *crtc)
|
|||
|
||||
static int nv50_cursor_set_pos(struct nv50_crtc *crtc, int x, int y)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
|
||||
NV_WRITE(NV50_HW_CURSOR_POS(crtc->index), ((y & 0xFFFF) << 16) | (x & 0xFFFF));
|
||||
/* Needed to make the cursor move. */
|
||||
|
|
@ -107,7 +107,7 @@ static int nv50_cursor_set_pos(struct nv50_crtc *crtc, int x, int y)
|
|||
|
||||
static int nv50_cursor_set_offset(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
|
||||
struct drm_nouveau_private *dev_priv = crtc->base.dev->dev_private;
|
||||
struct nouveau_gem_object *ngem = nouveau_gem_object(crtc->cursor->gem);
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
|
@ -128,9 +128,9 @@ nv50_cursor_set_bo(struct nv50_crtc *crtc, struct drm_gem_object *gem)
|
|||
struct nv50_cursor *cursor = crtc->cursor;
|
||||
|
||||
if (cursor->gem) {
|
||||
mutex_lock(&crtc->dev->struct_mutex);
|
||||
mutex_lock(&crtc->base.dev->struct_mutex);
|
||||
drm_gem_object_unreference(cursor->gem);
|
||||
mutex_unlock(&crtc->dev->struct_mutex);
|
||||
mutex_unlock(&crtc->base.dev->struct_mutex);
|
||||
|
||||
cursor->gem = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ static int nv50_display_disable(struct nv50_display *display)
|
|||
|
||||
/* Almost like ack'ing a vblank interrupt, maybe in the spirit of cleaning up? */
|
||||
list_for_each_entry(crtc, &display->crtcs, item) {
|
||||
if (crtc->enabled) {
|
||||
if (crtc->base.enabled) {
|
||||
uint32_t mask;
|
||||
|
||||
if (crtc->index == 1)
|
||||
|
|
|
|||
|
|
@ -39,32 +39,6 @@ struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev)
|
|||
return dev_priv->kms_priv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocation functions.
|
||||
*/
|
||||
|
||||
static void *nv50_kms_alloc_crtc(struct drm_device *dev)
|
||||
{
|
||||
struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev);
|
||||
struct nv50_kms_crtc *crtc = kzalloc(sizeof(struct nv50_kms_crtc), GFP_KERNEL);
|
||||
|
||||
if (!crtc)
|
||||
return NULL;
|
||||
|
||||
list_add_tail(&crtc->item, &kms_priv->crtcs);
|
||||
|
||||
return &(crtc->priv);
|
||||
}
|
||||
|
||||
static void nv50_kms_free_crtc(void *crtc)
|
||||
{
|
||||
struct nv50_kms_crtc *kms_crtc = from_nv50_crtc(crtc);
|
||||
|
||||
list_del(&kms_crtc->item);
|
||||
|
||||
kfree(kms_crtc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mode conversion functions.
|
||||
*/
|
||||
|
|
@ -102,14 +76,13 @@ struct nouveau_hw_mode *nv50_kms_to_hw_mode(struct drm_display_mode *mode)
|
|||
* State mirroring functions.
|
||||
*/
|
||||
|
||||
static void nv50_kms_mirror_routing(struct drm_device *dev)
|
||||
void nv50_kms_mirror_routing(struct drm_device *dev)
|
||||
{
|
||||
struct nv50_display *display = nv50_get_display(dev);
|
||||
struct nv50_crtc *crtc = NULL;
|
||||
struct nv50_output *output = NULL;
|
||||
struct nv50_connector *connector = NULL;
|
||||
struct drm_connector *drm_connector = NULL;
|
||||
struct drm_crtc *drm_crtc = NULL;
|
||||
|
||||
/* Wipe all previous connections. */
|
||||
list_for_each_entry(connector, &display->connectors, item) {
|
||||
|
|
@ -136,13 +109,6 @@ static void nv50_kms_mirror_routing(struct drm_device *dev)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* mirror crtc active state */
|
||||
list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
|
||||
crtc = to_nv50_crtc(drm_crtc);
|
||||
|
||||
crtc->enabled = drm_crtc->enabled;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -215,538 +181,6 @@ static const struct drm_mode_config_funcs nv50_kms_mode_funcs = {
|
|||
.fb_changed = nv50_kms_fb_changed,
|
||||
};
|
||||
|
||||
/*
|
||||
* CRTC functions.
|
||||
*/
|
||||
|
||||
static int nv50_kms_crtc_cursor_set(struct drm_crtc *drm_crtc,
|
||||
struct drm_file *file_priv,
|
||||
uint32_t buffer_handle,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
struct drm_device *dev = drm_crtc->dev;
|
||||
struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
|
||||
struct nv50_display *display = nv50_get_display(crtc->dev);
|
||||
struct drm_gem_object *gem = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (width != 64 || height != 64)
|
||||
return -EINVAL;
|
||||
|
||||
if (buffer_handle) {
|
||||
gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
|
||||
if (!gem)
|
||||
return -EINVAL;
|
||||
|
||||
ret = nouveau_gem_pin(gem, NOUVEAU_GEM_DOMAIN_VRAM);
|
||||
if (ret) {
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
drm_gem_object_unreference(gem);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
crtc->cursor->set_bo(crtc, gem);
|
||||
crtc->cursor->set_offset(crtc);
|
||||
ret = crtc->cursor->show(crtc);
|
||||
} else {
|
||||
crtc->cursor->set_bo(crtc, NULL);
|
||||
crtc->cursor->hide(crtc);
|
||||
}
|
||||
|
||||
display->update(display);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nv50_kms_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y)
|
||||
{
|
||||
struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
|
||||
|
||||
return crtc->cursor->set_pos(crtc, x, y);
|
||||
}
|
||||
|
||||
void nv50_kms_crtc_gamma_set(struct drm_crtc *drm_crtc, u16 *r, u16 *g, u16 *b,
|
||||
uint32_t size)
|
||||
{
|
||||
struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
|
||||
|
||||
if (size != 256)
|
||||
return;
|
||||
|
||||
crtc->lut->set(crtc, (uint16_t *)r, (uint16_t *)g, (uint16_t *)b);
|
||||
}
|
||||
|
||||
int nv50_kms_crtc_set_config(struct drm_mode_set *set)
|
||||
{
|
||||
int rval = 0, i;
|
||||
uint32_t crtc_mask = 0;
|
||||
struct drm_device *dev = NULL;
|
||||
struct drm_nouveau_private *dev_priv = NULL;
|
||||
struct nv50_display *display = NULL;
|
||||
struct drm_connector *drm_connector = NULL;
|
||||
struct drm_encoder *drm_encoder = NULL;
|
||||
struct drm_crtc *drm_crtc = NULL;
|
||||
|
||||
struct nv50_crtc *crtc = NULL;
|
||||
struct nv50_output *output = NULL;
|
||||
struct nv50_connector *connector = NULL;
|
||||
struct nouveau_hw_mode *hw_mode = NULL;
|
||||
struct nv50_fb_info fb_info;
|
||||
|
||||
bool blank = false;
|
||||
bool switch_fb = false;
|
||||
bool modeset = false;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
||||
/*
|
||||
* Supported operations:
|
||||
* - Switch mode.
|
||||
* - Switch framebuffer.
|
||||
* - Blank screen.
|
||||
*/
|
||||
|
||||
/* Sanity checking */
|
||||
if (!set) {
|
||||
DRM_ERROR("Sanity check failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!set->crtc) {
|
||||
DRM_ERROR("Sanity check failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (set->mode) {
|
||||
if (set->fb) {
|
||||
if (!drm_mode_equal(set->mode, &set->crtc->mode))
|
||||
modeset = true;
|
||||
|
||||
if (set->fb != set->crtc->fb)
|
||||
switch_fb = true;
|
||||
|
||||
if (set->x != set->crtc->x || set->y != set->crtc->y)
|
||||
switch_fb = true;
|
||||
}
|
||||
} else {
|
||||
blank = true;
|
||||
}
|
||||
|
||||
if (!set->connectors && !blank) {
|
||||
DRM_ERROR("Sanity check failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Basic variable setting */
|
||||
dev = set->crtc->dev;
|
||||
dev_priv = dev->dev_private;
|
||||
display = nv50_get_display(dev);
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
/**
|
||||
* Wiring up the encoders and connectors.
|
||||
*/
|
||||
|
||||
/* for switch_fb we verify if any important changes happened */
|
||||
if (!blank) {
|
||||
/* Mode validation */
|
||||
hw_mode = nv50_kms_to_hw_mode(set->mode);
|
||||
|
||||
rval = crtc->validate_mode(crtc, hw_mode);
|
||||
|
||||
if (rval != MODE_OK) {
|
||||
DRM_ERROR("Mode not ok\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
drm_connector = set->connectors[i];
|
||||
if (!drm_connector) {
|
||||
DRM_ERROR("No connector\n");
|
||||
goto out;
|
||||
}
|
||||
connector = to_nv50_connector(drm_connector);
|
||||
|
||||
/* This is to ensure it knows the connector subtype. */
|
||||
drm_connector->funcs->fill_modes(drm_connector, 0, 0);
|
||||
|
||||
output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
|
||||
if (!output) {
|
||||
DRM_ERROR("No output\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rval = output->validate_mode(output, hw_mode);
|
||||
if (rval != MODE_OK) {
|
||||
DRM_ERROR("Mode not ok\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* verify if any "sneaky" changes happened */
|
||||
if (output != connector->output)
|
||||
modeset = true;
|
||||
|
||||
if (output->crtc != crtc)
|
||||
modeset = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we verified if anything changed, fail if nothing has. */
|
||||
if (!modeset && !switch_fb && !blank)
|
||||
DRM_INFO("A seemingly empty modeset encountered, this could be a bug.\n");
|
||||
|
||||
/* Validation done, move on to cleaning of existing structures. */
|
||||
if (modeset) {
|
||||
/* find encoders that use this crtc. */
|
||||
list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) {
|
||||
if (drm_encoder->crtc == set->crtc) {
|
||||
/* find the connector that goes with it */
|
||||
list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
|
||||
if (drm_connector->encoder == drm_encoder) {
|
||||
drm_connector->encoder = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
drm_encoder->crtc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* now find if our desired encoders or connectors are in use already. */
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
drm_connector = set->connectors[i];
|
||||
if (!drm_connector) {
|
||||
DRM_ERROR("No connector\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!drm_connector->encoder)
|
||||
continue;
|
||||
|
||||
drm_encoder = drm_connector->encoder;
|
||||
drm_connector->encoder = NULL;
|
||||
|
||||
if (!drm_encoder->crtc)
|
||||
continue;
|
||||
|
||||
drm_crtc = drm_encoder->crtc;
|
||||
drm_encoder->crtc = NULL;
|
||||
|
||||
drm_crtc->enabled = false;
|
||||
}
|
||||
|
||||
/* Time to wire up the public encoder, the private one will be handled later. */
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
drm_connector = set->connectors[i];
|
||||
if (!drm_connector) {
|
||||
DRM_ERROR("No connector\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
|
||||
if (!output) {
|
||||
DRM_ERROR("No output\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
output->base.crtc = set->crtc;
|
||||
set->crtc->enabled = true;
|
||||
drm_connector->encoder = &output->base;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable crtc.
|
||||
*/
|
||||
|
||||
if (blank) {
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
set->crtc->enabled = false;
|
||||
|
||||
/* disconnect encoders and connectors */
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
drm_connector = set->connectors[i];
|
||||
|
||||
if (!drm_connector->encoder)
|
||||
continue;
|
||||
|
||||
drm_connector->encoder->crtc = NULL;
|
||||
drm_connector->encoder = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All state should now be updated, now onto the real work.
|
||||
*/
|
||||
|
||||
/* mirror everything to the private structs */
|
||||
nv50_kms_mirror_routing(dev);
|
||||
|
||||
/**
|
||||
* Bind framebuffer.
|
||||
*/
|
||||
|
||||
if (switch_fb) {
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
/* set framebuffer */
|
||||
set->crtc->fb = set->fb;
|
||||
|
||||
/* set private framebuffer */
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
fb_info.gem = nv50_framebuffer(set->fb)->gem;
|
||||
fb_info.width = set->fb->width;
|
||||
fb_info.height = set->fb->height;
|
||||
fb_info.depth = set->fb->depth;
|
||||
fb_info.bpp = set->fb->bits_per_pixel;
|
||||
fb_info.pitch = set->fb->pitch;
|
||||
fb_info.x = set->x;
|
||||
fb_info.y = set->y;
|
||||
|
||||
rval = crtc->fb->bind(crtc, &fb_info);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("fb_bind failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* this is !cursor_show */
|
||||
if (!crtc->cursor->enabled) {
|
||||
rval = crtc->cursor->enable(crtc);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("cursor_enable failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blanking.
|
||||
*/
|
||||
|
||||
if (blank) {
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
rval = crtc->blank(crtc, true);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("blanking failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* detach any outputs that are currently unused */
|
||||
list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) {
|
||||
if (!drm_encoder->crtc) {
|
||||
output = to_nv50_output(drm_encoder);
|
||||
|
||||
rval = output->execute_mode(output, true);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("detaching output failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change framebuffer, without changing mode.
|
||||
*/
|
||||
|
||||
if (switch_fb && !modeset && !blank) {
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
rval = crtc->set_fb(crtc);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("set_fb failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* this also sets the fb offset */
|
||||
rval = crtc->blank(crtc, false);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("unblanking failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal modesetting.
|
||||
*/
|
||||
|
||||
if (modeset) {
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
/* disconnect unused outputs */
|
||||
list_for_each_entry(output, &display->outputs, item) {
|
||||
if (output->crtc) {
|
||||
crtc_mask |= 1 << output->crtc->index;
|
||||
} else {
|
||||
rval = output->execute_mode(output, true);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("detaching output failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* blank any unused crtcs */
|
||||
list_for_each_entry(crtc, &display->crtcs, item) {
|
||||
if (!(crtc_mask & (1 << crtc->index)))
|
||||
crtc->blank(crtc, true);
|
||||
}
|
||||
|
||||
crtc = to_nv50_crtc(set->crtc);
|
||||
|
||||
rval = crtc->set_mode(crtc, hw_mode);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("crtc mode set failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* find native mode. */
|
||||
list_for_each_entry(output, &display->outputs, item) {
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
*crtc->native_mode = *output->native_mode;
|
||||
list_for_each_entry(connector, &display->connectors, item) {
|
||||
if (connector->output != output)
|
||||
continue;
|
||||
|
||||
crtc->requested_scaling_mode = connector->requested_scaling_mode;
|
||||
crtc->use_dithering = connector->use_dithering;
|
||||
break;
|
||||
}
|
||||
|
||||
if (crtc->requested_scaling_mode == SCALE_NON_GPU)
|
||||
crtc->use_native_mode = false;
|
||||
else
|
||||
crtc->use_native_mode = true;
|
||||
|
||||
break; /* no use in finding more than one mode */
|
||||
}
|
||||
|
||||
rval = crtc->execute_mode(crtc);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("crtc execute mode failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(output, &display->outputs, item) {
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
rval = output->execute_mode(output, false);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("output execute mode failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rval = crtc->set_scale(crtc);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("crtc set scale failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* next line changes crtc, so putting it here is important */
|
||||
display->last_crtc = crtc->index;
|
||||
}
|
||||
|
||||
/* always reset dpms, regardless if any other modesetting is done. */
|
||||
if (!blank) {
|
||||
/* this is executed immediately */
|
||||
list_for_each_entry(output, &display->outputs, item) {
|
||||
if (output->crtc != crtc)
|
||||
continue;
|
||||
|
||||
rval = output->set_power_mode(output, DRM_MODE_DPMS_ON);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("output set power mode failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* update dpms state to DPMSModeOn */
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
drm_connector = set->connectors[i];
|
||||
if (!drm_connector) {
|
||||
DRM_ERROR("No connector\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rval = drm_connector_property_set_value(drm_connector,
|
||||
dev->mode_config.dpms_property,
|
||||
DRM_MODE_DPMS_ON);
|
||||
if (rval != 0) {
|
||||
DRM_ERROR("failed to update dpms state\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
display->update(display);
|
||||
|
||||
/* Update the current mode, now that all has gone well. */
|
||||
if (modeset) {
|
||||
set->crtc->mode = *(set->mode);
|
||||
set->crtc->x = set->x;
|
||||
set->crtc->y = set->y;
|
||||
}
|
||||
|
||||
kfree(hw_mode);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
kfree(hw_mode);
|
||||
|
||||
if (rval != 0)
|
||||
return rval;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void nv50_kms_crtc_destroy(struct drm_crtc *drm_crtc)
|
||||
{
|
||||
struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
|
||||
|
||||
drm_crtc_cleanup(drm_crtc);
|
||||
|
||||
/* this will even destroy the public structure. */
|
||||
crtc->destroy(crtc);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs nv50_kms_crtc_funcs = {
|
||||
.save = NULL,
|
||||
.restore = NULL,
|
||||
.cursor_set = nv50_kms_crtc_cursor_set,
|
||||
.cursor_move = nv50_kms_crtc_cursor_move,
|
||||
.gamma_set = nv50_kms_crtc_gamma_set,
|
||||
.set_config = nv50_kms_crtc_set_config,
|
||||
.destroy = nv50_kms_crtc_destroy,
|
||||
};
|
||||
|
||||
static int nv50_kms_crtcs_init(struct drm_device *dev)
|
||||
{
|
||||
struct nv50_display *display = nv50_get_display(dev);
|
||||
struct nv50_crtc *crtc = NULL;
|
||||
|
||||
/*
|
||||
* This may look a bit confusing, but:
|
||||
* The internal structure is already allocated and so is the public one.
|
||||
* Just a matter of getting to the memory and register it.
|
||||
*/
|
||||
list_for_each_entry(crtc, &display->crtcs, item) {
|
||||
struct drm_crtc *drm_crtc = to_nv50_kms_crtc(crtc);
|
||||
|
||||
drm_crtc_init(dev, drm_crtc, &nv50_kms_crtc_funcs);
|
||||
|
||||
/* init lut storage */
|
||||
drm_mode_crtc_set_gamma_size(drm_crtc, 256);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main functions
|
||||
*/
|
||||
|
|
@ -763,11 +197,6 @@ int nv50_kms_init(struct drm_device *dev)
|
|||
|
||||
dev_priv->kms_priv = kms_priv;
|
||||
|
||||
/* function pointers */
|
||||
/* an allocation interface that deals with the outside world, without polluting the core. */
|
||||
dev_priv->alloc_crtc = nv50_kms_alloc_crtc;
|
||||
dev_priv->free_crtc = nv50_kms_free_crtc;
|
||||
|
||||
/* bios is needed for tables. */
|
||||
rval = nouveau_parse_bios(dev);
|
||||
if (rval != 0)
|
||||
|
|
@ -809,11 +238,6 @@ int nv50_kms_init(struct drm_device *dev)
|
|||
if (rval != 0)
|
||||
goto out;
|
||||
|
||||
/* init external layer */
|
||||
rval = nv50_kms_crtcs_init(dev);
|
||||
if (rval != 0)
|
||||
goto out;
|
||||
|
||||
/* init now, this'll kill the textmode */
|
||||
rval = display->init(display);
|
||||
if (rval != 0)
|
||||
|
|
|
|||
|
|
@ -43,23 +43,11 @@
|
|||
|
||||
/* Link internal modesetting structure to interface. */
|
||||
|
||||
struct nv50_kms_crtc {
|
||||
struct list_head item;
|
||||
|
||||
struct nv50_crtc priv;
|
||||
struct drm_crtc pub;
|
||||
};
|
||||
|
||||
struct nv50_kms_priv {
|
||||
struct list_head crtcs;
|
||||
};
|
||||
|
||||
/* Get private functions. */
|
||||
#define from_nv50_kms_crtc(x) container_of(x, struct nv50_kms_crtc, pub)
|
||||
#define from_nv50_crtc(x) container_of(x, struct nv50_kms_crtc, priv)
|
||||
|
||||
#define to_nv50_crtc(x) (&(from_nv50_kms_crtc(x)->priv))
|
||||
#define to_nv50_kms_crtc(x) (&(from_nv50_crtc(x)->pub))
|
||||
#define to_nv50_crtc(x) container_of((x), struct nv50_crtc, base)
|
||||
#define to_nv50_output(x) container_of((x), struct nv50_output, base)
|
||||
#define to_nv50_connector(x) container_of((x), struct nv50_connector, base)
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
static int nv50_lut_alloc(struct nv50_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_device *dev = crtc->base.dev;
|
||||
int ret;
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
|
|
|||
|
|
@ -341,11 +341,6 @@ struct drm_nouveau_private {
|
|||
void *display_priv; /* internal modesetting */
|
||||
void *kms_priv; /* related to public interface */
|
||||
|
||||
/* Hook these up to the "public interface" to accomodate a certain allocation style. */
|
||||
/* This is to avoid polluting the internal interface. */
|
||||
void *(*alloc_crtc) (struct drm_device *dev);
|
||||
void (*free_crtc) (void *crtc);
|
||||
|
||||
struct bios bios;
|
||||
|
||||
struct {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue