Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 90b282d5 authored by Clarence Ip's avatar Clarence Ip
Browse files

drm/msm/sde: add connector support for power modes



This change enables a new LP property to specify panel specific
low power modes. This is needed to differentiate between normal
panel "on" and "on but low power" scenarios, something that
is not available through the standard DRM DPMS property.

The sde connector calculates a consolidated "power mode" setting
between the DPMS and LP properties and provides a callback to
the underlying display driver(s) whenever one of them is updated.

CRs-Fixed: 2027543
Change-Id: If5c80ac9eefbf1f119bcae5513ae18c7be6f618d
Signed-off-by: default avatarClarence Ip <cip@codeaurora.org>
parent 0e6032e3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -165,6 +165,7 @@ enum msm_mdp_conn_property {
	CONNECTOR_PROP_TOPOLOGY_NAME,
	CONNECTOR_PROP_TOPOLOGY_CONTROL,
	CONNECTOR_PROP_AUTOREFRESH,
	CONNECTOR_PROP_LP,

	/* total # of properties */
	CONNECTOR_PROP_COUNT
+136 −7
Original line number Diff line number Diff line
@@ -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"
@@ -44,6 +44,12 @@ static const struct drm_prop_enum_list e_topology_control[] = {
	{SDE_RM_TOPCTL_FORCE_TILING,	"force_tiling"},
	{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"},
};

static int sde_backlight_device_update_status(struct backlight_device *bd)
{
@@ -294,6 +300,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);
@@ -541,6 +548,56 @@ static int _sde_connector_set_roi_v1(
	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,
@@ -567,8 +624,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);
@@ -598,12 +655,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_ROI_V1) {
@@ -719,6 +784,59 @@ sde_connector_detect(struct drm_connector *connector, bool force)
	return status;
}

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;
}

#ifdef CONFIG_DEBUG_FS
/**
 * sde_connector_init_debugfs - initialize connector debugfs
@@ -761,7 +879,7 @@ static void sde_connector_early_unregister(struct drm_connector *connector)
}

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,
@@ -885,6 +1003,10 @@ 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;

	/* cache mmu_id's for later */
	sde_kms = to_sde_kms(priv->kms);
	if (sde_kms->vbif[VBIF_NRT]) {
@@ -919,6 +1041,8 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
		goto error_cleanup_conn;
	}

	mutex_init(&c_conn->lock);

	rc = drm_mode_connector_attach_encoder(&c_conn->base, encoder);
	if (rc) {
		SDE_ERROR("failed to attach encoder to connector, %d\n", rc);
@@ -1006,6 +1130,10 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
			0, 1, e_topology_control,
			ARRAY_SIZE(e_topology_control),
			CONNECTOR_PROP_TOPOLOGY_CONTROL);
	msm_property_install_enum(&c_conn->property_info, "LP",
			0, 0, e_power_mode,
			ARRAY_SIZE(e_power_mode),
			CONNECTOR_PROP_LP);

	rc = msm_property_install_get_status(&c_conn->property_info);
	if (rc) {
@@ -1027,6 +1155,7 @@ struct drm_connector *sde_connector_init(struct drm_device *dev,
		drm_property_unreference_blob(c_conn->blob_hdr);
	msm_property_destroy(&c_conn->property_info);
error_cleanup_fence:
	mutex_destroy(&c_conn->lock);
	sde_fence_deinit(&c_conn->retire_fence);
error_cleanup_conn:
	drm_connector_cleanup(&c_conn->base);
+29 −0
Original line number Diff line number Diff line
@@ -169,6 +169,20 @@ struct sde_connector_ops {
	 * @enable: State of clks
	 */
	int (*clk_ctrl)(void *handle, u32 type, u32 state);

	/**
	 * 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);
};

/**
@@ -203,8 +217,12 @@ struct sde_connector_evt {
 * @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 context 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
@@ -226,8 +244,12 @@ struct sde_connector {

	char name[SDE_CONNECTOR_NAME_SIZE];

	struct mutex lock;
	struct sde_fence_context 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];
@@ -391,6 +413,13 @@ int sde_connector_get_info(struct drm_connector *connector,
 */
void sde_connector_clk_ctrl(struct drm_connector *connector, bool enable);

/**
 * 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);

/**
 * sde_connector_trigger_event - indicate that an event has occurred
 *	Any callbacks that have been registered against this event will
+12 −0
Original line number Diff line number Diff line
@@ -1901,6 +1901,14 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
	priv = sde_kms->dev->dev_private;
	cstate = to_sde_crtc_state(crtc->state);

	/*
	 * 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;

	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
		struct sde_encoder_kickoff_params params = { 0 };

@@ -2310,6 +2318,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;

	memset(pipe_staged, 0, sizeof(pipe_staged));

	mixer_width = sde_crtc_mixer_width(sde_crtc, mode);
+10 −0
Original line number Diff line number Diff line
@@ -356,4 +356,14 @@ struct sde_drm_roi_v1 {
	struct drm_clip_rect roi[SDE_MAX_ROI_V1];
};

/**
 * 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_ */