From: Dave Airlie Date: Fri, 7 May 2010 06:42:51 +0000 (+0000) Subject: drm/fbdev: rework output polling to be back in the core. (v4) X-Git-Tag: v2.6.35-rc1~18^2~61 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=eb1f8e4f3be898df808e2dfc131099f5831d491d;p=platform%2Fkernel%2Flinux-stable.git drm/fbdev: rework output polling to be back in the core. (v4) After thinking it over a lot it made more sense for the core to deal with the output polling especially so it can notify X. v2: drop plans for fake connector - per Michel's comments - fix X patch sent to xorg-devel, add intel polled/hpd setting, add initial nouveau polled/hpd settings. v3: add config lock take inside polling, add intel/nouveau poll init/fini calls v4: config lock was a bit agressive, only needed around connector list reading. otherwise it could re-enter. glisse: discard drm_helper_hpd_irq_event v3: Reviewed-by: Michel Dänzer Signed-off-by: Dave Airlie --- diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index be5aa7d..2583ddf 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -9,6 +9,7 @@ menuconfig DRM depends on (AGP || AGP=n) && PCI && !EMULATED_CMPXCHG && MMU select I2C select I2C_ALGOBIT + select SLOW_WORK help Kernel-level support for the Direct Rendering Infrastructure (DRI) introduced in XFree86 4.0. If you say Y here, you need to select @@ -23,7 +24,6 @@ config DRM_KMS_HELPER depends on DRM select FB select FRAMEBUFFER_CONSOLE if !EMBEDDED - select SLOW_WORK help FB and CRTC helpers for KMS drivers. diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index b142ac2..7644019 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -807,3 +807,98 @@ int drm_helper_resume_force_mode(struct drm_device *dev) return 0; } EXPORT_SYMBOL(drm_helper_resume_force_mode); + +static struct slow_work_ops output_poll_ops; + +#define DRM_OUTPUT_POLL_PERIOD (10*HZ) +static void output_poll_execute(struct slow_work *work) +{ + struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work); + struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_slow_work); + struct drm_connector *connector; + enum drm_connector_status old_status, status; + bool repoll = false, changed = false; + int ret; + + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + + /* if this is HPD or polled don't check it - + TV out for instance */ + if (!connector->polled) + continue; + + else if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT)) + repoll = true; + + old_status = connector->status; + /* if we are connected and don't want to poll for disconnect + skip it */ + if (old_status == connector_status_connected && + !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT) && + !(connector->polled & DRM_CONNECTOR_POLL_HPD)) + continue; + + status = connector->funcs->detect(connector); + if (old_status != status) + changed = true; + } + + mutex_unlock(&dev->mode_config.mutex); + + if (changed) { + /* send a uevent + call fbdev */ + drm_sysfs_hotplug_event(dev); + if (dev->mode_config.funcs->output_poll_changed) + dev->mode_config.funcs->output_poll_changed(dev); + } + + if (repoll) { + ret = delayed_slow_work_enqueue(delayed_work, DRM_OUTPUT_POLL_PERIOD); + if (ret) + DRM_ERROR("delayed enqueue failed %d\n", ret); + } +} + +void drm_kms_helper_poll_init(struct drm_device *dev) +{ + struct drm_connector *connector; + bool poll = false; + int ret; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->polled) + poll = true; + } + slow_work_register_user(THIS_MODULE); + delayed_slow_work_init(&dev->mode_config.output_poll_slow_work, + &output_poll_ops); + + if (poll) { + ret = delayed_slow_work_enqueue(&dev->mode_config.output_poll_slow_work, DRM_OUTPUT_POLL_PERIOD); + if (ret) + DRM_ERROR("delayed enqueue failed %d\n", ret); + } +} +EXPORT_SYMBOL(drm_kms_helper_poll_init); + +void drm_kms_helper_poll_fini(struct drm_device *dev) +{ + delayed_slow_work_cancel(&dev->mode_config.output_poll_slow_work); + slow_work_unregister_user(THIS_MODULE); +} +EXPORT_SYMBOL(drm_kms_helper_poll_fini); + +void drm_helper_hpd_irq_event(struct drm_device *dev) +{ + if (!dev->mode_config.poll_enabled) + return; + delayed_slow_work_cancel(&dev->mode_config.output_poll_slow_work); + /* schedule a slow work asap */ + delayed_slow_work_enqueue(&dev->mode_config.output_poll_slow_work, 0); +} +EXPORT_SYMBOL(drm_helper_hpd_irq_event); + +static struct slow_work_ops output_poll_ops = { + .execute = output_poll_execute, +}; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 5db4f47..f7b8fca 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -42,8 +42,6 @@ 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) { @@ -425,19 +423,13 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *fb_helper, - int crtc_count, int max_conn_count, - bool polled) + int crtc_count, int max_conn_count) { struct drm_crtc *crtc; int ret = 0; int i; 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); @@ -494,8 +486,6 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) 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); @@ -713,7 +703,7 @@ int drm_fb_helper_set_par(struct fb_info *info) if (fb_helper->delayed_hotplug) { fb_helper->delayed_hotplug = false; - delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0); + drm_fb_helper_hotplug_event(fb_helper); } return 0; } @@ -826,7 +816,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { /* hmm everyone went away - assume VGA cable just fell out and will come back later. */ - DRM_ERROR("Cannot find any crtc or sizes - going 1024x768\n"); + DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n"); sizes.fb_width = sizes.surface_width = 1024; sizes.fb_height = sizes.surface_height = 768; } @@ -1292,12 +1282,7 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) * we shouldn't end up with no modes here. */ if (count == 0) { - if (fb_helper->poll_enabled) { - 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 - printk(KERN_INFO "No connectors reported connected with modes\n"); + printk(KERN_INFO "No connectors reported connected with modes\n"); } drm_setup_crtcs(fb_helper); @@ -1305,71 +1290,16 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) } EXPORT_SYMBOL(drm_fb_helper_initial_config); -/* 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) +bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) { 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_status_change_slow_work); - } else { - ret = delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 5*HZ); - } - } - drm_setup_crtcs(fb_helper); - - return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); -} -EXPORT_SYMBOL(drm_helper_fb_hotplug_event); - -/* - * 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_status_change_slow_work); - struct drm_connector *connector; - enum drm_connector_status old_status, status; - bool repoll, changed = false; - int ret; - int i; bool bound = false, crtcs_bound = false; struct drm_crtc *crtc; - repoll = fb_helper->poll_enabled; + if (!fb_helper->fb) + return false; - /* 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; @@ -1377,38 +1307,21 @@ static void output_status_change_execute(struct slow_work *work) bound = true; } - if (bound == false && crtcs_bound) { + if (!bound && crtcs_bound) { fb_helper->delayed_hotplug = true; - goto requeue; + return false; } + DRM_DEBUG_KMS("\n"); - 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; - } - if (status == connector_status_connected && repoll) { - DRM_DEBUG("%s is connected - stop polling\n", drm_get_connector_name(connector)); - repoll = false; - } - } + max_width = fb_helper->fb->width; + max_height = fb_helper->fb->height; + bpp_sel = fb_helper->fb->bits_per_pixel; - if (changed) { - if (fb_helper->funcs->fb_output_status_changed) - fb_helper->funcs->fb_output_status_changed(fb_helper); - } + count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, + max_height); + drm_setup_crtcs(fb_helper); -requeue: - if (repoll) { - ret = delayed_slow_work_enqueue(delayed_work, 5*HZ); - if (ret) - DRM_ERROR("delayed enqueue failed %d\n", ret); - } + return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); } - -static struct slow_work_ops output_status_change_ops = { - .execute = output_status_change_execute, -}; +EXPORT_SYMBOL(drm_fb_helper_hotplug_event); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 52e468b..8fe66ac 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1493,7 +1493,7 @@ static int i915_load_modeset_init(struct drm_device *dev, I915_WRITE(INSTPM, (1 << 5) | (1 << 21)); intel_fbdev_init(dev); - + drm_kms_helper_poll_init(dev); return 0; destroy_ringbuffer: diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index ed26b7b..b034ea3 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -271,8 +271,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); + drm_helper_hpd_irq_event(dev); } static void i915_handle_rps_change(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 26756cd..e16ac5a 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -577,5 +577,10 @@ void intel_crt_init(struct drm_device *dev) drm_sysfs_connector_add(connector); + if (I915_HAS_HOTPLUG(dev)) + connector->polled = DRM_CONNECTOR_POLL_HPD; + else + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3836f56..4d739a1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4959,6 +4959,7 @@ intel_user_framebuffer_create(struct drm_device *dev, static const struct drm_mode_config_funcs intel_mode_funcs = { .fb_create = intel_user_framebuffer_create, + .output_poll_changed = intel_fb_output_poll_changed, }; static struct drm_gem_object * @@ -5346,6 +5347,7 @@ void intel_modeset_cleanup(struct drm_device *dev) mutex_lock(&dev->struct_mutex); + drm_kms_helper_poll_fini(dev); intel_fbdev_fini(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f6299bb..6b1c9a2 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1392,6 +1392,8 @@ intel_dp_init(struct drm_device *dev, int output_reg) DRM_MODE_CONNECTOR_DisplayPort); drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); + connector->polled = DRM_CONNECTOR_POLL_HPD; + if (output_reg == DP_A) intel_encoder->type = INTEL_OUTPUT_EDP; else diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 3230e8d..df931f7 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -235,5 +235,5 @@ extern int intel_overlay_put_image(struct drm_device *dev, void *data, 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); +extern void intel_fb_output_poll_changed(struct drm_device *dev); #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 7f1eabb..6f53cf7 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -207,12 +207,6 @@ static int intel_fb_find_or_create_single(struct drm_fb_helper *helper, return new_fb; } -void intelfb_hotplug(struct drm_device *dev, bool polled) -{ - 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, @@ -256,7 +250,7 @@ int intel_fbdev_init(struct drm_device *dev) ifbdev->helper.funcs = &intel_fb_helper_funcs; drm_fb_helper_init(dev, &ifbdev->helper, 2, - INTELFB_CONN_LIMIT, false); + INTELFB_CONN_LIMIT); drm_fb_helper_single_add_all_connectors(&ifbdev->helper); drm_fb_helper_initial_config(&ifbdev->helper, 32); @@ -274,3 +268,9 @@ void intel_fbdev_fini(struct drm_device *dev) dev_priv->fbdev = NULL; } MODULE_LICENSE("GPL and additional rights"); + +void intel_fb_output_poll_changed(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper); +} diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 8a1c4ed..65727f0 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -237,6 +237,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) intel_encoder->type = INTEL_OUTPUT_HDMI; + connector->polled = DRM_CONNECTOR_POLL_HPD; connector->interlace_allowed = 0; connector->doublescan_allowed = 0; intel_encoder->crtc_mask = (1 << 0) | (1 << 1); diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 42ceb15..ca372ab 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2244,6 +2244,7 @@ intel_sdvo_dvi_init(struct intel_encoder *intel_encoder, int device) } connector = &intel_connector->base; + connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; encoder->encoder_type = DRM_MODE_ENCODER_TMDS; connector->connector_type = DRM_MODE_CONNECTOR_DVID; @@ -2310,6 +2311,7 @@ intel_sdvo_analog_init(struct intel_encoder *intel_encoder, int device) return false; connector = &intel_connector->base; + connector->polled = DRM_CONNECTOR_POLL_CONNECT; encoder->encoder_type = DRM_MODE_ENCODER_DAC; connector->connector_type = DRM_MODE_CONNECTOR_VGA; sdvo_connector = intel_connector->dev_priv; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 14afe1e..7e663a7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -843,6 +843,7 @@ nouveau_connector_create(struct drm_device *dev, switch (dcb->type) { case DCB_CONNECTOR_VGA: + connector->polled = DRM_CONNECTOR_POLL_CONNECT; if (dev_priv->card_type >= NV_50) { drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property, @@ -854,6 +855,17 @@ nouveau_connector_create(struct drm_device *dev, case DCB_CONNECTOR_TV_3: nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; break; + case DCB_CONNECTOR_DP: + case DCB_CONNECTOR_eDP: + case DCB_CONNECTOR_HDMI_0: + case DCB_CONNECTOR_HDMI_1: + case DCB_CONNECTOR_DVI_I: + case DCB_CONNECTOR_DVI_D: + if (dev_priv->card_type >= NV_50) + connector->polled = DRM_CONNECTOR_POLL_HPD; + else + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + /* fall-through */ default: nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 9d7928f..74e6b4e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -101,5 +101,6 @@ nouveau_user_framebuffer_create(struct drm_device *dev, const struct drm_mode_config_funcs nouveau_mode_config_funcs = { .fb_create = nouveau_user_framebuffer_create, + .output_poll_changed = nouveau_fbcon_output_poll_changed, }; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 2c21993..fd4a2df 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -326,15 +326,11 @@ nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper, return new_fb; } -void nouveau_fbcon_hotplug(struct drm_device *dev) +void +nouveau_fbcon_output_poll_changed(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - drm_helper_fb_hpd_irq_event(&dev_priv->nfbdev->helper); -} - -static void nouveau_fbcon_output_status_changed(struct drm_fb_helper *fb_helper) -{ - drm_helper_fb_hotplug_event(fb_helper, true); + drm_fb_helper_hotplug_event(&dev_priv->nfbdev->helper); } int @@ -374,7 +370,6 @@ 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, }; @@ -391,8 +386,7 @@ int nouveau_fbcon_init(struct drm_device *dev) dev_priv->nfbdev = nfbdev; nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs; - drm_fb_helper_init(dev, &nfbdev->helper, - 2, 4, true); + drm_fb_helper_init(dev, &nfbdev->helper, 2, 4); drm_fb_helper_single_add_all_connectors(&nfbdev->helper); 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 bf8e00d..e7e1268 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h @@ -58,6 +58,6 @@ 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); +void nouveau_fbcon_output_poll_changed(struct drm_device *dev); #endif /* __NV50_FBCON_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index c667a11..e632339 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -516,8 +516,10 @@ nouveau_card_init(struct drm_device *dev) dev_priv->init_state = NOUVEAU_CARD_INIT_DONE; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (drm_core_check_feature(dev, DRIVER_MODESET)) { nouveau_fbcon_init(dev); + drm_kms_helper_poll_init(dev); + } return 0; @@ -844,6 +846,7 @@ int nouveau_unload(struct drm_device *dev) struct drm_nouveau_private *dev_priv = dev->dev_private; if (drm_core_check_feature(dev, DRIVER_MODESET)) { + drm_kms_helper_poll_fini(dev); nouveau_fbcon_fini(dev); if (dev_priv->card_type >= NV_50) nv50_display_destroy(dev); diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index f9b3048..34156b6 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -947,7 +947,7 @@ nv50_display_irq_hotplug_bh(struct work_struct *work) if (dev_priv->chipset >= 0x90) nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074)); - nouveau_fbcon_hotplug(dev); + drm_helper_hpd_irq_event(dev); } void diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index c489346..765854e 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -1085,6 +1085,7 @@ radeon_add_atom_connector(struct drm_device *dev, drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.load_detect_property, 1); + connector->polled = DRM_CONNECTOR_POLL_CONNECT; break; case DRM_MODE_CONNECTOR_DVIA: drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); @@ -1211,6 +1212,12 @@ radeon_add_atom_connector(struct drm_device *dev, break; } + if (hpd->hpd == RADEON_HPD_NONE) { + if (i2c_bus->valid) + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + } else + connector->polled = DRM_CONNECTOR_POLL_HPD; + connector->display_info.subpixel_order = subpixel_order; drm_sysfs_connector_add(connector); return; @@ -1272,6 +1279,7 @@ radeon_add_legacy_connector(struct drm_device *dev, drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.load_detect_property, 1); + connector->polled = DRM_CONNECTOR_POLL_CONNECT; break; case DRM_MODE_CONNECTOR_DVIA: drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); @@ -1338,6 +1346,11 @@ radeon_add_legacy_connector(struct drm_device *dev, break; } + if (hpd->hpd == RADEON_HPD_NONE) { + if (i2c_bus->valid) + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + } else + connector->polled = DRM_CONNECTOR_POLL_HPD; connector->display_info.subpixel_order = subpixel_order; drm_sysfs_connector_add(connector); return; diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 243c1c4..bc9cc92 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -888,8 +888,15 @@ radeon_user_framebuffer_create(struct drm_device *dev, return &radeon_fb->base; } +static void radeon_output_poll_changed(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + radeon_fb_output_poll_changed(rdev); +} + static const struct drm_mode_config_funcs radeon_mode_funcs = { .fb_create = radeon_user_framebuffer_create, + .output_poll_changed = radeon_output_poll_changed }; struct drm_prop_enum_list { @@ -1031,6 +1038,8 @@ int radeon_modeset_init(struct radeon_device *rdev) radeon_hpd_init(rdev); radeon_fbdev_init(rdev); + drm_kms_helper_poll_init(rdev->ddev); + return 0; } @@ -1040,6 +1049,7 @@ void radeon_modeset_fini(struct radeon_device *rdev) kfree(rdev->mode_info.bios_hardcoded_edid); if (rdev->mode_info.mode_config_initialized) { + drm_kms_helper_poll_fini(rdev->ddev); radeon_hpd_fini(rdev); drm_mode_config_cleanup(rdev->ddev); rdev->mode_info.mode_config_initialized = false; diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index b494802..e192acf 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -316,16 +316,9 @@ int radeon_parse_options(char *options) return 0; } -void radeonfb_hotplug(struct drm_device *dev, bool polled) +void radeon_fb_output_poll_changed(struct radeon_device *rdev) { - struct radeon_device *rdev = dev->dev_private; - - drm_helper_fb_hpd_irq_event(&rdev->mode_info.rfbdev->helper); -} - -static void radeon_fb_output_status_changed(struct drm_fb_helper *fb_helper) -{ - drm_helper_fb_hotplug_event(fb_helper, true); + drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper); } static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev) @@ -364,7 +357,6 @@ 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) @@ -386,11 +378,10 @@ int radeon_fbdev_init(struct radeon_device *rdev) drm_fb_helper_init(rdev->ddev, &rfbdev->helper, rdev->num_crtc, - RADEONFB_CONN_LIMIT, true); + RADEONFB_CONN_LIMIT); drm_fb_helper_single_add_all_connectors(&rfbdev->helper); drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); return 0; - } void radeon_fbdev_fini(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index a95907a..8fa40ed 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -26,6 +26,7 @@ * Jerome Glisse */ #include "drmP.h" +#include "drm_crtc_helper.h" #include "radeon_drm.h" #include "radeon_reg.h" #include "radeon.h" @@ -55,9 +56,7 @@ static void radeon_hotplug_work_func(struct work_struct *work) radeon_connector_hotplug(connector); } /* Just fire off a uevent and let userspace tell us what to do */ - radeonfb_hotplug(dev, false); - - drm_sysfs_hotplug_event(dev); + drm_helper_hpd_irq_event(dev); } void radeon_driver_irq_preinstall_kms(struct drm_device *dev) diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index dd451c5..061a2a6 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -586,5 +586,6 @@ void radeon_fbdev_fini(struct radeon_device *rdev); void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state); int radeon_fbdev_total_size(struct radeon_device *rdev); bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj); -void radeonfb_hotplug(struct drm_device *dev, bool polled); + +void radeon_fb_output_poll_changed(struct radeon_device *rdev); #endif diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c560364..2e4bf92 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -31,6 +31,7 @@ #include #include +#include struct drm_device; struct drm_mode_set; @@ -460,6 +461,15 @@ enum drm_connector_force { DRM_FORCE_ON_DIGITAL, /* for DVI-I use digital connector */ }; +/* should we poll this connector for connects and disconnects */ +/* hot plug detectable */ +#define DRM_CONNECTOR_POLL_HPD (1 << 0) +/* poll for connections */ +#define DRM_CONNECTOR_POLL_CONNECT (1 << 1) +/* can cleanly poll for disconnections without flickering the screen */ +/* DACs should rarely do this without a lot of testing */ +#define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2) + /** * drm_connector - central DRM connector control structure * @crtc: CRTC this connector is currently connected to, NULL if none @@ -504,6 +514,8 @@ struct drm_connector { u32 property_ids[DRM_CONNECTOR_MAX_PROPERTY]; uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY]; + uint8_t polled; /* DRM_CONNECTOR_POLL_* */ + /* requested DPMS state */ int dpms; @@ -543,6 +555,7 @@ struct drm_mode_set { */ struct drm_mode_config_funcs { struct drm_framebuffer *(*fb_create)(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd); + void (*output_poll_changed)(struct drm_device *dev); }; struct drm_mode_group { @@ -580,6 +593,10 @@ struct drm_mode_config { struct drm_mode_config_funcs *funcs; resource_size_t fb_base; + /* output poll support */ + bool poll_enabled; + struct delayed_slow_work output_poll_slow_work; + /* pointers to standard properties */ struct list_head property_blob_list; struct drm_property *edid_property; diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index b1fa0f8..dc5873c2 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -127,4 +127,7 @@ static inline void drm_connector_helper_add(struct drm_connector *connector, } extern int drm_helper_resume_force_mode(struct drm_device *dev); +extern void drm_kms_helper_poll_init(struct drm_device *dev); +extern void drm_kms_helper_poll_fini(struct drm_device *dev); +extern void drm_helper_hpd_irq_event(struct drm_device *dev); #endif diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 9b55a94..f0a6afc 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -30,8 +30,6 @@ #ifndef DRM_FB_HELPER_H #define DRM_FB_HELPER_H -#include - struct drm_fb_helper; struct drm_fb_helper_crtc { @@ -71,9 +69,6 @@ struct drm_fb_helper_funcs { 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 { @@ -95,8 +90,6 @@ struct drm_fb_helper { u32 pseudo_palette[17]; struct list_head kernel_fb_list; - struct delayed_slow_work output_status_change_slow_work; - bool poll_enabled; /* we got a hotplug but fbdev wasn't running the console delay until next set_par */ bool delayed_hotplug; @@ -107,7 +100,7 @@ int drm_fb_helper_single_fb_probe(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); + int max_conn); 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, @@ -130,10 +123,8 @@ 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, - bool polled); +bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); 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_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper); #endif