Browse Source

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>
Dave Airlie 15 năm trước cách đây
mục cha
commit
eb1f8e4f3b

+ 1 - 1
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.
 

+ 95 - 0
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,
+};

+ 18 - 105
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);
 

+ 1 - 1
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:

+ 1 - 2
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)

+ 5 - 0
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;
 }

+ 2 - 0
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) {

+ 2 - 0
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

+ 1 - 1
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__ */

+ 7 - 7
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);
+}

+ 1 - 0
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);

+ 2 - 0
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;

+ 12 - 0
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;
 

+ 1 - 0
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,
 };
 

+ 4 - 10
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;

+ 1 - 1
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__ */
 

+ 4 - 1
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);

+ 1 - 1
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

+ 13 - 0
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;

+ 10 - 0
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;

+ 3 - 12
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)

+ 2 - 3
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)

+ 2 - 1
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

+ 17 - 0
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 @@ 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;

+ 3 - 0
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

+ 2 - 11
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 @@ 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