diff options
author | Dave Airlie <airlied@redhat.com> | 2010-03-30 05:34:14 +0000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-04-07 10:24:56 +1000 |
commit | 8be48d924c307e72e3797ab5bde81b07a1ccc52d (patch) | |
tree | 7d2fec8d40b0a6e9f7bd8a5aef5bb100783ecd44 /drivers/gpu/drm/drm_fb_helper.c | |
parent | 386516744ba45d50f42c6999151cc210cb4f96e4 (diff) | |
download | linux-stable-8be48d924c307e72e3797ab5bde81b07a1ccc52d.tar.gz linux-stable-8be48d924c307e72e3797ab5bde81b07a1ccc52d.tar.bz2 linux-stable-8be48d924c307e72e3797ab5bde81b07a1ccc52d.zip |
drm/kms/fb: move to using fb helper crtc grouping instead of core crtc list
This move to using the list of crtcs in the fb helper and cleans up the
whole picking code, now we store the crtc/connectors we want directly
into the modeset and we use the modeset directly to set the mode.
Fixes from James Simmons and Ben Skeggs.
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 329 |
1 files changed, 139 insertions, 190 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 055b5be78877..2515563063ce 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -292,6 +292,7 @@ static void drm_fb_helper_on(struct fb_info *info) struct drm_fb_helper *fb_helper = info->par; struct drm_device *dev = fb_helper->dev; struct drm_crtc *crtc; + struct drm_crtc_helper_funcs *crtc_funcs; struct drm_encoder *encoder; int i; @@ -299,33 +300,28 @@ static void drm_fb_helper_on(struct fb_info *info) * For each CRTC in this fb, turn the crtc on then, * find all associated encoders and turn them on. */ + mutex_lock(&dev->mode_config.mutex); for (i = 0; i < fb_helper->crtc_count; i++) { - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct drm_crtc_helper_funcs *crtc_funcs = - crtc->helper_private; + crtc = fb_helper->crtc_info[i].mode_set.crtc; + crtc_funcs = crtc->helper_private; - /* Only mess with CRTCs in this fb */ - if (crtc->base.id != fb_helper->crtc_info[i].crtc_id || - !crtc->enabled) - continue; + if (!crtc->enabled) + continue; + + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); - mutex_lock(&dev->mode_config.mutex); - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); - mutex_unlock(&dev->mode_config.mutex); - /* Found a CRTC on this fb, now find encoders */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->crtc == crtc) { - struct drm_encoder_helper_funcs *encoder_funcs; + /* Found a CRTC on this fb, now find encoders */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + struct drm_encoder_helper_funcs *encoder_funcs; - encoder_funcs = encoder->helper_private; - mutex_lock(&dev->mode_config.mutex); - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); - mutex_unlock(&dev->mode_config.mutex); - } + encoder_funcs = encoder->helper_private; + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); } } } + mutex_unlock(&dev->mode_config.mutex); } static void drm_fb_helper_off(struct fb_info *info, int dpms_mode) @@ -333,6 +329,7 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode) struct drm_fb_helper *fb_helper = info->par; struct drm_device *dev = fb_helper->dev; struct drm_crtc *crtc; + struct drm_crtc_helper_funcs *crtc_funcs; struct drm_encoder *encoder; int i; @@ -340,32 +337,26 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode) * For each CRTC in this fb, find all associated encoders * and turn them off, then turn off the CRTC. */ + mutex_lock(&dev->mode_config.mutex); for (i = 0; i < fb_helper->crtc_count; i++) { - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct drm_crtc_helper_funcs *crtc_funcs = - crtc->helper_private; + crtc = fb_helper->crtc_info[i].mode_set.crtc; + crtc_funcs = crtc->helper_private; - /* Only mess with CRTCs in this fb */ - if (crtc->base.id != fb_helper->crtc_info[i].crtc_id || - !crtc->enabled) - continue; + if (!crtc->enabled) + continue; - /* Found a CRTC on this fb, now find encoders */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->crtc == crtc) { - struct drm_encoder_helper_funcs *encoder_funcs; + /* Found a CRTC on this fb, now find encoders */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + struct drm_encoder_helper_funcs *encoder_funcs; - encoder_funcs = encoder->helper_private; - mutex_lock(&dev->mode_config.mutex); - encoder_funcs->dpms(encoder, dpms_mode); - mutex_unlock(&dev->mode_config.mutex); - } + encoder_funcs = encoder->helper_private; + encoder_funcs->dpms(encoder, dpms_mode); } - mutex_lock(&dev->mode_config.mutex); - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); - mutex_unlock(&dev->mode_config.mutex); } + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); } + mutex_unlock(&dev->mode_config.mutex); } int drm_fb_helper_blank(int blank, struct fb_info *info) @@ -405,17 +396,19 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) kfree(helper->crtc_info); } -int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count) +int drm_fb_helper_init_crtc_count(struct drm_device *dev, + struct drm_fb_helper *helper, + int crtc_count, int max_conn_count) { - struct drm_device *dev = helper->dev; struct drm_crtc *crtc; int ret = 0; int i; + INIT_LIST_HEAD(&helper->kernel_fb_list); + helper->dev = dev; helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); if (!helper->crtc_info) return -ENOMEM; - helper->crtc_count = crtc_count; for (i = 0; i < crtc_count; i++) { @@ -507,20 +500,15 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; + struct drm_crtc_helper_funcs *crtc_funcs; u16 *red, *green, *blue, *transp; struct drm_crtc *crtc; int i, rc = 0; int start; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - for (i = 0; i < fb_helper->crtc_count; i++) { - if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) - break; - } - if (i == fb_helper->crtc_count) - continue; + for (i = 0; i < fb_helper->crtc_count; i++) { + crtc = fb_helper->crtc_info[i].mode_set.crtc; + crtc_funcs = crtc->helper_private; red = cmap->red; green = cmap->green; @@ -556,22 +544,17 @@ int drm_fb_helper_setcolreg(unsigned regno, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; struct drm_crtc *crtc; + struct drm_crtc_helper_funcs *crtc_funcs; int i; int ret; if (regno > 255) return 1; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - for (i = 0; i < fb_helper->crtc_count; i++) { - if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) - break; - } - if (i == fb_helper->crtc_count) - continue; + for (i = 0; i < fb_helper->crtc_count; i++) { + crtc = fb_helper->crtc_info[i].mode_set.crtc; + crtc_funcs = crtc->helper_private; ret = setcolreg(crtc, red, green, blue, regno, info); if (ret) @@ -686,23 +669,20 @@ int drm_fb_helper_set_par(struct fb_info *info) return -EINVAL; } - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - - for (i = 0; i < fb_helper->crtc_count; i++) { - if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) - break; - } - if (i == fb_helper->crtc_count) - continue; + mutex_lock(&dev->mode_config.mutex); + for (i = 0; i < fb_helper->crtc_count; i++) { + crtc = fb_helper->crtc_info[i].mode_set.crtc; - if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) { - mutex_lock(&dev->mode_config.mutex); + if (crtc->fb != fb_helper->crtc_info[i].mode_set.fb) { ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); - mutex_unlock(&dev->mode_config.mutex); - if (ret) + + if (ret) { + mutex_unlock(&dev->mode_config.mutex); return ret; + } } } + mutex_unlock(&dev->mode_config.mutex); return 0; } EXPORT_SYMBOL(drm_fb_helper_set_par); @@ -717,14 +697,9 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, int ret = 0; int i; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - for (i = 0; i < fb_helper->crtc_count; i++) { - if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) - break; - } - - if (i == fb_helper->crtc_count) - continue; + mutex_lock(&dev->mode_config.mutex); + for (i = 0; i < fb_helper->crtc_count; i++) { + crtc = fb_helper->crtc_info[i].mode_set.crtc; modeset = &fb_helper->crtc_info[i].mode_set; @@ -732,34 +707,29 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, modeset->y = var->yoffset; if (modeset->num_connectors) { - mutex_lock(&dev->mode_config.mutex); ret = crtc->funcs->set_config(modeset); - mutex_unlock(&dev->mode_config.mutex); if (!ret) { info->var.xoffset = var->xoffset; info->var.yoffset = var->yoffset; } } } + mutex_unlock(&dev->mode_config.mutex); return ret; } EXPORT_SYMBOL(drm_fb_helper_pan_display); -int drm_fb_helper_single_fb_probe(struct drm_device *dev, - int preferred_bpp, - int (*fb_find_or_create)(struct drm_device *dev, - struct drm_fb_helper_surface_size *sizes, - struct drm_fb_helper **fb_ptr)) +int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, + int preferred_bpp) { - struct drm_crtc *crtc; + struct drm_device *dev = fb_helper->dev; struct drm_connector *connector; int new_fb = 0; int crtc_count = 0; - int ret, i, conn_count = 0; + int ret, i; struct fb_info *info; - struct drm_mode_set *modeset = NULL; - struct drm_fb_helper *fb_helper; struct drm_fb_helper_surface_size sizes; + int gamma_size = 0; memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); sizes.surface_depth = 24; @@ -775,7 +745,6 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, /* first up get a count of crtcs now in use and new min/maxes width/heights */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; - struct drm_fb_helper_cmdline_mode *cmdline_mode; if (!fb_help_conn) @@ -807,21 +776,22 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, } } - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (drm_helper_crtc_in_use(crtc)) { - if (crtc->desired_mode) { - if (crtc->desired_mode->hdisplay < sizes.fb_width) - sizes.fb_width = crtc->desired_mode->hdisplay; - - if (crtc->desired_mode->vdisplay < sizes.fb_height) - sizes.fb_height = crtc->desired_mode->vdisplay; - - if (crtc->desired_mode->hdisplay > sizes.surface_width) - sizes.surface_width = crtc->desired_mode->hdisplay; - - if (crtc->desired_mode->vdisplay > sizes.surface_height) - sizes.surface_height = crtc->desired_mode->vdisplay; - } + crtc_count = 0; + for (i = 0; i < fb_helper->crtc_count; i++) { + struct drm_display_mode *desired_mode; + desired_mode = fb_helper->crtc_info[i].desired_mode; + + if (desired_mode) { + if (gamma_size == 0) + gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; + if (desired_mode->hdisplay < sizes.fb_width) + sizes.fb_width = desired_mode->hdisplay; + if (desired_mode->vdisplay < sizes.fb_height) + sizes.fb_height = desired_mode->vdisplay; + if (desired_mode->hdisplay > sizes.surface_width) + sizes.surface_width = desired_mode->hdisplay; + if (desired_mode->vdisplay > sizes.surface_height) + sizes.surface_height = desired_mode->vdisplay; crtc_count++; } } @@ -833,48 +803,20 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, } /* push down into drivers */ - new_fb = (*fb_find_or_create)(dev, &sizes, - &fb_helper); + new_fb = (*fb_helper->fb_probe)(fb_helper, &sizes); if (new_fb < 0) return new_fb; info = fb_helper->fbdev; - crtc_count = 0; - /* okay we need to setup new connector sets in the crtcs */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - modeset = &fb_helper->crtc_info[crtc_count].mode_set; - modeset->fb = fb_helper->fb; - conn_count = 0; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder) - if (connector->encoder->crtc == modeset->crtc) { - modeset->connectors[conn_count] = connector; - conn_count++; - if (conn_count > fb_helper->conn_limit) - BUG(); - } - } - - for (i = conn_count; i < fb_helper->conn_limit; i++) - modeset->connectors[i] = NULL; - - modeset->crtc = crtc; - crtc_count++; - - modeset->num_connectors = conn_count; - if (modeset->crtc->desired_mode) { - if (modeset->mode) - drm_mode_destroy(dev, modeset->mode); - modeset->mode = drm_mode_duplicate(dev, - modeset->crtc->desired_mode); - } + /* set the fb pointer */ + for (i = 0; i < fb_helper->crtc_count; i++) { + fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; } - fb_helper->crtc_count = crtc_count; if (new_fb) { info->var.pixclock = 0; - ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0); + ret = fb_alloc_cmap(&info->cmap, gamma_size, 0); if (ret) return ret; if (register_framebuffer(info) < 0) { @@ -906,15 +848,18 @@ EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); void drm_fb_helper_free(struct drm_fb_helper *helper) { - list_del(&helper->kernel_fb_list); - if (list_empty(&kernel_fb_helper_list)) { - printk(KERN_INFO "unregistered panic notifier\n"); - atomic_notifier_chain_unregister(&panic_notifier_list, - &paniced); - unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); + if (!list_empty(&helper->kernel_fb_list)) { + list_del(&helper->kernel_fb_list); + if (list_empty(&kernel_fb_helper_list)) { + printk(KERN_INFO "unregistered panic notifier\n"); + atomic_notifier_chain_unregister(&panic_notifier_list, + &paniced); + unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); + } } drm_fb_helper_crtc_free(helper); - fb_dealloc_cmap(&helper->fbdev->cmap); + if (helper->fbdev->cmap.len) + fb_dealloc_cmap(&helper->fbdev->cmap); } EXPORT_SYMBOL(drm_fb_helper_free); @@ -1168,20 +1113,21 @@ static bool drm_target_preferred(struct drm_device *dev, return true; } -static int drm_pick_crtcs(struct drm_device *dev, - struct drm_crtc **best_crtcs, +static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_crtc **best_crtcs, struct drm_display_mode **modes, int n, int width, int height) { int c, o; + struct drm_device *dev = fb_helper->dev; struct drm_connector *connector; struct drm_connector_helper_funcs *connector_funcs; struct drm_encoder *encoder; - struct drm_crtc *best_crtc; + struct drm_fb_helper_crtc *best_crtc; int my_score, best_score, score; - struct drm_crtc **crtcs, *crtc; + struct drm_fb_helper_crtc **crtcs, *crtc; - if (n == dev->mode_config.num_connector) + if (n == fb_helper->dev->mode_config.num_connector) return 0; c = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { @@ -1192,12 +1138,12 @@ static int drm_pick_crtcs(struct drm_device *dev, best_crtcs[n] = NULL; best_crtc = NULL; - best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); + best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); if (modes[n] == NULL) return best_score; - crtcs = kmalloc(dev->mode_config.num_connector * - sizeof(struct drm_crtc *), GFP_KERNEL); + crtcs = kzalloc(dev->mode_config.num_connector * + sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); if (!crtcs) return best_score; @@ -1214,15 +1160,12 @@ static int drm_pick_crtcs(struct drm_device *dev, if (!encoder) goto out; - connector->encoder = encoder; - /* select a crtc for this connector and then attempt to configure remaining connectors */ - c = 0; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + for (c = 0; c < fb_helper->crtc_count; c++) { + crtc = &fb_helper->crtc_info[c]; if ((encoder->possible_crtcs & (1 << c)) == 0) { - c++; continue; } @@ -1232,34 +1175,34 @@ static int drm_pick_crtcs(struct drm_device *dev, if (o < n) { /* ignore cloning for now */ - c++; continue; } crtcs[n] = crtc; - memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); - score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, + memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); + score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, width, height); if (score > best_score) { best_crtc = crtc; best_score = score; memcpy(best_crtcs, crtcs, dev->mode_config.num_connector * - sizeof(struct drm_crtc *)); + sizeof(struct drm_fb_helper_crtc *)); } - c++; } out: kfree(crtcs); return best_score; } -static void drm_setup_crtcs(struct drm_device *dev) +static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) { - struct drm_crtc **crtcs; + struct drm_device *dev = fb_helper->dev; + struct drm_fb_helper_crtc **crtcs; struct drm_display_mode **modes; struct drm_encoder *encoder; struct drm_connector *connector; + struct drm_mode_set *modeset; bool *enabled; int width, height; int i, ret; @@ -1275,7 +1218,7 @@ static void drm_setup_crtcs(struct drm_device *dev) } crtcs = kcalloc(dev->mode_config.num_connector, - sizeof(struct drm_crtc *), GFP_KERNEL); + sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); modes = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_display_mode *), GFP_KERNEL); enabled = kcalloc(dev->mode_config.num_connector, @@ -1289,26 +1232,30 @@ static void drm_setup_crtcs(struct drm_device *dev) DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); - drm_pick_crtcs(dev, crtcs, modes, 0, width, height); + drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); + + /* need to set the modesets up here for use later */ + /* fill out the connector<->crtc mappings into the modesets */ + for (i = 0; i < fb_helper->crtc_count; i++) { + modeset = &fb_helper->crtc_info[i].mode_set; + modeset->num_connectors = 0; + } i = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct drm_display_mode *mode = modes[i]; - struct drm_crtc *crtc = crtcs[i]; - - if (connector->encoder == NULL) { - i++; - continue; - } + struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; + modeset = &fb_crtc->mode_set; - if (mode && crtc) { + if (mode && fb_crtc) { DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", - mode->name, crtc->base.id); - crtc->desired_mode = mode; - connector->encoder->crtc = crtc; - } else { - connector->encoder->crtc = NULL; - connector->encoder = NULL; + mode->name, fb_crtc->mode_set.crtc->base.id); + fb_crtc->desired_mode = mode; + if (modeset->mode) + drm_mode_destroy(dev, modeset->mode); + modeset->mode = drm_mode_duplicate(dev, + fb_crtc->desired_mode); + modeset->connectors[modeset->num_connectors++] = connector; } i++; } @@ -1332,14 +1279,15 @@ static void drm_setup_crtcs(struct drm_device *dev) * RETURNS: * Zero if everything went ok, nonzero otherwise. */ -bool drm_helper_initial_config(struct drm_device *dev) +bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) { + struct drm_device *dev = fb_helper->dev; int count = 0; /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); + drm_helper_disable_unused_functions(fb_helper->dev); - drm_fb_helper_parse_command_line(dev); + drm_fb_helper_parse_command_line(fb_helper->dev); count = drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, @@ -1351,20 +1299,21 @@ bool drm_helper_initial_config(struct drm_device *dev) if (count == 0) printk(KERN_INFO "No connectors reported connected with modes\n"); - drm_setup_crtcs(dev); + drm_setup_crtcs(fb_helper); return 0; } -EXPORT_SYMBOL(drm_helper_initial_config); +EXPORT_SYMBOL(drm_fb_helper_initial_config); -bool drm_helper_fb_hotplug_event(struct drm_device *dev) +bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, + u32 max_width, u32 max_height) { DRM_DEBUG_KMS("\n"); - drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, - dev->mode_config.max_height); + drm_helper_probe_connector_modes(fb_helper->dev, max_width, + max_height); - drm_setup_crtcs(dev); + drm_setup_crtcs(fb_helper); return true; } |