Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb1f8e4f authored by Dave Airlie's avatar Dave Airlie
Browse files

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: default avatarDave Airlie <airlied@redhat.com>
parent 0ddfa7d5
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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,11 +1282,6 @@ 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");
	}
	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
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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)
Loading