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 |