diff options
| -rw-r--r-- | drivers/gpu/drm/msm/msm_drv.h | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_connector.c | 147 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_connector.h | 29 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_crtc.c | 13 | ||||
| -rw-r--r-- | include/uapi/drm/sde_drm.h | 10 |
5 files changed, 193 insertions, 7 deletions
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 49b6029c3342..ae3a930005b6 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -162,6 +162,7 @@ enum msm_mdp_conn_property { /* enum/bitmask properties */ CONNECTOR_PROP_TOPOLOGY_NAME, CONNECTOR_PROP_TOPOLOGY_CONTROL, + CONNECTOR_PROP_LP, /* total # of properties */ CONNECTOR_PROP_COUNT diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 5fa4c21060f9..a1cbd0cdf795 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -10,7 +10,7 @@ * GNU General Public License for more details. */ -#define pr_fmt(fmt) "sde-drm:[%s] " fmt, __func__ +#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ #include "msm_drv.h" #include "sde_kms.h" @@ -38,6 +38,13 @@ static const struct drm_prop_enum_list e_topology_control[] = { {SDE_RM_TOPCTL_PPSPLIT, "ppsplit"} }; +static const struct drm_prop_enum_list e_power_mode[] = { + {SDE_MODE_DPMS_ON, "ON"}, + {SDE_MODE_DPMS_LP1, "LP1"}, + {SDE_MODE_DPMS_LP2, "LP2"}, + {SDE_MODE_DPMS_OFF, "OFF"}, +}; + int sde_connector_get_info(struct drm_connector *connector, struct msm_display_info *info) { @@ -155,6 +162,7 @@ static void sde_connector_destroy(struct drm_connector *connector) msm_property_destroy(&c_conn->property_info); drm_connector_unregister(connector); + mutex_destroy(&c_conn->lock); sde_fence_deinit(&c_conn->retire_fence); drm_connector_cleanup(connector); kfree(c_conn); @@ -353,6 +361,56 @@ static int _sde_connector_set_hdr_info( return 0; } +static int _sde_connector_update_power_locked(struct sde_connector *c_conn) +{ + struct drm_connector *connector; + void *display; + int (*set_power)(struct drm_connector *, int, void *); + int mode, rc = 0; + + if (!c_conn) + return -EINVAL; + connector = &c_conn->base; + + mode = c_conn->lp_mode; + if (c_conn->dpms_mode != DRM_MODE_DPMS_ON) + mode = SDE_MODE_DPMS_OFF; + switch (c_conn->dpms_mode) { + case DRM_MODE_DPMS_ON: + mode = c_conn->lp_mode; + break; + case DRM_MODE_DPMS_STANDBY: + mode = SDE_MODE_DPMS_STANDBY; + break; + case DRM_MODE_DPMS_SUSPEND: + mode = SDE_MODE_DPMS_SUSPEND; + break; + case DRM_MODE_DPMS_OFF: + mode = SDE_MODE_DPMS_OFF; + break; + default: + mode = c_conn->lp_mode; + SDE_ERROR("conn %d dpms set to unrecognized mode %d\n", + connector->base.id, mode); + break; + } + + SDE_DEBUG("conn %d - dpms %d, lp %d, panel %d\n", connector->base.id, + c_conn->dpms_mode, c_conn->lp_mode, mode); + + if (mode != c_conn->last_panel_power_mode && c_conn->ops.set_power) { + display = c_conn->display; + set_power = c_conn->ops.set_power; + + mutex_unlock(&c_conn->lock); + rc = set_power(connector, mode, display); + mutex_lock(&c_conn->lock); + } + c_conn->last_panel_power_mode = mode; + + return rc; +} + static int sde_connector_atomic_set_property(struct drm_connector *connector, struct drm_connector_state *state, struct drm_property *property, @@ -379,8 +437,8 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, /* connector-specific property handling */ idx = msm_property_index(&c_conn->property_info, property); - - if (idx == CONNECTOR_PROP_OUT_FB) { + switch (idx) { + case CONNECTOR_PROP_OUT_FB: /* clear old fb, if present */ if (c_state->out_fb) _sde_connector_destroy_fb(c_conn, c_state); @@ -404,12 +462,20 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, if (rc) SDE_ERROR("prep fb failed, %d\n", rc); } - } - - if (idx == CONNECTOR_PROP_TOPOLOGY_CONTROL) { + break; + case CONNECTOR_PROP_TOPOLOGY_CONTROL: rc = sde_rm_check_property_topctl(val); if (rc) SDE_ERROR("invalid topology_control: 0x%llX\n", val); + break; + case CONNECTOR_PROP_LP: + mutex_lock(&c_conn->lock); + c_conn->lp_mode = val; + _sde_connector_update_power_locked(c_conn); + mutex_unlock(&c_conn->lock); + break; + default: + break; } if (idx == CONNECTOR_PROP_HDR_CONTROL) { @@ -510,6 +576,60 @@ void sde_connector_complete_commit(struct drm_connector *connector) sde_fence_signal(&to_sde_connector(connector)->retire_fence, 0); } +static int sde_connector_dpms(struct drm_connector *connector, + int mode) +{ + struct sde_connector *c_conn; + + if (!connector) { + SDE_ERROR("invalid connector\n"); + return -EINVAL; + } + c_conn = to_sde_connector(connector); + + /* validate incoming dpms request */ + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + SDE_DEBUG("conn %d dpms set to %d\n", + connector->base.id, mode); + break; + default: + SDE_ERROR("conn %d dpms set to unrecognized mode %d\n", + connector->base.id, mode); + break; + } + + mutex_lock(&c_conn->lock); + c_conn->dpms_mode = mode; + _sde_connector_update_power_locked(c_conn); + mutex_unlock(&c_conn->lock); + + /* use helper for boilerplate handling */ + return drm_atomic_helper_connector_dpms(connector, mode); +} + +int sde_connector_get_dpms(struct drm_connector *connector) +{ + struct sde_connector *c_conn; + int rc; + + if (!connector) { + SDE_DEBUG("invalid connector\n"); + return DRM_MODE_DPMS_OFF; + } + + c_conn = to_sde_connector(connector); + + mutex_lock(&c_conn->lock); + rc = c_conn->dpms_mode; + mutex_unlock(&c_conn->lock); + + return rc; +} + static void sde_connector_update_hdr_props(struct drm_connector *connector) { struct sde_connector *c_conn = to_sde_connector(connector); @@ -558,7 +678,7 @@ sde_connector_detect(struct drm_connector *connector, bool force) } static const struct drm_connector_funcs sde_connector_ops = { - .dpms = drm_atomic_helper_connector_dpms, + .dpms = sde_connector_dpms, .reset = sde_connector_atomic_reset, .detect = sde_connector_detect, .destroy = sde_connector_destroy, @@ -681,6 +801,11 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, c_conn->panel = panel; c_conn->display = display; + c_conn->dpms_mode = DRM_MODE_DPMS_ON; + c_conn->lp_mode = 0; + c_conn->last_panel_power_mode = SDE_MODE_DPMS_ON; + + sde_kms = to_sde_kms(priv->kms); if (sde_kms->vbif[VBIF_NRT]) { c_conn->aspace[SDE_IOMMU_DOMAIN_UNSECURE] = @@ -714,6 +839,8 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, goto error_cleanup_conn; } + mutex_init(&c_conn->lock); + rc = drm_connector_register(&c_conn->base); if (rc) { SDE_ERROR("failed to register drm connector, %d\n", rc); @@ -793,6 +920,11 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, ARRAY_SIZE(e_topology_control), CONNECTOR_PROP_TOPOLOGY_CONTROL, 0); + msm_property_install_enum(&c_conn->property_info, "LP", + 0, 0, e_power_mode, + ARRAY_SIZE(e_power_mode), + CONNECTOR_PROP_LP, 0); + rc = msm_property_install_get_status(&c_conn->property_info); if (rc) { SDE_ERROR("failed to create one or more properties\n"); @@ -819,6 +951,7 @@ error_destroy_property: error_unregister_conn: drm_connector_unregister(&c_conn->base); error_cleanup_fence: + mutex_destroy(&c_conn->lock); sde_fence_deinit(&c_conn->retire_fence); error_cleanup_conn: drm_connector_cleanup(&c_conn->base); diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h index b76ce0aaf577..f9b8c3966d74 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.h +++ b/drivers/gpu/drm/msm/sde/sde_connector.h @@ -153,6 +153,20 @@ struct sde_connector_ops { */ enum sde_csc_type (*get_csc_type)(struct drm_connector *connector, void *display); + + /** + * set_power - update dpms setting + * @connector: Pointer to drm connector structure + * @power_mode: One of the following, + * SDE_MODE_DPMS_ON + * SDE_MODE_DPMS_LP1 + * SDE_MODE_DPMS_LP2 + * SDE_MODE_DPMS_OFF + * @display: Pointer to private display structure + * Returns: Zero on success + */ + int (*set_power)(struct drm_connector *connector, + int power_mode, void *display); }; /** @@ -165,8 +179,12 @@ struct sde_connector_ops { * @mmu_secure: MMU id for secure buffers * @mmu_unsecure: MMU id for unsecure buffers * @name: ASCII name of connector + * @lock: Mutex lock object for this structure * @retire_fence: Retire fence reference * @ops: Local callback function pointer table + * @dpms_mode: DPMS property setting from user space + * @lp_mode: LP property setting from user space + * @last_panel_power_mode: Last consolidated dpms/lp mode setting * @property_info: Private structure for generic property handling * @property_data: Array of private data for generic property handling * @blob_caps: Pointer to blob structure for 'capabilities' property @@ -185,8 +203,12 @@ struct sde_connector { char name[SDE_CONNECTOR_NAME_SIZE]; + struct mutex lock; struct sde_fence retire_fence; struct sde_connector_ops ops; + int dpms_mode; + int lp_mode; + int last_panel_power_mode; struct msm_property_info property_info; struct msm_property_data property_data[CONNECTOR_PROP_COUNT]; @@ -361,5 +383,12 @@ bool sde_connector_mode_needs_full_range(struct drm_connector *connector); */ enum sde_csc_type sde_connector_get_csc_type(struct drm_connector *conn); +/** + * sde_connector_get_dpms - query dpms setting + * @connector: Pointer to drm connector structure + * Returns: Current DPMS setting for connector + */ +int sde_connector_get_dpms(struct drm_connector *connector); + #endif /* _SDE_CONNECTOR_H_ */ diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index 07c9e2172f41..983695d886fd 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -935,6 +935,15 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc) sde_kms = _sde_crtc_get_kms(crtc); priv = sde_kms->dev->dev_private; + /* + * If no mixers has been allocated in sde_crtc_atomic_check(), + * it means we are trying to start a CRTC whose state is disabled: + * nothing else needs to be done. + */ + if (unlikely(!sde_crtc->num_mixers)) + return; + + SDE_ATRACE_BEGIN("crtc_commit"); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->crtc != crtc) @@ -1345,6 +1354,10 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, mode = &state->adjusted_mode; SDE_DEBUG("%s: check", sde_crtc->name); + /* force a full mode set if active state changed */ + if (state->active_changed) + state->mode_changed = true; + mixer_width = sde_crtc_mixer_width(sde_crtc, mode); /* get plane state for all drm planes associated with crtc state */ diff --git a/include/uapi/drm/sde_drm.h b/include/uapi/drm/sde_drm.h index bef841446247..71159cb377d8 100644 --- a/include/uapi/drm/sde_drm.h +++ b/include/uapi/drm/sde_drm.h @@ -337,4 +337,14 @@ struct sde_drm_wb_cfg { uint64_t modes; }; +/** + * Define extended power modes supported by the SDE connectors. + */ +#define SDE_MODE_DPMS_ON 0 +#define SDE_MODE_DPMS_LP1 1 +#define SDE_MODE_DPMS_LP2 2 +#define SDE_MODE_DPMS_STANDBY 3 +#define SDE_MODE_DPMS_SUSPEND 4 +#define SDE_MODE_DPMS_OFF 5 + #endif /* _SDE_DRM_H_ */ |
