drm: Hook up DPMS property handling in drm_crtc.c. Add drm_helper_connector_dpms.

Making the drm_crtc.c code recognize the DPMS property and invoke the
connector->dpms function doesn't remove any capability from the driver while
reducing code duplication.

That just highlighted the problem with the existing DPMS functions which
could turn off the connector, but failed to turn off any relevant crtcs. The
new drm_helper_connector_dpms function manages all of that, using the
drm_helper-specific crtc and encoder dpms functions, automatically computing
the appropriate DPMS level for each object in the system.

This fixes the current troubles in the i915 driver which left PLLs, pipes
and planes running while in DPMS_OFF mode or even while they were unused.

Signed-off-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
Keith Packard 2009-05-30 20:42:28 -07:00 committed by Dave Airlie
parent e36ebaf492
commit c9fb15f60e
10 changed files with 124 additions and 13 deletions

View File

@ -2294,7 +2294,12 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
} }
} }
if (connector->funcs->set_property) /* Do DPMS ourselves */
if (property == connector->dev->mode_config.dpms_property) {
if (connector->funcs->dpms)
(*connector->funcs->dpms)(connector, (int) out_resp->value);
ret = 0;
} else if (connector->funcs->set_property)
ret = connector->funcs->set_property(connector, property, out_resp->value); ret = connector->funcs->set_property(connector, property, out_resp->value);
/* store the property value if succesful */ /* store the property value if succesful */

View File

@ -198,6 +198,29 @@ static void drm_helper_add_std_modes(struct drm_device *dev,
} }
} }
/**
* drm_helper_encoder_in_use - check if a given encoder is in use
* @encoder: encoder to check
*
* LOCKING:
* Caller must hold mode config lock.
*
* Walk @encoders's DRM device's mode_config and see if it's in use.
*
* RETURNS:
* True if @encoder is part of the mode_config, false otherwise.
*/
bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
{
struct drm_connector *connector;
struct drm_device *dev = encoder->dev;
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder == encoder)
return true;
return false;
}
EXPORT_SYMBOL(drm_helper_encoder_in_use);
/** /**
* drm_helper_crtc_in_use - check if a given CRTC is in a mode_config * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
* @crtc: CRTC to check * @crtc: CRTC to check
@ -216,7 +239,7 @@ bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
/* FIXME: Locking around list access? */ /* FIXME: Locking around list access? */
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
if (encoder->crtc == crtc) if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder))
return true; return true;
return false; return false;
} }
@ -240,7 +263,7 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
encoder_funcs = encoder->helper_private; encoder_funcs = encoder->helper_private;
if (!encoder->crtc) if (!drm_helper_encoder_in_use(encoder))
(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
} }
@ -935,6 +958,88 @@ bool drm_helper_initial_config(struct drm_device *dev)
} }
EXPORT_SYMBOL(drm_helper_initial_config); EXPORT_SYMBOL(drm_helper_initial_config);
static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder)
{
int dpms = DRM_MODE_DPMS_OFF;
struct drm_connector *connector;
struct drm_device *dev = encoder->dev;
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder == encoder)
if (connector->dpms < dpms)
dpms = connector->dpms;
return dpms;
}
static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc)
{
int dpms = DRM_MODE_DPMS_OFF;
struct drm_connector *connector;
struct drm_device *dev = crtc->dev;
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder && connector->encoder->crtc == crtc)
if (connector->dpms < dpms)
dpms = connector->dpms;
return dpms;
}
/**
* drm_helper_connector_dpms
* @connector affected connector
* @mode DPMS mode
*
* Calls the low-level connector DPMS function, then
* calls appropriate encoder and crtc DPMS functions as well
*/
void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
{
struct drm_encoder *encoder = connector->encoder;
struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
int old_dpms;
if (mode == connector->dpms)
return;
old_dpms = connector->dpms;
connector->dpms = mode;
/* from off to on, do crtc then encoder */
if (mode < old_dpms) {
if (crtc) {
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
if (crtc_funcs->dpms)
(*crtc_funcs->dpms) (crtc,
drm_helper_choose_crtc_dpms(crtc));
}
if (encoder) {
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
if (encoder_funcs->dpms)
(*encoder_funcs->dpms) (encoder,
drm_helper_choose_encoder_dpms(encoder));
}
}
/* from on to off, do encoder then crtc */
if (mode > old_dpms) {
if (encoder) {
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
if (encoder_funcs->dpms)
(*encoder_funcs->dpms) (encoder,
drm_helper_choose_encoder_dpms(encoder));
}
if (crtc) {
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
if (crtc_funcs->dpms)
(*crtc_funcs->dpms) (crtc,
drm_helper_choose_crtc_dpms(crtc));
}
}
return;
}
EXPORT_SYMBOL(drm_helper_connector_dpms);
/** /**
* drm_hotplug_stage_two * drm_hotplug_stage_two
* @dev DRM device * @dev DRM device

View File

@ -381,11 +381,6 @@ static int intel_crt_set_property(struct drm_connector *connector,
struct drm_property *property, struct drm_property *property,
uint64_t value) uint64_t value)
{ {
struct drm_device *dev = connector->dev;
if (property == dev->mode_config.dpms_property && connector->encoder)
intel_crt_dpms(connector->encoder, (uint32_t)(value & 0xf));
return 0; return 0;
} }
@ -402,6 +397,7 @@ static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = {
}; };
static const struct drm_connector_funcs intel_crt_connector_funcs = { static const struct drm_connector_funcs intel_crt_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = intel_crt_detect, .detect = intel_crt_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = intel_crt_destroy, .destroy = intel_crt_destroy,

View File

@ -316,6 +316,7 @@ static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
}; };
static const struct drm_connector_funcs intel_dvo_connector_funcs = { static const struct drm_connector_funcs intel_dvo_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.save = intel_dvo_save, .save = intel_dvo_save,
.restore = intel_dvo_restore, .restore = intel_dvo_restore,
.detect = intel_dvo_detect, .detect = intel_dvo_detect,

View File

@ -219,6 +219,7 @@ static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
}; };
static const struct drm_connector_funcs intel_hdmi_connector_funcs = { static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.save = intel_hdmi_save, .save = intel_hdmi_save,
.restore = intel_hdmi_restore, .restore = intel_hdmi_restore,
.detect = intel_hdmi_detect, .detect = intel_hdmi_detect,

View File

@ -343,11 +343,6 @@ static int intel_lvds_set_property(struct drm_connector *connector,
struct drm_property *property, struct drm_property *property,
uint64_t value) uint64_t value)
{ {
struct drm_device *dev = connector->dev;
if (property == dev->mode_config.dpms_property && connector->encoder)
intel_lvds_dpms(connector->encoder, (uint32_t)(value & 0xf));
return 0; return 0;
} }
@ -366,6 +361,7 @@ static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs
}; };
static const struct drm_connector_funcs intel_lvds_connector_funcs = { static const struct drm_connector_funcs intel_lvds_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.save = intel_lvds_save, .save = intel_lvds_save,
.restore = intel_lvds_restore, .restore = intel_lvds_restore,
.detect = intel_lvds_detect, .detect = intel_lvds_detect,

View File

@ -1616,6 +1616,7 @@ static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = {
}; };
static const struct drm_connector_funcs intel_sdvo_connector_funcs = { static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.save = intel_sdvo_save, .save = intel_sdvo_save,
.restore = intel_sdvo_restore, .restore = intel_sdvo_restore,
.detect = intel_sdvo_detect, .detect = intel_sdvo_detect,

View File

@ -1626,6 +1626,7 @@ static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
}; };
static const struct drm_connector_funcs intel_tv_connector_funcs = { static const struct drm_connector_funcs intel_tv_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.save = intel_tv_save, .save = intel_tv_save,
.restore = intel_tv_restore, .restore = intel_tv_restore,
.detect = intel_tv_detect, .detect = intel_tv_detect,

View File

@ -471,6 +471,9 @@ 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];
/* requested DPMS state */
int dpms;
void *helper_private; void *helper_private;
uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]; uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER];

View File

@ -99,6 +99,8 @@ extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
struct drm_framebuffer *old_fb); struct drm_framebuffer *old_fb);
extern bool drm_helper_crtc_in_use(struct drm_crtc *crtc); extern bool drm_helper_crtc_in_use(struct drm_crtc *crtc);
extern void drm_helper_connector_dpms(struct drm_connector *connector, int mode);
extern int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, extern int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
struct drm_mode_fb_cmd *mode_cmd); struct drm_mode_fb_cmd *mode_cmd);