From 4abe35204af82a018ca3ce6db4102aa09719698e Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 30 Mar 2010 05:34:18 +0000 Subject: [PATCH] drm/kms/fb: use slow work mechanism for normal hotplug also. a) slow work is always used now for any fbcon hotplug, as its not a fast task and is more suited to being ran under slow work. b) attempt to not do any fbdev changes when X is running as we'll just mess it up. This hooks set_par to hopefully do the changes once X hands control to fbdev. This also adds the nouveau/intel hotplug support. Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fb_helper.c | 207 ++++++++++++++---------- drivers/gpu/drm/i915/i915_irq.c | 1 + drivers/gpu/drm/i915/intel_drv.h | 2 + drivers/gpu/drm/i915/intel_fb.c | 42 ++--- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 45 ++++-- drivers/gpu/drm/nouveau/nouveau_fbcon.h | 2 + drivers/gpu/drm/nouveau/nv50_display.c | 3 + drivers/gpu/drm/radeon/radeon_fb.c | 74 ++++----- include/drm/drm_fb_helper.h | 47 +++--- 9 files changed, 241 insertions(+), 182 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 3312092f2c7e..b889eb0aaf5f 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -41,6 +41,8 @@ MODULE_LICENSE("GPL and additional rights"); static LIST_HEAD(kernel_fb_helper_list); +static struct slow_work_ops output_status_change_ops; + /* simple single crtc case helper function */ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) { @@ -420,54 +422,81 @@ 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_device *dev, - struct drm_fb_helper *helper, - int crtc_count, int max_conn_count) +int drm_fb_helper_init(struct drm_device *dev, + struct drm_fb_helper *fb_helper, + int crtc_count, int max_conn_count, + bool polled) { 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) + fb_helper->dev = dev; + fb_helper->poll_enabled = polled; + + slow_work_register_user(THIS_MODULE); + delayed_slow_work_init(&fb_helper->output_status_change_slow_work, + &output_status_change_ops); + + INIT_LIST_HEAD(&fb_helper->kernel_fb_list); + + fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); + if (!fb_helper->crtc_info) return -ENOMEM; - helper->crtc_count = crtc_count; - helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); - if (!helper->connector_info) { - kfree(helper->crtc_info); + fb_helper->crtc_count = crtc_count; + fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); + if (!fb_helper->connector_info) { + kfree(fb_helper->crtc_info); return -ENOMEM; } - helper->connector_count = 0; + fb_helper->connector_count = 0; for (i = 0; i < crtc_count; i++) { - helper->crtc_info[i].mode_set.connectors = + fb_helper->crtc_info[i].mode_set.connectors = kcalloc(max_conn_count, sizeof(struct drm_connector *), GFP_KERNEL); - if (!helper->crtc_info[i].mode_set.connectors) { + if (!fb_helper->crtc_info[i].mode_set.connectors) { ret = -ENOMEM; goto out_free; } - helper->crtc_info[i].mode_set.num_connectors = 0; + fb_helper->crtc_info[i].mode_set.num_connectors = 0; } i = 0; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - helper->crtc_info[i].crtc_id = crtc->base.id; - helper->crtc_info[i].mode_set.crtc = crtc; + fb_helper->crtc_info[i].crtc_id = crtc->base.id; + fb_helper->crtc_info[i].mode_set.crtc = crtc; i++; } - helper->conn_limit = max_conn_count; + fb_helper->conn_limit = max_conn_count; return 0; out_free: - drm_fb_helper_crtc_free(helper); + drm_fb_helper_crtc_free(fb_helper); return -ENOMEM; } -EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); +EXPORT_SYMBOL(drm_fb_helper_init); + +void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) +{ + if (!list_empty(&fb_helper->kernel_fb_list)) { + list_del(&fb_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(fb_helper); + + delayed_slow_work_cancel(&fb_helper->output_status_change_slow_work); + slow_work_unregister_user(THIS_MODULE); +} +EXPORT_SYMBOL(drm_fb_helper_fini); static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, u16 regno, struct fb_info *info) @@ -710,6 +739,11 @@ int drm_fb_helper_set_par(struct fb_info *info) } } mutex_unlock(&dev->mode_config.mutex); + + if (fb_helper->delayed_hotplug) { + fb_helper->delayed_hotplug = false; + delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0); + } return 0; } EXPORT_SYMBOL(drm_fb_helper_set_par); @@ -751,7 +785,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, { int new_fb = 0; int crtc_count = 0; - int ret, i; + int i; struct fb_info *info; struct drm_fb_helper_surface_size sizes; int gamma_size = 0; @@ -827,7 +861,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, } /* push down into drivers */ - new_fb = (*fb_helper->fb_probe)(fb_helper, &sizes); + new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); if (new_fb < 0) return new_fb; @@ -840,11 +874,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, if (new_fb) { info->var.pixclock = 0; - ret = fb_alloc_cmap(&info->cmap, gamma_size, 0); - if (ret) - return ret; if (register_framebuffer(info) < 0) { - fb_dealloc_cmap(&info->cmap); return -EINVAL; } @@ -870,23 +900,6 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, } EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); -void drm_fb_helper_free(struct drm_fb_helper *helper) -{ - 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); - if (helper->fbdev->cmap.len) - fb_dealloc_cmap(&helper->fbdev->cmap); -} -EXPORT_SYMBOL(drm_fb_helper_free); - void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, uint32_t depth) { @@ -1291,7 +1304,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) * RETURNS: * Zero if everything went ok, nonzero otherwise. */ -bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) +bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) { struct drm_device *dev = fb_helper->dev; int count = 0; @@ -1304,13 +1317,12 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) count = drm_fb_helper_probe_connector_modes(fb_helper, dev->mode_config.max_width, dev->mode_config.max_height); - /* * we shouldn't end up with no modes here. */ if (count == 0) { if (fb_helper->poll_enabled) { - delayed_slow_work_enqueue(&fb_helper->output_poll_slow_work, + delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 5*HZ); printk(KERN_INFO "No connectors reported connected with modes - started polling\n"); } else @@ -1318,85 +1330,114 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) } drm_setup_crtcs(fb_helper); - return 0; + return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); } EXPORT_SYMBOL(drm_fb_helper_initial_config); -bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, - u32 max_width, u32 max_height, bool polled) +/* we got a hotplug irq - need to update fbcon */ +void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper) +{ + /* if we don't have the fbdev registered yet do nothing */ + if (!fb_helper->fbdev) + return; + + /* schedule a slow work asap */ + delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0); +} +EXPORT_SYMBOL(drm_helper_fb_hpd_irq_event); + +bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, bool polled) { int count = 0; int ret; + u32 max_width, max_height, bpp_sel; + + if (!fb_helper->fb) + return false; DRM_DEBUG_KMS("\n"); + max_width = fb_helper->fb->width; + max_height = fb_helper->fb->height; + bpp_sel = fb_helper->fb->bits_per_pixel; + count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height); if (fb_helper->poll_enabled && !polled) { if (count) { - delayed_slow_work_cancel(&fb_helper->output_poll_slow_work); + delayed_slow_work_cancel(&fb_helper->output_status_change_slow_work); } else { - ret = delayed_slow_work_enqueue(&fb_helper->output_poll_slow_work, 5*HZ); + ret = delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 5*HZ); } } drm_setup_crtcs(fb_helper); - return true; + return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); } EXPORT_SYMBOL(drm_helper_fb_hotplug_event); -static void output_poll_execute(struct slow_work *work) +/* + * delayed work queue execution function + * - check if fbdev is actually in use on the gpu + * - if not set delayed flag and repoll if necessary + * - check for connector status change + * - repoll if 0 modes found + *- call driver output status changed notifier + */ +static void output_status_change_execute(struct slow_work *work) { struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work); - struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_poll_slow_work); - struct drm_device *dev = fb_helper->dev; + struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_status_change_slow_work); struct drm_connector *connector; enum drm_connector_status old_status, status; - bool repoll = true, changed = false; + bool repoll, changed = false; int ret; + int i; + bool bound = false, crtcs_bound = false; + struct drm_crtc *crtc; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + repoll = fb_helper->poll_enabled; + + /* first of all check the fbcon framebuffer is actually bound to any crtc */ + /* take into account that no crtc at all maybe bound */ + list_for_each_entry(crtc, &fb_helper->dev->mode_config.crtc_list, head) { + if (crtc->fb) + crtcs_bound = true; + if (crtc->fb == fb_helper->fb) + bound = true; + } + + if (bound == false && crtcs_bound) { + fb_helper->delayed_hotplug = true; + goto requeue; + } + + for (i = 0; i < fb_helper->connector_count; i++) { + connector = fb_helper->connector_info[i]->connector; old_status = connector->status; status = connector->funcs->detect(connector); if (old_status != status) { changed = true; - /* something changed */ } - if (status == connector_status_connected) { + if (status == connector_status_connected && repoll) { DRM_DEBUG("%s is connected - stop polling\n", drm_get_connector_name(connector)); repoll = false; } } + if (changed) { + if (fb_helper->funcs->fb_output_status_changed) + fb_helper->funcs->fb_output_status_changed(fb_helper); + } + +requeue: if (repoll) { ret = delayed_slow_work_enqueue(delayed_work, 5*HZ); if (ret) DRM_ERROR("delayed enqueue failed %d\n", ret); } - - if (changed) { - if (fb_helper->fb_poll_changed) - fb_helper->fb_poll_changed(fb_helper); - } } -struct slow_work_ops output_poll_ops = { - .execute = output_poll_execute, +static struct slow_work_ops output_status_change_ops = { + .execute = output_status_change_execute, }; -void drm_fb_helper_poll_init(struct drm_fb_helper *fb_helper) -{ - int ret; - - ret = slow_work_register_user(THIS_MODULE); - - delayed_slow_work_init(&fb_helper->output_poll_slow_work, &output_poll_ops); - fb_helper->poll_enabled = true; -} -EXPORT_SYMBOL(drm_fb_helper_poll_init); - -void drm_fb_helper_poll_fini(struct drm_fb_helper *fb_helper) -{ - delayed_slow_work_cancel(&fb_helper->output_poll_slow_work); - slow_work_unregister_user(THIS_MODULE); -} -EXPORT_SYMBOL(drm_fb_helper_poll_fini); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 5388354da0d1..fdf08f222901 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -266,6 +266,7 @@ static void i915_hotplug_work_func(struct work_struct *work) } } /* Just fire off a uevent and let userspace tell us what to do */ + intelfb_hotplug(dev, false); drm_sysfs_hotplug_event(dev); } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9ffb9f2c9abd..11fce595882d 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -228,4 +228,6 @@ extern int intel_overlay_put_image(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int intel_overlay_attrs(struct drm_device *dev, void *data, struct drm_file *file_priv); + +void intelfb_hotplug(struct drm_device *dev, bool polled); #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 8f7a7c476098..cc726ff0a02d 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -65,12 +65,6 @@ static struct fb_ops intelfb_ops = { .fb_setcmap = drm_fb_helper_setcmap, }; -static struct drm_fb_helper_funcs intel_fb_helper_funcs = { - .gamma_set = intel_crtc_fb_gamma_set, - .gamma_get = intel_crtc_fb_gamma_get, -}; - - static int intelfb_create(struct intel_fbdev *ifbdev, struct drm_fb_helper_surface_size *sizes) { @@ -129,7 +123,6 @@ static int intelfb_create(struct intel_fbdev *ifbdev, ifbdev->helper.fb = fb; ifbdev->helper.fbdev = info; - ifbdev->helper.funcs = &intel_fb_helper_funcs; strcpy(info->fix.id, "inteldrmfb"); @@ -154,6 +147,12 @@ static int intelfb_create(struct intel_fbdev *ifbdev, ret = -ENOSPC; goto out_unpin; } + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto out_unpin; + } info->screen_size = size; // memset(info->screen_base, 0, size); @@ -205,15 +204,18 @@ static int intel_fb_find_or_create_single(struct drm_fb_helper *helper, return new_fb; } -static int intelfb_probe(struct intel_fbdev *ifbdev) +void intelfb_hotplug(struct drm_device *dev, bool polled) { - int ret; - - DRM_DEBUG_KMS("\n"); - ret = drm_fb_helper_single_fb_probe(&ifbdev->helper, 32); - return ret; + drm_i915_private_t *dev_priv = dev->dev_private; + drm_helper_fb_hpd_irq_event(&dev_priv->fbdev->helper); } +static struct drm_fb_helper_funcs intel_fb_helper_funcs = { + .gamma_set = intel_crtc_fb_gamma_set, + .gamma_get = intel_crtc_fb_gamma_get, + .fb_probe = intel_fb_find_or_create_single, +}; + int intel_fbdev_destroy(struct drm_device *dev, struct intel_fbdev *ifbdev) { @@ -224,10 +226,12 @@ int intel_fbdev_destroy(struct drm_device *dev, info = ifbdev->helper.fbdev; unregister_framebuffer(info); iounmap(info->screen_base); + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } - drm_fb_helper_free(&ifbdev->helper); + drm_fb_helper_fini(&ifbdev->helper); drm_framebuffer_cleanup(&ifb->base); if (ifb->obj) @@ -246,13 +250,13 @@ int intel_fbdev_init(struct drm_device *dev) return -ENOMEM; dev_priv->fbdev = ifbdev; + ifbdev->helper.funcs = &intel_fb_helper_funcs; + + drm_fb_helper_init(dev, &ifbdev->helper, 2, + INTELFB_CONN_LIMIT, false); - drm_fb_helper_init_crtc_count(dev, &ifbdev->helper, 2, - INTELFB_CONN_LIMIT); drm_fb_helper_single_add_all_connectors(&ifbdev->helper); - ifbdev->helper.fb_probe = intel_fb_find_or_create_single; - drm_fb_helper_initial_config(&ifbdev->helper); - intelfb_probe(ifbdev); + drm_fb_helper_initial_config(&ifbdev->helper, 32); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index fd5d3cde0a07..bc81ec7dc131 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -156,11 +156,6 @@ static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, *blue = nv_crtc->lut.b[regno]; } -static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { - .gamma_set = nouveau_fbcon_gamma_set, - .gamma_get = nouveau_fbcon_gamma_get -}; - #if defined(__i386__) || defined(__x86_64__) static bool nouveau_fbcon_has_vesafb_or_efifb(struct drm_device *dev) @@ -272,6 +267,12 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev, goto out_unref; } + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto out_unref; + } + info->par = nfbdev; nouveau_framebuffer_init(dev, &nfbdev->nouveau_fb, &mode_cmd, nvbo); @@ -282,7 +283,6 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev, /* setup helper */ nfbdev->helper.fb = fb; nfbdev->helper.fbdev = info; - nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs; strcpy(info->fix.id, "nouveaufb"); if (nouveau_nofbaccel) @@ -381,12 +381,15 @@ nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper, return new_fb; } -static int -nouveau_fbcon_probe(struct nouveau_fbdev *nfbdev) +void nouveau_fbcon_hotplug(struct drm_device *dev) { - NV_DEBUG_KMS(nfbdev->dev, "\n"); + struct drm_nouveau_private *dev_priv = dev->dev_private; + drm_helper_fb_hpd_irq_event(&dev_priv->nfbdev->helper); +} - return drm_fb_helper_single_fb_probe(&nfbdev->helper, 32); +static void nouveau_fbcon_output_status_changed(struct drm_fb_helper *fb_helper) +{ + drm_helper_fb_hotplug_event(fb_helper, true); } int @@ -398,6 +401,8 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *nfbdev) if (nfbdev->helper.fbdev) { info = nfbdev->helper.fbdev; unregister_framebuffer(info); + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } @@ -406,7 +411,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *nfbdev) drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem); nouveau_fb->nvbo = NULL; } - drm_fb_helper_free(&nfbdev->helper); + drm_fb_helper_fini(&nfbdev->helper); drm_framebuffer_cleanup(&nouveau_fb->base); return 0; } @@ -420,6 +425,14 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info) info->flags |= FBINFO_HWACCEL_DISABLED; } +static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { + .gamma_set = nouveau_fbcon_gamma_set, + .gamma_get = nouveau_fbcon_gamma_get, + .fb_probe = nouveau_fbcon_find_or_create_single, + .fb_output_status_changed = nouveau_fbcon_output_status_changed, +}; + + int nouveau_fbcon_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -431,14 +444,12 @@ int nouveau_fbcon_init(struct drm_device *dev) nfbdev->dev = dev; dev_priv->nfbdev = nfbdev; + nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs; - drm_fb_helper_init_crtc_count(dev, &nfbdev->helper, - 2, 4); - nfbdev->helper.fb_probe = nouveau_fbcon_find_or_create_single; + drm_fb_helper_init(dev, &nfbdev->helper, + 2, 4, true); drm_fb_helper_single_add_all_connectors(&nfbdev->helper); - - drm_fb_helper_initial_config(&nfbdev->helper); - nouveau_fbcon_probe(nfbdev); + drm_fb_helper_initial_config(&nfbdev->helper, 32); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h index 7835b5685555..bf8e00d4de65 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h @@ -57,5 +57,7 @@ void nouveau_fbcon_set_suspend(struct drm_device *dev, int state); void nouveau_fbcon_zfill_all(struct drm_device *dev); void nouveau_fbcon_save_disable_accel(struct drm_device *dev); void nouveau_fbcon_restore_accel(struct drm_device *dev); + +void nouveau_fbcon_hotplug(struct drm_device *dev); #endif /* __NV50_FBCON_H__ */ diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index fac6c88a2b1f..d4f06c2a61e6 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -29,6 +29,7 @@ #include "nouveau_encoder.h" #include "nouveau_connector.h" #include "nouveau_fb.h" +#include "nouveau_fbcon.h" #include "drm_crtc_helper.h" static void @@ -941,6 +942,8 @@ nv50_display_irq_hotplug(struct drm_device *dev) nv_wr32(dev, 0xe054, nv_rd32(dev, 0xe054)); if (dev_priv->chipset >= 0x90) nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074)); + + nouveau_fbcon_hotplug(dev); } void diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 7913e50fe501..93cc54fac330 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -86,11 +86,6 @@ static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bo return aligned; } -static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { - .gamma_set = radeon_crtc_fb_gamma_set, - .gamma_get = radeon_crtc_fb_gamma_get, -}; - static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj) { struct radeon_bo *rbo = gobj->driver_private; @@ -222,7 +217,6 @@ static int radeonfb_create(struct radeon_fbdev *rfbdev, /* setup helper */ rfbdev->helper.fb = fb; rfbdev->helper.fbdev = info; - rfbdev->helper.funcs = &radeon_fb_helper_funcs; memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo)); @@ -252,10 +246,18 @@ static int radeonfb_create(struct radeon_fbdev *rfbdev, info->pixmap.access_align = 32; info->pixmap.flags = FB_PIXMAP_SYSTEM; info->pixmap.scan_align = 1; + if (info->screen_base == NULL) { ret = -ENOSPC; goto out_unref; } + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + ret = -ENOMEM; + goto out_unref; + } + DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); DRM_INFO("vram apper at 0x%lX\n", (unsigned long)rdev->mc.aper_base); DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo)); @@ -309,33 +311,16 @@ int radeon_parse_options(char *options) return 0; } -static int radeonfb_probe(struct radeon_fbdev *rfbdev) -{ - struct radeon_device *rdev = rfbdev->rdev; - int bpp_sel = 32; - - /* select 8 bpp console on RN50 or 16MB cards */ - if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024)) - bpp_sel = 8; - - return drm_fb_helper_single_fb_probe(&rfbdev->helper, bpp_sel); -} - void radeonfb_hotplug(struct drm_device *dev, bool polled) { struct radeon_device *rdev = dev->dev_private; - int max_width, max_height; - - max_width = rdev->mode_info.rfbdev->rfb.base.width; - max_height = rdev->mode_info.rfbdev->rfb.base.height; - drm_helper_fb_hotplug_event(&rdev->mode_info.rfbdev->helper, max_width, max_height, polled); - radeonfb_probe(rdev->mode_info.rfbdev); + drm_helper_fb_hpd_irq_event(&rdev->mode_info.rfbdev->helper); } -static void radeon_fb_poll_changed(struct drm_fb_helper *fb_helper) +static void radeon_fb_output_status_changed(struct drm_fb_helper *fb_helper) { - radeonfb_hotplug(fb_helper->dev, true); + drm_helper_fb_hotplug_event(fb_helper, true); } static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev) @@ -347,7 +332,10 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb if (rfbdev->helper.fbdev) { info = rfbdev->helper.fbdev; + unregister_framebuffer(info); + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } @@ -361,16 +349,27 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb } drm_gem_object_unreference_unlocked(rfb->obj); } - drm_fb_helper_free(&rfbdev->helper); + drm_fb_helper_fini(&rfbdev->helper); drm_framebuffer_cleanup(&rfb->base); return 0; } -MODULE_LICENSE("GPL"); + +static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { + .gamma_set = radeon_crtc_fb_gamma_set, + .gamma_get = radeon_crtc_fb_gamma_get, + .fb_probe = radeon_fb_find_or_create_single, + .fb_output_status_changed = radeon_fb_output_status_changed, +}; int radeon_fbdev_init(struct radeon_device *rdev) { struct radeon_fbdev *rfbdev; + int bpp_sel = 32; + + /* select 8 bpp console on RN50 or 16MB cards */ + if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024)) + bpp_sel = 8; rfbdev = kzalloc(sizeof(struct radeon_fbdev), GFP_KERNEL); if (!rfbdev) @@ -378,20 +377,13 @@ int radeon_fbdev_init(struct radeon_device *rdev) rfbdev->rdev = rdev; rdev->mode_info.rfbdev = rfbdev; + rfbdev->helper.funcs = &radeon_fb_helper_funcs; - drm_fb_helper_init_crtc_count(rdev->ddev, &rfbdev->helper, - rdev->num_crtc, - RADEONFB_CONN_LIMIT); - rfbdev->helper.fb_probe = radeon_fb_find_or_create_single; - + drm_fb_helper_init(rdev->ddev, &rfbdev->helper, + rdev->num_crtc, + RADEONFB_CONN_LIMIT, true); drm_fb_helper_single_add_all_connectors(&rfbdev->helper); - - rfbdev->helper.fb_poll_changed = radeon_fb_poll_changed; - drm_fb_helper_poll_init(&rfbdev->helper); - - drm_fb_helper_initial_config(&rfbdev->helper); - radeonfb_probe(rfbdev); - + drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); return 0; } @@ -401,7 +393,6 @@ void radeon_fbdev_fini(struct radeon_device *rdev) if (!rdev->mode_info.rfbdev) return; - drm_fb_helper_poll_fini(&rdev->mode_info.rfbdev->helper); radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev); kfree(rdev->mode_info.rfbdev); rdev->mode_info.rfbdev = NULL; @@ -428,4 +419,3 @@ bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj) return true; return false; } - diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index a073d73c195e..9b55a94feada 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -32,20 +32,14 @@ #include +struct drm_fb_helper; + struct drm_fb_helper_crtc { uint32_t crtc_id; struct drm_mode_set mode_set; struct drm_display_mode *desired_mode; }; - -struct drm_fb_helper_funcs { - void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno); - void (*gamma_get)(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, int regno); -}; - /* mode specified on the command line */ struct drm_fb_helper_cmdline_mode { bool specified; @@ -69,6 +63,19 @@ struct drm_fb_helper_surface_size { u32 surface_depth; }; +struct drm_fb_helper_funcs { + void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno); + void (*gamma_get)(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno); + + int (*fb_probe)(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes); + + void (*fb_output_status_changed)(struct drm_fb_helper *helper); + +}; + struct drm_fb_helper_connector { struct drm_fb_helper_cmdline_mode cmdline_mode; struct drm_connector *connector; @@ -88,21 +95,20 @@ struct drm_fb_helper { u32 pseudo_palette[17]; struct list_head kernel_fb_list; - struct delayed_slow_work output_poll_slow_work; + struct delayed_slow_work output_status_change_slow_work; bool poll_enabled; - int (*fb_probe)(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes); - - void (*fb_poll_changed)(struct drm_fb_helper *helper); + /* we got a hotplug but fbdev wasn't running the console + delay until next set_par */ + bool delayed_hotplug; }; int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper, int preferred_bpp); -int drm_fb_helper_init_crtc_count(struct drm_device *dev, - struct drm_fb_helper *helper, int crtc_count, - int max_conn); -void drm_fb_helper_free(struct drm_fb_helper *helper); +int drm_fb_helper_init(struct drm_device *dev, + struct drm_fb_helper *helper, int crtc_count, + int max_conn, bool polled); +void drm_fb_helper_fini(struct drm_fb_helper *helper); int drm_fb_helper_blank(int blank, struct fb_info *info); int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); @@ -125,10 +131,9 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info); bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, - u32 max_width, u32 max_height, bool polled); -bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper); + bool polled); +bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper); -void drm_fb_helper_poll_init(struct drm_fb_helper *fb_helper); -void drm_fb_helper_poll_fini(struct drm_fb_helper *fb_helper); +void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper); #endif -- 2.34.1