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>
This commit is contained in:
Dave Airlie 2010-05-07 06:42:51 +00:00
parent 0ddfa7d574
commit eb1f8e4f3b
26 changed files with 213 additions and 159 deletions

View file

@ -9,6 +9,7 @@ menuconfig DRM
depends on (AGP || AGP=n) && PCI && !EMULATED_CMPXCHG && MMU depends on (AGP || AGP=n) && PCI && !EMULATED_CMPXCHG && MMU
select I2C select I2C
select I2C_ALGOBIT select I2C_ALGOBIT
select SLOW_WORK
help help
Kernel-level support for the Direct Rendering Infrastructure (DRI) Kernel-level support for the Direct Rendering Infrastructure (DRI)
introduced in XFree86 4.0. If you say Y here, you need to select 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 depends on DRM
select FB select FB
select FRAMEBUFFER_CONSOLE if !EMBEDDED select FRAMEBUFFER_CONSOLE if !EMBEDDED
select SLOW_WORK
help help
FB and CRTC helpers for KMS drivers. FB and CRTC helpers for KMS drivers.

View file

@ -807,3 +807,98 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
return 0; return 0;
} }
EXPORT_SYMBOL(drm_helper_resume_force_mode); 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,
};

View file

@ -42,8 +42,6 @@ MODULE_LICENSE("GPL and additional rights");
static LIST_HEAD(kernel_fb_helper_list); static LIST_HEAD(kernel_fb_helper_list);
static struct slow_work_ops output_status_change_ops;
/* simple single crtc case helper function */ /* simple single crtc case helper function */
int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) 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, int drm_fb_helper_init(struct drm_device *dev,
struct drm_fb_helper *fb_helper, struct drm_fb_helper *fb_helper,
int crtc_count, int max_conn_count, int crtc_count, int max_conn_count)
bool polled)
{ {
struct drm_crtc *crtc; struct drm_crtc *crtc;
int ret = 0; int ret = 0;
int i; int i;
fb_helper->dev = dev; 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); 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); 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); 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) { if (fb_helper->delayed_hotplug) {
fb_helper->delayed_hotplug = false; 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; 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) { if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
/* hmm everyone went away - assume VGA cable just fell out /* hmm everyone went away - assume VGA cable just fell out
and will come back later. */ 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_width = sizes.surface_width = 1024;
sizes.fb_height = sizes.surface_height = 768; 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. * we shouldn't end up with no modes here.
*/ */
if (count == 0) { 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); drm_setup_crtcs(fb_helper);
@ -1305,26 +1290,27 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
} }
EXPORT_SYMBOL(drm_fb_helper_initial_config); EXPORT_SYMBOL(drm_fb_helper_initial_config);
/* we got a hotplug irq - need to update fbcon */ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper)
{
/* if we don't have the fbdev registered yet do nothing */
if (!fb_helper->fbdev)
return;
/* schedule a slow work asap */
delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0);
}
EXPORT_SYMBOL(drm_helper_fb_hpd_irq_event);
bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, bool polled)
{ {
int count = 0; int count = 0;
int ret;
u32 max_width, max_height, bpp_sel; u32 max_width, max_height, bpp_sel;
bool bound = false, crtcs_bound = false;
struct drm_crtc *crtc;
if (!fb_helper->fb) if (!fb_helper->fb)
return false; 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"); DRM_DEBUG_KMS("\n");
max_width = fb_helper->fb->width; max_width = fb_helper->fb->width;
@ -1333,82 +1319,9 @@ bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, bool polled)
count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
max_height); 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); drm_setup_crtcs(fb_helper);
return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
} }
EXPORT_SYMBOL(drm_helper_fb_hotplug_event); EXPORT_SYMBOL(drm_fb_helper_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,
};

View file

@ -1493,7 +1493,7 @@ static int i915_load_modeset_init(struct drm_device *dev,
I915_WRITE(INSTPM, (1 << 5) | (1 << 21)); I915_WRITE(INSTPM, (1 << 5) | (1 << 21));
intel_fbdev_init(dev); intel_fbdev_init(dev);
drm_kms_helper_poll_init(dev);
return 0; return 0;
destroy_ringbuffer: destroy_ringbuffer:

View file

@ -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 */ /* Just fire off a uevent and let userspace tell us what to do */
intelfb_hotplug(dev, false); drm_helper_hpd_irq_event(dev);
drm_sysfs_hotplug_event(dev);
} }
static void i915_handle_rps_change(struct drm_device *dev) static void i915_handle_rps_change(struct drm_device *dev)

View file

@ -577,5 +577,10 @@ void intel_crt_init(struct drm_device *dev)
drm_sysfs_connector_add(connector); 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; dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS;
} }

View file

@ -4959,6 +4959,7 @@ intel_user_framebuffer_create(struct drm_device *dev,
static const struct drm_mode_config_funcs intel_mode_funcs = { static const struct drm_mode_config_funcs intel_mode_funcs = {
.fb_create = intel_user_framebuffer_create, .fb_create = intel_user_framebuffer_create,
.output_poll_changed = intel_fb_output_poll_changed,
}; };
static struct drm_gem_object * static struct drm_gem_object *
@ -5346,6 +5347,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
drm_kms_helper_poll_fini(dev);
intel_fbdev_fini(dev); intel_fbdev_fini(dev);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {

View file

@ -1392,6 +1392,8 @@ intel_dp_init(struct drm_device *dev, int output_reg)
DRM_MODE_CONNECTOR_DisplayPort); DRM_MODE_CONNECTOR_DisplayPort);
drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
connector->polled = DRM_CONNECTOR_POLL_HPD;
if (output_reg == DP_A) if (output_reg == DP_A)
intel_encoder->type = INTEL_OUTPUT_EDP; intel_encoder->type = INTEL_OUTPUT_EDP;
else else

View file

@ -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, extern int intel_overlay_attrs(struct drm_device *dev, void *data,
struct drm_file *file_priv); 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__ */ #endif /* __INTEL_DRV_H__ */

View file

@ -207,12 +207,6 @@ static int intel_fb_find_or_create_single(struct drm_fb_helper *helper,
return new_fb; 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 = { static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
.gamma_set = intel_crtc_fb_gamma_set, .gamma_set = intel_crtc_fb_gamma_set,
.gamma_get = intel_crtc_fb_gamma_get, .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; ifbdev->helper.funcs = &intel_fb_helper_funcs;
drm_fb_helper_init(dev, &ifbdev->helper, 2, 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_single_add_all_connectors(&ifbdev->helper);
drm_fb_helper_initial_config(&ifbdev->helper, 32); drm_fb_helper_initial_config(&ifbdev->helper, 32);
@ -274,3 +268,9 @@ void intel_fbdev_fini(struct drm_device *dev)
dev_priv->fbdev = NULL; dev_priv->fbdev = NULL;
} }
MODULE_LICENSE("GPL and additional rights"); 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);
}

View file

@ -237,6 +237,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
intel_encoder->type = INTEL_OUTPUT_HDMI; intel_encoder->type = INTEL_OUTPUT_HDMI;
connector->polled = DRM_CONNECTOR_POLL_HPD;
connector->interlace_allowed = 0; connector->interlace_allowed = 0;
connector->doublescan_allowed = 0; connector->doublescan_allowed = 0;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1); intel_encoder->crtc_mask = (1 << 0) | (1 << 1);

View file

@ -2244,6 +2244,7 @@ intel_sdvo_dvi_init(struct intel_encoder *intel_encoder, int device)
} }
connector = &intel_connector->base; connector = &intel_connector->base;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
encoder->encoder_type = DRM_MODE_ENCODER_TMDS; encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
connector->connector_type = DRM_MODE_CONNECTOR_DVID; connector->connector_type = DRM_MODE_CONNECTOR_DVID;
@ -2310,6 +2311,7 @@ intel_sdvo_analog_init(struct intel_encoder *intel_encoder, int device)
return false; return false;
connector = &intel_connector->base; connector = &intel_connector->base;
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
encoder->encoder_type = DRM_MODE_ENCODER_DAC; encoder->encoder_type = DRM_MODE_ENCODER_DAC;
connector->connector_type = DRM_MODE_CONNECTOR_VGA; connector->connector_type = DRM_MODE_CONNECTOR_VGA;
sdvo_connector = intel_connector->dev_priv; sdvo_connector = intel_connector->dev_priv;

View file

@ -843,6 +843,7 @@ nouveau_connector_create(struct drm_device *dev,
switch (dcb->type) { switch (dcb->type) {
case DCB_CONNECTOR_VGA: case DCB_CONNECTOR_VGA:
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
if (dev_priv->card_type >= NV_50) { if (dev_priv->card_type >= NV_50) {
drm_connector_attach_property(connector, drm_connector_attach_property(connector,
dev->mode_config.scaling_mode_property, dev->mode_config.scaling_mode_property,
@ -854,6 +855,17 @@ nouveau_connector_create(struct drm_device *dev,
case DCB_CONNECTOR_TV_3: case DCB_CONNECTOR_TV_3:
nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
break; 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: default:
nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN; nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;

View file

@ -101,5 +101,6 @@ nouveau_user_framebuffer_create(struct drm_device *dev,
const struct drm_mode_config_funcs nouveau_mode_config_funcs = { const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
.fb_create = nouveau_user_framebuffer_create, .fb_create = nouveau_user_framebuffer_create,
.output_poll_changed = nouveau_fbcon_output_poll_changed,
}; };

View file

@ -326,15 +326,11 @@ nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper,
return new_fb; 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; struct drm_nouveau_private *dev_priv = dev->dev_private;
drm_helper_fb_hpd_irq_event(&dev_priv->nfbdev->helper); drm_fb_helper_hotplug_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);
} }
int int
@ -374,7 +370,6 @@ static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
.gamma_set = nouveau_fbcon_gamma_set, .gamma_set = nouveau_fbcon_gamma_set,
.gamma_get = nouveau_fbcon_gamma_get, .gamma_get = nouveau_fbcon_gamma_get,
.fb_probe = nouveau_fbcon_find_or_create_single, .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; dev_priv->nfbdev = nfbdev;
nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs; nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs;
drm_fb_helper_init(dev, &nfbdev->helper, drm_fb_helper_init(dev, &nfbdev->helper, 2, 4);
2, 4, true);
drm_fb_helper_single_add_all_connectors(&nfbdev->helper); drm_fb_helper_single_add_all_connectors(&nfbdev->helper);
drm_fb_helper_initial_config(&nfbdev->helper, 32); drm_fb_helper_initial_config(&nfbdev->helper, 32);
return 0; return 0;

View file

@ -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_save_disable_accel(struct drm_device *dev);
void nouveau_fbcon_restore_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__ */ #endif /* __NV50_FBCON_H__ */

View file

@ -516,8 +516,10 @@ nouveau_card_init(struct drm_device *dev)
dev_priv->init_state = NOUVEAU_CARD_INIT_DONE; 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); nouveau_fbcon_init(dev);
drm_kms_helper_poll_init(dev);
}
return 0; return 0;
@ -844,6 +846,7 @@ int nouveau_unload(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
if (drm_core_check_feature(dev, DRIVER_MODESET)) { if (drm_core_check_feature(dev, DRIVER_MODESET)) {
drm_kms_helper_poll_fini(dev);
nouveau_fbcon_fini(dev); nouveau_fbcon_fini(dev);
if (dev_priv->card_type >= NV_50) if (dev_priv->card_type >= NV_50)
nv50_display_destroy(dev); nv50_display_destroy(dev);

View file

@ -947,7 +947,7 @@ nv50_display_irq_hotplug_bh(struct work_struct *work)
if (dev_priv->chipset >= 0x90) if (dev_priv->chipset >= 0x90)
nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074)); nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074));
nouveau_fbcon_hotplug(dev); drm_helper_hpd_irq_event(dev);
} }
void void

View file

@ -1085,6 +1085,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_attach_property(&radeon_connector->base, drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.load_detect_property, rdev->mode_info.load_detect_property,
1); 1);
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
break; break;
case DRM_MODE_CONNECTOR_DVIA: case DRM_MODE_CONNECTOR_DVIA:
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); 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; 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; connector->display_info.subpixel_order = subpixel_order;
drm_sysfs_connector_add(connector); drm_sysfs_connector_add(connector);
return; return;
@ -1272,6 +1279,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
drm_connector_attach_property(&radeon_connector->base, drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.load_detect_property, rdev->mode_info.load_detect_property,
1); 1);
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
break; break;
case DRM_MODE_CONNECTOR_DVIA: case DRM_MODE_CONNECTOR_DVIA:
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); 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; 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; connector->display_info.subpixel_order = subpixel_order;
drm_sysfs_connector_add(connector); drm_sysfs_connector_add(connector);
return; return;

View file

@ -888,8 +888,15 @@ radeon_user_framebuffer_create(struct drm_device *dev,
return &radeon_fb->base; 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 = { static const struct drm_mode_config_funcs radeon_mode_funcs = {
.fb_create = radeon_user_framebuffer_create, .fb_create = radeon_user_framebuffer_create,
.output_poll_changed = radeon_output_poll_changed
}; };
struct drm_prop_enum_list { struct drm_prop_enum_list {
@ -1031,6 +1038,8 @@ int radeon_modeset_init(struct radeon_device *rdev)
radeon_hpd_init(rdev); radeon_hpd_init(rdev);
radeon_fbdev_init(rdev); radeon_fbdev_init(rdev);
drm_kms_helper_poll_init(rdev->ddev);
return 0; return 0;
} }
@ -1040,6 +1049,7 @@ void radeon_modeset_fini(struct radeon_device *rdev)
kfree(rdev->mode_info.bios_hardcoded_edid); kfree(rdev->mode_info.bios_hardcoded_edid);
if (rdev->mode_info.mode_config_initialized) { if (rdev->mode_info.mode_config_initialized) {
drm_kms_helper_poll_fini(rdev->ddev);
radeon_hpd_fini(rdev); radeon_hpd_fini(rdev);
drm_mode_config_cleanup(rdev->ddev); drm_mode_config_cleanup(rdev->ddev);
rdev->mode_info.mode_config_initialized = false; rdev->mode_info.mode_config_initialized = false;

View file

@ -316,16 +316,9 @@ int radeon_parse_options(char *options)
return 0; 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_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper);
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);
} }
static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev) 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_set = radeon_crtc_fb_gamma_set,
.gamma_get = radeon_crtc_fb_gamma_get, .gamma_get = radeon_crtc_fb_gamma_get,
.fb_probe = radeon_fb_find_or_create_single, .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) 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, drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
rdev->num_crtc, rdev->num_crtc,
RADEONFB_CONN_LIMIT, true); RADEONFB_CONN_LIMIT);
drm_fb_helper_single_add_all_connectors(&rfbdev->helper); drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
return 0; return 0;
} }
void radeon_fbdev_fini(struct radeon_device *rdev) void radeon_fbdev_fini(struct radeon_device *rdev)

View file

@ -26,6 +26,7 @@
* Jerome Glisse * Jerome Glisse
*/ */
#include "drmP.h" #include "drmP.h"
#include "drm_crtc_helper.h"
#include "radeon_drm.h" #include "radeon_drm.h"
#include "radeon_reg.h" #include "radeon_reg.h"
#include "radeon.h" #include "radeon.h"
@ -55,9 +56,7 @@ static void radeon_hotplug_work_func(struct work_struct *work)
radeon_connector_hotplug(connector); radeon_connector_hotplug(connector);
} }
/* Just fire off a uevent and let userspace tell us what to do */ /* Just fire off a uevent and let userspace tell us what to do */
radeonfb_hotplug(dev, false); drm_helper_hpd_irq_event(dev);
drm_sysfs_hotplug_event(dev);
} }
void radeon_driver_irq_preinstall_kms(struct drm_device *dev) void radeon_driver_irq_preinstall_kms(struct drm_device *dev)

View file

@ -586,5 +586,6 @@ void radeon_fbdev_fini(struct radeon_device *rdev);
void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state); void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state);
int radeon_fbdev_total_size(struct radeon_device *rdev); int radeon_fbdev_total_size(struct radeon_device *rdev);
bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj); 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 #endif

View file

@ -31,6 +31,7 @@
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/slow-work.h>
struct drm_device; struct drm_device;
struct drm_mode_set; struct drm_mode_set;
@ -460,6 +461,15 @@ enum drm_connector_force {
DRM_FORCE_ON_DIGITAL, /* for DVI-I use digital connector */ 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 * drm_connector - central DRM connector control structure
* @crtc: CRTC this connector is currently connected to, NULL if none * @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]; u32 property_ids[DRM_CONNECTOR_MAX_PROPERTY];
uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY]; uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY];
uint8_t polled; /* DRM_CONNECTOR_POLL_* */
/* requested DPMS state */ /* requested DPMS state */
int dpms; int dpms;
@ -543,6 +555,7 @@ struct drm_mode_set {
*/ */
struct drm_mode_config_funcs { 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); 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 { struct drm_mode_group {
@ -580,6 +593,10 @@ struct drm_mode_config {
struct drm_mode_config_funcs *funcs; struct drm_mode_config_funcs *funcs;
resource_size_t fb_base; resource_size_t fb_base;
/* output poll support */
bool poll_enabled;
struct delayed_slow_work output_poll_slow_work;
/* pointers to standard properties */ /* pointers to standard properties */
struct list_head property_blob_list; struct list_head property_blob_list;
struct drm_property *edid_property; struct drm_property *edid_property;

View file

@ -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 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 #endif

View file

@ -30,8 +30,6 @@
#ifndef DRM_FB_HELPER_H #ifndef DRM_FB_HELPER_H
#define DRM_FB_HELPER_H #define DRM_FB_HELPER_H
#include <linux/slow-work.h>
struct drm_fb_helper; struct drm_fb_helper;
struct drm_fb_helper_crtc { struct drm_fb_helper_crtc {
@ -71,9 +69,6 @@ struct drm_fb_helper_funcs {
int (*fb_probe)(struct drm_fb_helper *helper, int (*fb_probe)(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes); struct drm_fb_helper_surface_size *sizes);
void (*fb_output_status_changed)(struct drm_fb_helper *helper);
}; };
struct drm_fb_helper_connector { struct drm_fb_helper_connector {
@ -95,8 +90,6 @@ struct drm_fb_helper {
u32 pseudo_palette[17]; u32 pseudo_palette[17];
struct list_head kernel_fb_list; 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 /* we got a hotplug but fbdev wasn't running the console
delay until next set_par */ delay until next set_par */
bool delayed_hotplug; 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, int drm_fb_helper_init(struct drm_device *dev,
struct drm_fb_helper *helper, int crtc_count, 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); 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_blank(int blank, struct fb_info *info);
int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, 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); 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 drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper);
bool polled);
bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); 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); 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 #endif