summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h1
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.c147
-rw-r--r--drivers/gpu/drm/msm/sde/sde_connector.h29
-rw-r--r--drivers/gpu/drm/msm/sde/sde_crtc.c13
-rw-r--r--include/uapi/drm/sde_drm.h10
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_ */