drm/msm/sde: add generic support for drm connectors

Add generic drm_connector support to sde driver. Place
all reusable code for common drm connector properties and
state handling inside 'sde_connector.'

Change-Id: Iff1871710e7261450d1f23fa6f0938c25b5fcb45
Signed-off-by: Clarence Ip <cip@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 3f2621e..366da8a 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -3,6 +3,7 @@
 ccflags-$(CONFIG_DRM_MSM_DSI) += -Idrivers/gpu/drm/msm/dsi
 ccflags-$(CONFIG_SYNC) += -Idrivers/staging/android
 ccflags-$(CONFIG_DRM_MSM_DSI_PLL) += -Idrivers/gpu/drm/msm/dsi
+ccflags-y += -Idrivers/gpu/drm/msm/sde
 
 msm-y := \
 	hdmi/hdmi.o \
@@ -39,6 +40,7 @@
 	sde/sde_kms_utils.o \
 	sde/sde_kms.o \
 	sde/sde_plane.o \
+	sde/sde_connector.o \
 	msm_atomic.o \
 	msm_debugfs.o \
 	msm_drv.o \
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index cdfc91e..74050e2 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -105,6 +105,23 @@
 	CRTC_PROP_COUNT
 };
 
+enum msm_mdp_conn_property {
+	/* blob properties, always put these first */
+	CONNECTOR_PROP_SDE_INFO,
+
+	/* # of blob properties */
+	CONNECTOR_PROP_BLOBCOUNT,
+
+	/* range properties */
+	CONNECTOR_PROP_OUT_FB = CONNECTOR_PROP_BLOBCOUNT,
+	CONNECTOR_PROP_RETIRE_FENCE,
+
+	/* enum/bitmask properties */
+
+	/* total # of properties */
+	CONNECTOR_PROP_COUNT
+};
+
 struct msm_vblank_ctrl {
 	struct work_struct work;
 	struct list_head event_list;
@@ -183,6 +200,7 @@
 	/* Properties */
 	struct drm_property *plane_property[PLANE_PROP_COUNT];
 	struct drm_property *crtc_property[CRTC_PROP_COUNT];
+	struct drm_property *conn_property[CONNECTOR_PROP_COUNT];
 
 	/* VRAM carveout, used when no IOMMU: */
 	struct {
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
new file mode 100644
index 0000000..28e8cbc
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -0,0 +1,502 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"sde-drm:[%s] " fmt, __func__
+#include "msm_drv.h"
+
+#include "sde_kms.h"
+#include "sde_connector.h"
+
+static void sde_connector_destroy(struct drm_connector *connector)
+{
+	struct sde_connector *c_conn;
+
+	if (!connector) {
+		SDE_ERROR("invalid connector\n");
+		return;
+	}
+
+	c_conn = to_sde_connector(connector);
+
+	if (c_conn->blob_sde_info)
+		drm_property_unreference_blob(c_conn->blob_sde_info);
+	msm_property_destroy(&c_conn->property_info);
+
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+	kfree(c_conn);
+}
+
+/**
+ * _sde_connector_destroy_fb - clean up connector state's out_fb buffer
+ * @c_conn: Pointer to sde connector structure
+ * @c_state: Pointer to sde connector state structure
+ */
+static void _sde_connector_destroy_fb(struct sde_connector *c_conn,
+		struct sde_connector_state *c_state)
+{
+	if (!c_state || !c_state->out_fb) {
+		SDE_ERROR("invalid state %pK\n", c_state);
+		return;
+	}
+
+	msm_framebuffer_cleanup(c_state->out_fb,
+			c_state->mmu_id);
+	drm_framebuffer_unreference(c_state->out_fb);
+	c_state->out_fb = NULL;
+
+	if (c_conn) {
+		c_state->property_values[CONNECTOR_PROP_OUT_FB] =
+			msm_property_get_default(&c_conn->property_info,
+					CONNECTOR_PROP_OUT_FB);
+	} else {
+		c_state->property_values[CONNECTOR_PROP_OUT_FB] = ~0;
+	}
+}
+
+static void sde_connector_atomic_destroy_state(struct drm_connector *connector,
+		struct drm_connector_state *state)
+{
+	struct sde_connector *c_conn = NULL;
+	struct sde_connector_state *c_state = NULL;
+
+	if (!state) {
+		SDE_ERROR("invalid state\n");
+		return;
+	}
+
+	/*
+	 * The base DRM framework currently always passes in a NULL
+	 * connector pointer. This is not correct, but attempt to
+	 * handle that case as much as possible.
+	 */
+	if (connector)
+		c_conn = to_sde_connector(connector);
+	c_state = to_sde_connector_state(state);
+
+	if (c_state->out_fb)
+		_sde_connector_destroy_fb(c_conn, c_state);
+
+	if (!c_conn) {
+		kfree(c_state);
+	} else {
+		/* destroy value helper */
+		msm_property_destroy_state(&c_conn->property_info, c_state,
+				c_state->property_values, 0);
+	}
+}
+
+static void sde_connector_atomic_reset(struct drm_connector *connector)
+{
+	struct sde_connector *c_conn;
+	struct sde_connector_state *c_state;
+
+	if (!connector) {
+		SDE_ERROR("invalid connector\n");
+		return;
+	}
+
+	c_conn = to_sde_connector(connector);
+
+	if (connector->state) {
+		sde_connector_atomic_destroy_state(connector, connector->state);
+		connector->state = 0;
+	}
+
+	c_state = msm_property_alloc_state(&c_conn->property_info);
+	if (!c_state) {
+		SDE_ERROR("state alloc failed\n");
+		return;
+	}
+
+	/* reset value helper, zero out state structure and reset properties */
+	msm_property_reset_state(&c_conn->property_info, c_state,
+			c_state->property_values, 0);
+
+	c_state->base.connector = connector;
+	connector->state = &c_state->base;
+}
+
+static struct drm_connector_state *
+sde_connector_atomic_duplicate_state(struct drm_connector *connector)
+{
+	struct sde_connector *c_conn;
+	struct sde_connector_state *c_state, *c_oldstate;
+	int rc;
+
+	if (!connector || !connector->state) {
+		SDE_ERROR("invalid connector %pK\n", connector);
+		return NULL;
+	}
+
+	c_conn = to_sde_connector(connector);
+	c_oldstate = to_sde_connector_state(connector->state);
+	c_state = msm_property_alloc_state(&c_conn->property_info);
+	if (!c_state) {
+		SDE_ERROR("state alloc failed\n");
+		return NULL;
+	}
+
+	/* duplicate value helper */
+	msm_property_duplicate_state(&c_conn->property_info,
+			c_oldstate, c_state, c_state->property_values, 0);
+
+	/* additional handling for drm framebuffer objects */
+	if (c_state->out_fb) {
+		drm_framebuffer_reference(c_state->out_fb);
+		rc = msm_framebuffer_prepare(c_state->out_fb,
+				c_state->mmu_id);
+		if (rc)
+			SDE_ERROR("failed to prepare fb, %d\n", rc);
+	}
+
+	return &c_state->base;
+}
+
+static int sde_connector_atomic_set_property(struct drm_connector *connector,
+		struct drm_connector_state *state,
+		struct drm_property *property,
+		uint64_t val)
+{
+	struct sde_connector *c_conn;
+	struct sde_connector_state *c_state;
+	int idx, rc;
+
+	if (!connector || !state || !property) {
+		SDE_ERROR("invalid argument(s), conn %pK, state %pK, prp %pK\n",
+				connector, state, property);
+		return -EINVAL;
+	}
+
+	c_conn = to_sde_connector(connector);
+	c_state = to_sde_connector_state(state);
+
+	/* generic property handling */
+	rc = msm_property_atomic_set(&c_conn->property_info,
+			c_state->property_values, 0, property, val);
+	if (rc)
+		goto end;
+
+	/* connector-specific property handling */
+	idx = msm_property_index(&c_conn->property_info, property);
+
+	if (idx == CONNECTOR_PROP_OUT_FB) {
+		/* clear old fb, if present */
+		if (c_state->out_fb)
+			_sde_connector_destroy_fb(c_conn, c_state);
+
+		/* convert fb val to drm framebuffer and prepare it */
+		c_state->out_fb =
+			drm_framebuffer_lookup(connector->dev, val);
+		if (!c_state->out_fb) {
+			SDE_ERROR("failed to look up fb %lld\n", val);
+			rc = -EFAULT;
+		} else {
+			c_state->mmu_id = c_conn->mmu_id;
+
+			rc = msm_framebuffer_prepare(c_state->out_fb,
+					c_state->mmu_id);
+			if (rc)
+				SDE_ERROR("prep fb failed, %d\n", rc);
+		}
+	}
+
+	/* check for custom property handling */
+	if (!rc && c_conn->ops.set_property) {
+		rc = c_conn->ops.set_property(connector,
+				state,
+				idx,
+				val,
+				c_conn->display);
+
+		/* potentially clean up out_fb if rc != 0 */
+		if ((idx == CONNECTOR_PROP_OUT_FB) && rc)
+			_sde_connector_destroy_fb(c_conn, c_state);
+	}
+end:
+	return rc;
+}
+
+static int sde_connector_set_property(struct drm_connector *connector,
+		struct drm_property *property,
+		uint64_t val)
+{
+	if (!connector) {
+		SDE_ERROR("invalid connector\n");
+		return -EINVAL;
+	}
+
+	return sde_connector_atomic_set_property(connector,
+			connector->state, property, val);
+}
+
+static int sde_connector_atomic_get_property(struct drm_connector *connector,
+		const struct drm_connector_state *state,
+		struct drm_property *property,
+		uint64_t *val)
+{
+	struct sde_connector *c_conn;
+	struct sde_connector_state *c_state;
+	int idx, rc = -EINVAL;
+
+	if (!connector || !state) {
+		SDE_ERROR("invalid argument(s), conn %pK, state %pK\n",
+				connector, state);
+		return -EINVAL;
+	}
+
+	c_conn = to_sde_connector(connector);
+	c_state = to_sde_connector_state(state);
+
+	idx = msm_property_index(&c_conn->property_info, property);
+
+	/* get cached property value */
+	rc = msm_property_atomic_get(&c_conn->property_info,
+			c_state->property_values, 0, property, val);
+
+	/* allow for custom override */
+	if (c_conn->ops.get_property)
+		rc = c_conn->ops.get_property(connector,
+				(struct drm_connector_state *)state,
+				idx,
+				val,
+				c_conn->display);
+	return rc;
+}
+
+static enum drm_connector_status
+sde_connector_detect(struct drm_connector *connector, bool force)
+{
+	enum drm_connector_status status = connector_status_unknown;
+	struct sde_connector *c_conn;
+
+	if (!connector) {
+		SDE_ERROR("invalid connector\n");
+		return status;
+	}
+
+	c_conn = to_sde_connector(connector);
+
+	if (c_conn->ops.detect)
+		status = c_conn->ops.detect(connector,
+				force,
+				c_conn->display);
+
+	return status;
+}
+
+static const struct drm_connector_funcs sde_connector_ops = {
+	.dpms =                   drm_atomic_helper_connector_dpms,
+	.reset =                  sde_connector_atomic_reset,
+	.detect =                 sde_connector_detect,
+	.destroy =                sde_connector_destroy,
+	.fill_modes =             drm_helper_probe_single_connector_modes,
+	.atomic_duplicate_state = sde_connector_atomic_duplicate_state,
+	.atomic_destroy_state =   sde_connector_atomic_destroy_state,
+	.atomic_set_property =    sde_connector_atomic_set_property,
+	.atomic_get_property =    sde_connector_atomic_get_property,
+	.set_property =           sde_connector_set_property,
+};
+
+static int sde_connector_get_modes(struct drm_connector *connector)
+{
+	struct sde_connector *c_conn;
+
+	if (!connector) {
+		SDE_ERROR("invalid connector\n");
+		return 0;
+	}
+
+	c_conn = to_sde_connector(connector);
+	if (!c_conn->ops.get_modes) {
+		SDE_DEBUG("missing get_modes callback\n");
+		return 0;
+	}
+
+	return c_conn->ops.get_modes(connector, c_conn->display);
+}
+
+static enum drm_mode_status
+sde_connector_mode_valid(struct drm_connector *connector,
+		struct drm_display_mode *mode)
+{
+	struct sde_connector *c_conn;
+
+	if (!connector || !mode) {
+		SDE_ERROR("invalid argument(s), conn %pK, mode %pK\n",
+				connector, mode);
+		return MODE_ERROR;
+	}
+
+	c_conn = to_sde_connector(connector);
+
+	if (c_conn->ops.mode_valid)
+		return c_conn->ops.mode_valid(connector, mode, c_conn->display);
+
+	/* assume all modes okay by default */
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+sde_connector_best_encoder(struct drm_connector *connector)
+{
+	struct sde_connector *c_conn = to_sde_connector(connector);
+
+	if (!connector) {
+		SDE_ERROR("invalid connector\n");
+		return NULL;
+	}
+
+	/*
+	 * This is true for now, revisit this code when multiple encoders are
+	 * supported.
+	 */
+	return c_conn->encoder;
+}
+
+static const struct drm_connector_helper_funcs sde_connector_helper_ops = {
+	.get_modes =    sde_connector_get_modes,
+	.mode_valid =   sde_connector_mode_valid,
+	.best_encoder = sde_connector_best_encoder,
+};
+
+struct drm_connector *sde_connector_init(struct drm_device *dev,
+		struct drm_encoder *encoder,
+		struct drm_panel *panel,
+		void *display,
+		const struct sde_connector_ops *ops,
+		int connector_poll,
+		int connector_type)
+{
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+	struct sde_kms_info *info;
+	struct sde_connector *c_conn = NULL;
+	int rc;
+
+	if (!dev || !dev->dev_private || !encoder) {
+		SDE_ERROR("invalid argument(s), dev %pK, enc %pK\n",
+				dev, encoder);
+		return ERR_PTR(-EINVAL);
+	}
+
+	priv = dev->dev_private;
+	if (!priv->kms) {
+		SDE_ERROR("invalid kms reference\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	c_conn = kzalloc(sizeof(*c_conn), GFP_KERNEL);
+	if (!c_conn) {
+		SDE_ERROR("failed to alloc sde connector\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	rc = drm_connector_init(dev,
+			&c_conn->base,
+			&sde_connector_ops,
+			connector_type);
+	if (rc)
+		goto error_free_conn;
+
+	c_conn->connector_type = connector_type;
+	c_conn->encoder = encoder;
+	c_conn->panel = panel;
+	c_conn->display = display;
+
+	sde_kms = to_sde_kms(priv->kms);
+	c_conn->mmu_id = sde_kms->mmu_id;
+
+	if (ops)
+		c_conn->ops = *ops;
+
+	c_conn->base.helper_private = &sde_connector_helper_ops;
+	c_conn->base.polled = connector_poll;
+	c_conn->base.interlace_allowed = 0;
+	c_conn->base.doublescan_allowed = 0;
+
+	snprintf(c_conn->name,
+			SDE_CONNECTOR_NAME_SIZE,
+			"conn%u",
+			c_conn->base.base.id);
+
+	rc = drm_connector_register(&c_conn->base);
+	if (rc) {
+		SDE_ERROR("failed to register drm connector, %d\n", rc);
+		goto error_cleanup_conn;
+	}
+
+	rc = drm_mode_connector_attach_encoder(&c_conn->base, encoder);
+	if (rc) {
+		SDE_ERROR("failed to attach encoder to connector, %d\n", rc);
+		goto error_unregister_conn;
+	}
+
+	/* create properties */
+	msm_property_init(&c_conn->property_info, &c_conn->base.base, dev,
+			priv->conn_property, c_conn->property_data,
+			CONNECTOR_PROP_COUNT, CONNECTOR_PROP_BLOBCOUNT,
+			sizeof(struct sde_connector_state));
+
+	if (c_conn->ops.post_init) {
+		info = kmalloc(sizeof(*info), GFP_KERNEL);
+		if (!info) {
+			SDE_ERROR("failed to allocate info buffer\n");
+			rc = -ENOMEM;
+			goto error_unregister_conn;
+		}
+
+		sde_kms_info_reset(info);
+		rc = c_conn->ops.post_init(&c_conn->base, info, display);
+		if (rc) {
+			SDE_ERROR("post-init failed, %d\n", rc);
+			kfree(info);
+			goto error_unregister_conn;
+		}
+
+		msm_property_install_blob(&c_conn->property_info,
+				"sde_info",
+				DRM_MODE_PROP_IMMUTABLE,
+				CONNECTOR_PROP_SDE_INFO);
+
+		msm_property_set_blob(&c_conn->property_info,
+				&c_conn->blob_sde_info,
+				SDE_KMS_INFO_DATA(info),
+				SDE_KMS_INFO_DATALEN(info),
+				CONNECTOR_PROP_SDE_INFO);
+		kfree(info);
+	}
+
+	rc = msm_property_install_get_status(&c_conn->property_info);
+	if (rc) {
+		SDE_ERROR("failed to create one or more properties\n");
+		goto error_destroy_property;
+	}
+
+	priv->connectors[priv->num_connectors++] = &c_conn->base;
+
+	return &c_conn->base;
+
+error_destroy_property:
+	if (c_conn->blob_sde_info)
+		drm_property_unreference_blob(c_conn->blob_sde_info);
+	msm_property_destroy(&c_conn->property_info);
+error_unregister_conn:
+	drm_connector_unregister(&c_conn->base);
+error_cleanup_conn:
+	drm_connector_cleanup(&c_conn->base);
+error_free_conn:
+	kfree(c_conn);
+
+	return ERR_PTR(rc);
+}
+
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
new file mode 100644
index 0000000..a4dbc2e
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -0,0 +1,240 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SDE_CONNECTOR_H_
+#define _SDE_CONNECTOR_H_
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_panel.h>
+
+#include "msm_prop.h"
+#include "sde_kms.h"
+
+#define SDE_CONNECTOR_NAME_SIZE	16
+
+struct sde_connector;
+struct sde_connector_state;
+
+/**
+ * struct sde_connector_ops - callback functions for generic sde connector
+ * Individual callbacks documented below.
+ */
+struct sde_connector_ops {
+	/**
+	 * post_init - perform additional initialization steps
+	 * @connector: Pointer to drm connector structure
+	 * @info: Pointer to sde connector info structure
+	 * @display: Pointer to private display handle
+	 * Returns: Zero on success
+	 */
+	int (*post_init)(struct drm_connector *connector,
+			void *info,
+			void *display);
+
+	/**
+	 * detect - determine if connector is connected
+	 * @connector: Pointer to drm connector structure
+	 * @force: Force detect setting from drm framework
+	 * @display: Pointer to private display handle
+	 * Returns: Connector 'is connected' status
+	 */
+	enum drm_connector_status (*detect)(struct drm_connector *connector,
+			bool force,
+			void *display);
+
+	/**
+	 * get_modes - add drm modes via drm_mode_probed_add()
+	 * @connector: Pointer to drm connector structure
+	 * @display: Pointer to private display handle
+	 * Returns: Number of modes added
+	 */
+	int (*get_modes)(struct drm_connector *connector,
+			void *display);
+
+	/**
+	 * mode_valid - determine if specified mode is valid
+	 * @connector: Pointer to drm connector structure
+	 * @mode: Pointer to drm mode structure
+	 * @display: Pointer to private display handle
+	 * Returns: Validity status for specified mode
+	 */
+	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+			struct drm_display_mode *mode,
+			void *display);
+
+	/**
+	 * set_property - set property value
+	 * @connector: Pointer to drm connector structure
+	 * @state: Pointer to drm connector state structure
+	 * @property_index: DRM property index
+	 * @value: Incoming property value
+	 * @display: Pointer to private display structure
+	 * Returns: Zero on success
+	 */
+	int (*set_property)(struct drm_connector *connector,
+			struct drm_connector_state *state,
+			int property_index,
+			uint64_t value,
+			void *display);
+
+	/**
+	 * get_property - get property value
+	 * @connector: Pointer to drm connector structure
+	 * @state: Pointer to drm connector state structure
+	 * @property_index: DRM property index
+	 * @value: Pointer to variable for accepting property value
+	 * @display: Pointer to private display structure
+	 * Returns: Zero on success
+	 */
+	int (*get_property)(struct drm_connector *connector,
+			struct drm_connector_state *state,
+			int property_index,
+			uint64_t *value,
+			void *display);
+};
+
+/**
+ * struct sde_connector - local sde connector structure
+ * @base: Base drm connector structure
+ * @connector_type: Set to one of DRM_MODE_CONNECTOR_ types
+ * @encoder: Pointer to preferred drm encoder
+ * @panel: Pointer to drm panel, if present
+ * @display: Pointer to private display data structure
+ * @mmu_id: MMU is for buffer mapping
+ * @name: ASCII name of connector
+ * @ops: Local callback function pointer table
+ * @property_info: Private structure for generic property handling
+ * @property_data: Array of private data for generic property handling
+ * @blob_sde_info: Pointer to blob structure for 'sde_info' property
+ */
+struct sde_connector {
+	struct drm_connector base;
+
+	int connector_type;
+
+	struct drm_encoder *encoder;
+	struct drm_panel *panel;
+	void *display;
+
+	int mmu_id;
+
+	char name[SDE_CONNECTOR_NAME_SIZE];
+
+	struct sde_connector_ops ops;
+
+	struct msm_property_info property_info;
+	struct msm_property_data property_data[CONNECTOR_PROP_COUNT];
+	struct drm_property_blob *blob_sde_info;
+};
+
+/**
+ * to_sde_connector - convert drm_connector pointer to sde connector pointer
+ * @X: Pointer to drm_connector structure
+ * Returns: Pointer to sde_connector structure
+ */
+#define to_sde_connector(x)     container_of((x), struct sde_connector, base)
+
+/**
+ * sde_connector_get_display - get sde connector's private display pointer
+ * @C: Pointer to drm connector structure
+ * Returns: Pointer to associated private display structure
+ */
+#define sde_connector_get_display(C) \
+	((C) ? to_sde_connector((C))->display : 0)
+
+/**
+ * sde_connector_get_panel - get sde connector's private panel pointer
+ * @C: Pointer to drm connector structure
+ * Returns: Pointer to associated private display structure
+ */
+#define sde_connector_get_panel(C) \
+	((C) ? to_sde_connector((C))->panel : 0)
+
+/**
+ * sde_connector_get_encoder - get sde connector's private encoder pointer
+ * @C: Pointer to drm connector structure
+ * Returns: Pointer to associated private encoder structure
+ */
+#define sde_connector_get_encoder(C) \
+	((C) ? to_sde_connector((C))->encoder : 0)
+
+/**
+ * sde_connector_get_propinfo - get sde connector's property info pointer
+ * @C: Pointer to drm connector structure
+ * Returns: Pointer to associated private property info structure
+ */
+#define sde_connector_get_propinfo(C) \
+	((C) ? &to_sde_connector((C))->property_info : 0)
+
+/**
+ * struct sde_connector_state - private connector status structure
+ * @base: Base drm connector structure
+ * @out_fb: Pointer to output frame buffer, if applicable
+ * @mmu_id: MMU ID for accessing frame buffer objects, if applicable
+ * @property_values: Local cache of current connector property values
+ */
+struct sde_connector_state {
+	struct drm_connector_state base;
+	struct drm_framebuffer *out_fb;
+	int mmu_id;
+	uint64_t property_values[CONNECTOR_PROP_COUNT];
+};
+
+/**
+ * to_sde_connector_state - convert drm_connector_state pointer to
+ *                          sde connector state pointer
+ * @X: Pointer to drm_connector_state structure
+ * Returns: Pointer to sde_connector_state structure
+ */
+#define to_sde_connector_state(x) \
+	container_of((x), struct sde_connector_state, base)
+
+/**
+ * sde_connector_get_property - query integer value of connector property
+ * @S: Pointer to drm connector state
+ * @X: Property index, from enum msm_mdp_connector_property
+ * Returns: Integer value of requested property
+ */
+#define sde_connector_get_property(S, X) \
+	((S) && ((X) < CONNECTOR_PROP_COUNT) ? \
+	 (to_sde_connector_state((S))->property_values[(X)]) : 0)
+
+/**
+ * sde_connector_get_out_fb - query out_fb value from sde connector state
+ * @S: Pointer to drm connector state
+ * Returns: Output fb associated with specified connector state
+ */
+#define sde_connector_get_out_fb(S) \
+	((S) ? to_sde_connector_state((S))->out_fb : 0)
+
+/**
+ * sde_connector_init - create drm connector object for a given display
+ * @dev: Pointer to drm device struct
+ * @encoder: Pointer to associated encoder
+ * @panel: Pointer to associated panel, can be NULL
+ * @display: Pointer to associated display object
+ * @ops: Pointer to callback operations function table
+ * @connector_poll: Set to appropriate DRM_CONNECTOR_POLL_ setting
+ * @connector_type: Set to appropriate DRM_MODE_CONNECTOR_ type
+ * Returns: Pointer to newly created drm connector struct
+ */
+struct drm_connector *sde_connector_init(struct drm_device *dev,
+		struct drm_encoder *encoder,
+		struct drm_panel *panel,
+		void *display,
+		const struct sde_connector_ops *ops,
+		int connector_poll,
+		int connector_type);
+
+#endif /* _SDE_CONNECTOR_H_ */
+