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 <michel@daenzer.net>
Signed-off-by: Dave Airlie <airlied@redhat.com>
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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,26 +1290,27 @@
}
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;
+ bool bound = false, crtcs_bound = false;
+ struct drm_crtc *crtc;
if (!fb_helper->fb)
return false;
+
+ 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 && crtcs_bound) {
+ fb_helper->delayed_hotplug = true;
+ return false;
+ }
DRM_DEBUG_KMS("\n");
max_width = fb_helper->fb->width;
@@ -1333,82 +1319,9 @@
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;
-
- /* 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;
- }
- 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);
- }
-}
-
-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 @@
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 @@
}
}
/* 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 @@
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 @@
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 @@
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 @@
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_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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
.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 @@
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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
radeon_hpd_init(rdev);
radeon_fbdev_init(rdev);
+ drm_kms_helper_poll_init(rdev->ddev);
+
return 0;
}
@@ -1040,6 +1049,7 @@
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 @@
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 @@
.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 @@
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 @@
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_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 <linux/idr.h>
#include <linux/fb.h>
+#include <linux/slow-work.h>
struct drm_device;
struct drm_mode_set;
@@ -460,6 +461,15 @@
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 @@
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_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_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..dc5873c 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -127,4 +127,7 @@
}
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 <linux/slow-work.h>
-
struct drm_fb_helper;
struct drm_fb_helper_crtc {
@@ -71,9 +69,6 @@
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 @@
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_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 @@
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