Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 1962 → Rev 1963

/drivers/video/drm/drm_crtc_helper.c
34,6 → 34,8
#include "drm_crtc_helper.h"
#include "drm_fb_helper.h"
 
static bool drm_kms_helper_poll = true;
 
static void drm_mode_validate_flag(struct drm_connector *connector,
int flags)
{
55,7 → 57,7
}
 
/**
* drm_helper_probe_connector_modes - get complete set of display modes
* drm_helper_probe_single_connector_modes - get complete set of display modes
* @dev: DRM device
* @maxX: max width for modes
* @maxY: max height for modes
86,7 → 88,8
int count = 0;
int mode_flags = 0;
 
DRM_DEBUG_KMS("%s\n", drm_get_connector_name(connector));
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
drm_get_connector_name(connector));
/* set all modes to the unverified state */
list_for_each_entry_safe(mode, t, &connector->modes, head)
mode->status = MODE_UNVERIFIED;
98,21 → 101,23
connector->status = connector_status_disconnected;
if (connector->funcs->force)
connector->funcs->force(connector);
} else
connector->status = connector->funcs->detect(connector);
} else {
connector->status = connector->funcs->detect(connector, true);
// drm_kms_helper_poll_enable(dev);
}
 
if (connector->status == connector_status_disconnected) {
DRM_DEBUG_KMS("%s is disconnected\n",
drm_get_connector_name(connector));
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n",
connector->base.id, drm_get_connector_name(connector));
drm_mode_connector_update_edid_property(connector, NULL);
goto prune;
}
 
count = (*connector_funcs->get_modes)(connector);
if (!count) {
if (count == 0 && connector->status == connector_status_connected)
count = drm_add_modes_noedid(connector, 1024, 768);
if (!count)
return 0;
}
if (count == 0)
goto prune;
 
drm_mode_connector_list_update(connector);
 
140,7 → 145,7
 
drm_mode_sort(&connector->modes);
 
DRM_DEBUG_KMS("Probed modes for %s\n",
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
drm_get_connector_name(connector));
list_for_each_entry_safe(mode, t, &connector->modes, head) {
mode->vrefresh = drm_mode_vrefresh(mode);
153,21 → 158,6
}
EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
 
int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX,
uint32_t maxY)
{
struct drm_connector *connector;
int count = 0;
 
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
count += drm_helper_probe_single_connector_modes(connector,
maxX, maxY);
}
 
return count;
}
EXPORT_SYMBOL(drm_helper_probe_connector_modes);
 
/**
* drm_helper_encoder_in_use - check if a given encoder is in use
* @encoder: encoder to check
215,6 → 205,17
}
EXPORT_SYMBOL(drm_helper_crtc_in_use);
 
static void
drm_encoder_disable(struct drm_encoder *encoder)
{
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
 
if (encoder_funcs->disable)
(*encoder_funcs->disable)(encoder);
else
(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
}
 
/**
* drm_helper_disable_unused_functions - disable unused objects
* @dev: DRM device
229,7 → 230,6
{
struct drm_encoder *encoder;
struct drm_connector *connector;
struct drm_encoder_helper_funcs *encoder_funcs;
struct drm_crtc *crtc;
 
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
240,12 → 240,8
}
 
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
encoder_funcs = encoder->helper_private;
if (!drm_helper_encoder_in_use(encoder)) {
if (encoder_funcs->disable)
(*encoder_funcs->disable)(encoder);
else
(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
drm_encoder_disable(encoder);
/* disconnector encoder from any connector */
encoder->crtc = NULL;
}
255,7 → 251,10
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
crtc->enabled = drm_helper_crtc_in_use(crtc);
if (!crtc->enabled) {
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
if (crtc_funcs->disable)
(*crtc_funcs->disable)(crtc);
else
(*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF);
crtc->fb = NULL;
}
}
262,302 → 261,6
}
EXPORT_SYMBOL(drm_helper_disable_unused_functions);
 
static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height)
{
struct drm_display_mode *mode;
 
list_for_each_entry(mode, &connector->modes, head) {
if (drm_mode_width(mode) > width ||
drm_mode_height(mode) > height)
continue;
if (mode->type & DRM_MODE_TYPE_PREFERRED)
return mode;
}
return NULL;
}
 
static bool drm_has_cmdline_mode(struct drm_connector *connector)
{
struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
struct drm_fb_helper_cmdline_mode *cmdline_mode;
 
if (!fb_help_conn)
return false;
 
cmdline_mode = &fb_help_conn->cmdline_mode;
return cmdline_mode->specified;
}
 
static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height)
{
struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
struct drm_fb_helper_cmdline_mode *cmdline_mode;
struct drm_display_mode *mode = NULL;
 
if (!fb_help_conn)
return mode;
 
cmdline_mode = &fb_help_conn->cmdline_mode;
if (cmdline_mode->specified == false)
return mode;
 
/* attempt to find a matching mode in the list of modes
* we have gotten so far, if not add a CVT mode that conforms
*/
if (cmdline_mode->rb || cmdline_mode->margins)
goto create_mode;
 
list_for_each_entry(mode, &connector->modes, head) {
/* check width/height */
if (mode->hdisplay != cmdline_mode->xres ||
mode->vdisplay != cmdline_mode->yres)
continue;
 
if (cmdline_mode->refresh_specified) {
if (mode->vrefresh != cmdline_mode->refresh)
continue;
}
 
if (cmdline_mode->interlace) {
if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
continue;
}
return mode;
}
 
create_mode:
mode = drm_cvt_mode(connector->dev, cmdline_mode->xres,
cmdline_mode->yres,
cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
cmdline_mode->rb, cmdline_mode->interlace,
cmdline_mode->margins);
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
list_add(&mode->head, &connector->modes);
return mode;
}
 
static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
{
bool enable;
 
if (strict) {
enable = connector->status == connector_status_connected;
} else {
enable = connector->status != connector_status_disconnected;
}
return enable;
}
 
static void drm_enable_connectors(struct drm_device *dev, bool *enabled)
{
bool any_enabled = false;
struct drm_connector *connector;
int i = 0;
 
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
enabled[i] = drm_connector_enabled(connector, true);
DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
enabled[i] ? "yes" : "no");
any_enabled |= enabled[i];
i++;
}
 
if (any_enabled)
return;
 
i = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
enabled[i] = drm_connector_enabled(connector, false);
i++;
}
}
 
static bool drm_target_preferred(struct drm_device *dev,
struct drm_display_mode **modes,
bool *enabled, int width, int height)
{
struct drm_connector *connector;
int i = 0;
 
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 
if (enabled[i] == false) {
i++;
continue;
}
 
DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
connector->base.id);
 
/* got for command line mode first */
modes[i] = drm_pick_cmdline_mode(connector, width, height);
if (!modes[i]) {
DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
connector->base.id);
modes[i] = drm_has_preferred_mode(connector, width, height);
}
/* No preferred modes, pick one off the list */
if (!modes[i] && !list_empty(&connector->modes)) {
list_for_each_entry(modes[i], &connector->modes, head)
break;
}
DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
"none");
i++;
}
return true;
}
 
static int drm_pick_crtcs(struct drm_device *dev,
struct drm_crtc **best_crtcs,
struct drm_display_mode **modes,
int n, int width, int height)
{
int c, o;
struct drm_connector *connector;
struct drm_connector_helper_funcs *connector_funcs;
struct drm_encoder *encoder;
struct drm_crtc *best_crtc;
int my_score, best_score, score;
struct drm_crtc **crtcs, *crtc;
 
if (n == dev->mode_config.num_connector)
return 0;
c = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (c == n)
break;
c++;
}
 
best_crtcs[n] = NULL;
best_crtc = NULL;
best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height);
if (modes[n] == NULL)
return best_score;
 
crtcs = kmalloc(dev->mode_config.num_connector *
sizeof(struct drm_crtc *), GFP_KERNEL);
if (!crtcs)
return best_score;
 
my_score = 1;
if (connector->status == connector_status_connected)
my_score++;
if (drm_has_cmdline_mode(connector))
my_score++;
if (drm_has_preferred_mode(connector, width, height))
my_score++;
 
connector_funcs = connector->helper_private;
encoder = connector_funcs->best_encoder(connector);
if (!encoder)
goto out;
 
connector->encoder = encoder;
 
/* select a crtc for this connector and then attempt to configure
remaining connectors */
c = 0;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 
if ((encoder->possible_crtcs & (1 << c)) == 0) {
c++;
continue;
}
 
for (o = 0; o < n; o++)
if (best_crtcs[o] == crtc)
break;
 
if (o < n) {
/* ignore cloning for now */
c++;
continue;
}
 
crtcs[n] = crtc;
memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *));
score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1,
width, height);
if (score > best_score) {
best_crtc = crtc;
best_score = score;
memcpy(best_crtcs, crtcs,
dev->mode_config.num_connector *
sizeof(struct drm_crtc *));
}
c++;
}
out:
kfree(crtcs);
return best_score;
}
 
static void drm_setup_crtcs(struct drm_device *dev)
{
struct drm_crtc **crtcs;
struct drm_display_mode **modes;
struct drm_encoder *encoder;
struct drm_connector *connector;
bool *enabled;
int width, height;
int i, ret;
 
DRM_DEBUG_KMS("\n");
 
width = dev->mode_config.max_width;
height = dev->mode_config.max_height;
 
/* clean out all the encoder/crtc combos */
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
encoder->crtc = NULL;
}
 
crtcs = kcalloc(dev->mode_config.num_connector,
sizeof(struct drm_crtc *), GFP_KERNEL);
modes = kcalloc(dev->mode_config.num_connector,
sizeof(struct drm_display_mode *), GFP_KERNEL);
enabled = kcalloc(dev->mode_config.num_connector,
sizeof(bool), GFP_KERNEL);
 
drm_enable_connectors(dev, enabled);
 
ret = drm_target_preferred(dev, modes, enabled, width, height);
if (!ret)
DRM_ERROR("Unable to find initial modes\n");
 
DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
 
drm_pick_crtcs(dev, crtcs, modes, 0, width, height);
 
i = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct drm_display_mode *mode = modes[i];
struct drm_crtc *crtc = crtcs[i];
 
if (connector->encoder == NULL) {
i++;
continue;
}
 
if (mode && crtc) {
DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
mode->name, crtc->base.id);
crtc->desired_mode = mode;
connector->encoder->crtc = crtc;
} else {
connector->encoder->crtc = NULL;
connector->encoder = NULL;
}
i++;
}
 
kfree(crtcs);
kfree(modes);
kfree(enabled);
}
 
/**
* drm_encoder_crtc_ok - can a given crtc drive a given encoder?
* @encoder: encoder to test
572,7 → 275,7
struct drm_crtc *tmp;
int crtc_mask = 1;
 
WARN(!crtc, "checking null crtc?");
WARN(!crtc, "checking null crtc?\n");
 
dev = crtc->dev;
 
602,11 → 305,11
encoder_funcs = encoder->helper_private;
/* Disable unused encoders */
if (encoder->crtc == NULL)
(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
drm_encoder_disable(encoder);
/* Disable encoders whose CRTC is about to change */
if (encoder_funcs->get_crtc &&
encoder->crtc != (*encoder_funcs->get_crtc)(encoder))
(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
drm_encoder_disable(encoder);
}
}
 
632,7 → 335,7
struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
struct drm_display_mode *adjusted_mode, saved_mode;
struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode;
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
struct drm_encoder_helper_funcs *encoder_funcs;
int saved_x, saved_y;
639,13 → 342,13
struct drm_encoder *encoder;
bool ret = true;
 
adjusted_mode = drm_mode_duplicate(dev, mode);
 
crtc->enabled = drm_helper_crtc_in_use(crtc);
 
if (!crtc->enabled)
return true;
 
adjusted_mode = drm_mode_duplicate(dev, mode);
 
saved_hwmode = crtc->hwmode;
saved_mode = crtc->mode;
saved_x = crtc->x;
saved_y = crtc->y;
675,6 → 378,7
if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) {
goto done;
}
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
 
/* Prepare the encoders and CRTCs before setting the mode. */
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
702,8 → 406,9
if (encoder->crtc != crtc)
continue;
 
DRM_DEBUG("%s: set mode %s %x\n", drm_get_encoder_name(encoder),
mode->name, mode->base.id);
DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n",
encoder->base.id, drm_get_encoder_name(encoder),
mode->base.id, mode->name);
encoder_funcs = encoder->helper_private;
encoder_funcs->mode_set(encoder, mode, adjusted_mode);
}
721,11 → 426,20
 
}
 
/* XXX free adjustedmode */
drm_mode_destroy(dev, adjusted_mode);
/* Store real post-adjustment hardware mode. */
crtc->hwmode = *adjusted_mode;
 
/* Calculate and store various constants which
* are later needed by vblank and swap-completion
* timestamping. They are derived from true hwmode.
*/
drm_calc_timestamping_constants(crtc);
 
/* FIXME: add subpixel order */
done:
drm_mode_destroy(dev, adjusted_mode);
if (!ret) {
crtc->hwmode = saved_hwmode;
crtc->mode = saved_mode;
crtc->x = saved_x;
crtc->y = saved_y;
765,6 → 479,7
int count = 0, ro, fail = 0;
struct drm_crtc_helper_funcs *crtc_funcs;
int ret = 0;
int i;
 
DRM_DEBUG_KMS("\n");
 
779,10 → 494,18
 
crtc_funcs = set->crtc->helper_private;
 
DRM_DEBUG_KMS("crtc: %p %d fb: %p connectors: %p num_connectors:"
" %d (x, y) (%i, %i)\n",
set->crtc, set->crtc->base.id, set->fb, set->connectors,
if (!set->mode)
set->fb = NULL;
 
if (set->fb) {
DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n",
set->crtc->base.id, set->fb->base.id,
(int)set->num_connectors, set->x, set->y);
} else {
DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id);
set->mode = NULL;
set->num_connectors = 0;
}
 
dev = set->crtc->dev;
 
911,9 → 634,15
mode_changed = true;
connector->encoder->crtc = new_crtc;
}
DRM_DEBUG_KMS("setting connector %d crtc to %p\n",
connector->base.id, new_crtc);
if (new_crtc) {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
connector->base.id, drm_get_connector_name(connector),
new_crtc->base.id);
} else {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
connector->base.id, drm_get_connector_name(connector));
}
}
 
/* mode_set_base is not a required function */
if (fb_changed && !crtc_funcs->mode_set_base)
920,26 → 649,29
mode_changed = true;
 
if (mode_changed) {
old_fb = set->crtc->fb;
set->crtc->fb = set->fb;
set->crtc->enabled = (set->mode != NULL);
if (set->mode != NULL) {
set->crtc->enabled = drm_helper_crtc_in_use(set->crtc);
if (set->crtc->enabled) {
DRM_DEBUG_KMS("attempting to set mode from"
" userspace\n");
drm_mode_debug_printmodeline(set->mode);
old_fb = set->crtc->fb;
set->crtc->fb = set->fb;
if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
set->x, set->y,
old_fb)) {
DRM_ERROR("failed to set mode on crtc %p\n",
set->crtc);
DRM_ERROR("failed to set mode on [CRTC:%d]\n",
set->crtc->base.id);
set->crtc->fb = old_fb;
ret = -EINVAL;
goto fail;
}
/* TODO are these needed? */
set->crtc->desired_x = set->x;
set->crtc->desired_y = set->y;
set->crtc->desired_mode = set->mode;
DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
for (i = 0; i < set->num_connectors; i++) {
DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
drm_get_connector_name(set->connectors[i]));
set->connectors[i]->dpms = DRM_MODE_DPMS_ON;
}
}
drm_helper_disable_unused_functions(dev);
} else if (fb_changed) {
set->crtc->x = set->x;
950,9 → 682,11
set->crtc->fb = set->fb;
ret = crtc_funcs->mode_set_base(set->crtc,
set->x, set->y, old_fb);
if (ret != 0)
if (ret != 0) {
set->crtc->fb = old_fb;
goto fail;
}
}
 
kfree(save_connectors);
kfree(save_encoders);
983,63 → 717,6
}
EXPORT_SYMBOL(drm_crtc_helper_set_config);
 
bool drm_helper_plugged_event(struct drm_device *dev)
{
DRM_DEBUG_KMS("\n");
 
drm_helper_probe_connector_modes(dev, dev->mode_config.max_width,
dev->mode_config.max_height);
 
drm_setup_crtcs(dev);
 
/* alert the driver fb layer */
dev->mode_config.funcs->fb_changed(dev);
 
/* FIXME: send hotplug event */
return true;
}
/**
* drm_initial_config - setup a sane initial connector configuration
* @dev: DRM device
*
* LOCKING:
* Called at init time, must take mode config lock.
*
* Scan the CRTCs and connectors and try to put together an initial setup.
* At the moment, this is a cloned configuration across all heads with
* a new framebuffer object as the backing store.
*
* RETURNS:
* Zero if everything went ok, nonzero otherwise.
*/
bool drm_helper_initial_config(struct drm_device *dev)
{
int count = 0;
 
/* disable all the possible outputs/crtcs before entering KMS mode */
// drm_helper_disable_unused_functions(dev);
 
// drm_fb_helper_parse_command_line(dev);
 
count = drm_helper_probe_connector_modes(dev,
dev->mode_config.max_width,
dev->mode_config.max_height);
 
/*
* we shouldn't end up with no modes here.
*/
if (count == 0)
printk(KERN_INFO "No connectors reported connected with modes\n");
 
drm_setup_crtcs(dev);
 
/* alert the driver fb layer */
dev->mode_config.funcs->fb_changed(dev);
 
return 0;
}
EXPORT_SYMBOL(drm_helper_initial_config);
 
static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder)
{
int dpms = DRM_MODE_DPMS_OFF;
1122,27 → 799,6
}
EXPORT_SYMBOL(drm_helper_connector_dpms);
 
/**
* drm_hotplug_stage_two
* @dev DRM device
* @connector hotpluged connector
*
* LOCKING.
* Caller must hold mode config lock, function might grab struct lock.
*
* Stage two of a hotplug.
*
* RETURNS:
* Zero on success, errno on failure.
*/
int drm_helper_hotplug_stage_two(struct drm_device *dev)
{
drm_helper_plugged_event(dev);
 
return 0;
}
EXPORT_SYMBOL(drm_helper_hotplug_stage_two);
 
int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
struct drm_mode_fb_cmd *mode_cmd)
{
1186,6 → 842,7
if (encoder_funcs->dpms)
(*encoder_funcs->dpms) (encoder,
drm_helper_choose_encoder_dpms(encoder));
}
 
crtc_funcs = crtc->helper_private;
if (crtc_funcs->dpms)
1193,9 → 850,119
drm_helper_choose_crtc_dpms(crtc));
}
}
}
/* disable the unused connectors while restoring the modesetting */
drm_helper_disable_unused_functions(dev);
return 0;
}
EXPORT_SYMBOL(drm_helper_resume_force_mode);
 
#if 0
 
#define DRM_OUTPUT_POLL_PERIOD (10*HZ)
static void output_poll_execute(struct work_struct *work)
{
struct delayed_work *delayed_work = to_delayed_work(work);
struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work);
struct drm_connector *connector;
enum drm_connector_status old_status;
bool repoll = false, changed = false;
 
if (!drm_kms_helper_poll)
return;
 
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;
 
connector->status = connector->funcs->detect(connector, false);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
connector->base.id,
drm_get_connector_name(connector),
old_status, connector->status);
if (old_status != connector->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)
queue_delayed_work(system_nrt_wq, delayed_work, DRM_OUTPUT_POLL_PERIOD);
}
 
void drm_kms_helper_poll_disable(struct drm_device *dev)
{
if (!dev->mode_config.poll_enabled)
return;
cancel_delayed_work_sync(&dev->mode_config.output_poll_work);
}
EXPORT_SYMBOL(drm_kms_helper_poll_disable);
 
void drm_kms_helper_poll_enable(struct drm_device *dev)
{
bool poll = false;
struct drm_connector *connector;
 
if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll)
return;
 
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->polled)
poll = true;
}
 
if (poll)
queue_delayed_work(system_nrt_wq, &dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD);
}
EXPORT_SYMBOL(drm_kms_helper_poll_enable);
 
void drm_kms_helper_poll_init(struct drm_device *dev)
{
INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute);
dev->mode_config.poll_enabled = true;
 
drm_kms_helper_poll_enable(dev);
}
EXPORT_SYMBOL(drm_kms_helper_poll_init);
 
void drm_kms_helper_poll_fini(struct drm_device *dev)
{
drm_kms_helper_poll_disable(dev);
}
EXPORT_SYMBOL(drm_kms_helper_poll_fini);
 
void drm_helper_hpd_irq_event(struct drm_device *dev)
{
if (!dev->mode_config.poll_enabled)
return;
 
/* kill timer and schedule immediate execution, this doesn't block */
cancel_delayed_work(&dev->mode_config.output_poll_work);
if (drm_kms_helper_poll)
queue_delayed_work(system_nrt_wq, &dev->mode_config.output_poll_work, 0);
}
EXPORT_SYMBOL(drm_helper_hpd_irq_event);
 
 
#endif