diff mbox

[38/72] imx-drm: Crtcs moved to device tree

Message ID 1414796095-10107-39-git-send-email-steve_longerbeam@mentor.com (mailing list archive)
State New, archived
Headers show

Commit Message

Steve Longerbeam Oct. 31, 2014, 10:54 p.m. UTC
The crtcs are now nodes in the device tree, and the encoder endpoints
fall under the crtc port.

ipu_client_platformdata is no longer needed and is gone. The crtc
retrieves the following required parameters from its node:

- ipu phandle.
- di number.
- the port endpoints.

Optionally, "dual-plane" can be specified to configure the crtc device
with a foreground plane. If not given, the crtc will have only a
single plane.

The DC and IDMAC channels can be inferred from the dual-plane parameter.
In table form, the channel usage is:

                 Background Plane   Foreground Plane     Single Plane
                 ----------------   ----------------     ------------
    Flow Type    IDMAC   DC   DP     IDMAC   DP           IDMAC   DC
    ---------    -----  ---  ---     -----  ---           -----  ---
    Sync          23     5    0       27     1             28     1
    Async         24     6    2       29     3             41     2

Async flows are included in the above table but async displays are
not yet supported by the crtc and plane drivers.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/staging/imx-drm/imx-drm-core.c |   28 +++--
 drivers/staging/imx-drm/ipuv3-crtc.c   |  204 +++++++++++++++++++++++---------
 2 files changed, 162 insertions(+), 70 deletions(-)
diff mbox

Patch

diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c
index 9cb222e..e5ec010 100644
--- a/drivers/staging/imx-drm/imx-drm-core.c
+++ b/drivers/staging/imx-drm/imx-drm-core.c
@@ -591,37 +591,43 @@  static const struct component_master_ops imx_drm_ops = {
 
 static int imx_drm_platform_probe(struct platform_device *pdev)
 {
-	struct device_node *ep, *port, *remote;
+	struct device_node *ep, *crtc, *port, *remote;
 	struct component_match *match = NULL;
 	int ret;
 	int i;
 
 	/*
-	 * Bind the IPU display interface ports first, so that
-	 * imx_drm_encoder_parse_of called from encoder .bind callbacks
-	 * works as expected.
+	 * Bind the crtcs first, so that imx_drm_encoder_parse_of called
+	 * from encoder .bind callbacks works as expected.
 	 */
 	for (i = 0; ; i++) {
-		port = of_parse_phandle(pdev->dev.of_node, "ports", i);
-		if (!port)
+		crtc = of_parse_phandle(pdev->dev.of_node, "crtcs", i);
+		if (!crtc)
 			break;
 
-		component_match_add(&pdev->dev, &match, compare_of, port);
+		component_match_add(&pdev->dev, &match, compare_of, crtc);
 	}
 
 	if (i == 0) {
-		dev_err(&pdev->dev, "missing 'ports' property\n");
+		dev_err(&pdev->dev, "missing 'crtcs' property\n");
 		return -ENODEV;
 	}
 
 	/* Then bind all encoders */
 	for (i = 0; ; i++) {
-		port = of_parse_phandle(pdev->dev.of_node, "ports", i);
-		if (!port)
+		crtc = of_parse_phandle(pdev->dev.of_node, "crtcs", i);
+		if (!crtc)
 			break;
 
+		port = of_get_child_by_name(crtc, "port");
+		if (!port) {
+			dev_warn(&pdev->dev, "%s has no port\n", crtc->name);
+			continue;
+		}
+
 		for_each_child_of_node(port, ep) {
 			remote = of_graph_get_remote_port_parent(ep);
+
 			if (!remote || !of_device_is_available(remote)) {
 				of_node_put(remote);
 				continue;
@@ -635,7 +641,7 @@  static int imx_drm_platform_probe(struct platform_device *pdev)
 			component_match_add(&pdev->dev, &match, compare_of, remote);
 			of_node_put(remote);
 		}
-		of_node_put(port);
+		of_node_put(crtc);
 	}
 
 	ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c
index 8b4440a..5a60017 100644
--- a/drivers/staging/imx-drm/ipuv3-crtc.c
+++ b/drivers/staging/imx-drm/ipuv3-crtc.c
@@ -36,10 +36,52 @@ 
 
 #define DRIVER_DESC		"i.MX IPUv3 Graphics"
 
+struct ipu_channels {
+	int dma[2]; /* BG, FG */
+	int dp[2];  /* BG, FG */
+	int dc;
+};
+
+#define NO_DP -1
+
+static const struct ipu_channels sync_dual_plane = {
+	.dma = { IPUV3_CHANNEL_MEM_BG_SYNC, IPUV3_CHANNEL_MEM_FG_SYNC },
+	.dp  = { IPU_DP_FLOW_SYNC_BG, IPU_DP_FLOW_SYNC_FG },
+	.dc  = IPU_DC_CHANNEL_DP_SYNC,
+};
+static const struct ipu_channels sync_single_plane = {
+	.dma = { IPUV3_CHANNEL_MEM_DC_SYNC, },
+	.dp  = { NO_DP, NO_DP },
+	.dc  = IPU_DC_CHANNEL_SYNC,
+};
+
+/*
+ * This driver does not yet support async flows for "smart" displays,
+ * but keep this around for future reference. The crtc nodes could in
+ * future add an "async" property.
+ */
+#if 0
+static const struct ipu_channels async_dual_plane = {
+	.dma = { IPUV3_CHANNEL_MEM_BG_ASYNC, IPUV3_CHANNEL_MEM_FG_ASYNC },
+	.dp  = { IPU_DP_FLOW_ASYNC0_BG, IPU_DP_FLOW_ASYNC0_FG },
+	.dc  = IPU_DC_CHANNEL_DP_ASYNC,
+};
+static const struct ipu_channels async_single_plane = {
+	.dma = { IPUV3_CHANNEL_MEM_DC_ASYNC, },
+	.dp  = { NO_DP, NO_DP },
+	.dc    = IPU_DC_CHANNEL_ASYNC,
+};
+#endif
+
 struct ipu_crtc {
 	struct device		*dev;
 	struct drm_crtc		base;
 	struct imx_drm_crtc	*imx_crtc;
+	struct device           *ipu_dev; /* our ipu */
+	struct ipu_soc          *ipu;
+	struct device_node      *port;    /* our port */
+
+	const struct ipu_channels *ch;
 
 	/* plane[0] is the full plane, plane[1] is the partial plane */
 	struct ipu_plane	*plane[2];
@@ -311,32 +353,106 @@  static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = {
 	.crtc_helper_funcs = &ipu_helper_funcs,
 };
 
+static int of_dev_node_match(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int get_ipu(struct ipu_crtc *ipu_crtc, struct device_node *node)
+{
+	struct device_node *ipu_node;
+	struct device *ipu_dev;
+	int ret;
+
+	ipu_node = of_parse_phandle(node, "ipu", 0);
+	if (!ipu_node) {
+		dev_err(ipu_crtc->dev, "missing ipu phandle!\n");
+		return -ENODEV;
+	}
+
+	ipu_dev = bus_find_device(&platform_bus_type, NULL,
+				  ipu_node, of_dev_node_match);
+	of_node_put(ipu_node);
+
+	if (!ipu_dev) {
+		dev_err(ipu_crtc->dev, "failed to find ipu device!\n");
+		return -ENODEV;
+	}
+
+	device_lock(ipu_dev);
+
+	if (!ipu_dev->driver || !try_module_get(ipu_dev->driver->owner)) {
+		ret = -EPROBE_DEFER;
+		dev_warn(ipu_crtc->dev, "IPU driver not loaded\n");
+		device_unlock(ipu_dev);
+		goto dev_put;
+	}
+
+	ipu_crtc->ipu_dev = ipu_dev;
+	ipu_crtc->ipu = dev_get_drvdata(ipu_dev);
+
+	device_unlock(ipu_dev);
+	return 0;
+dev_put:
+	put_device(ipu_dev);
+	return ret;
+}
+
 static void ipu_put_resources(struct ipu_crtc *ipu_crtc)
 {
 	if (!IS_ERR_OR_NULL(ipu_crtc->dc))
 		ipu_dc_put(ipu_crtc->dc);
 	if (!IS_ERR_OR_NULL(ipu_crtc->di))
 		ipu_di_put(ipu_crtc->di);
+	if (!IS_ERR_OR_NULL(ipu_crtc->ipu_dev)) {
+		module_put(ipu_crtc->ipu_dev->driver->owner);
+		put_device(ipu_crtc->ipu_dev);
+	}
 }
 
 static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
-		struct ipu_client_platformdata *pdata)
+			     struct device_node *np)
 {
-	struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
+	u32 di;
 	int ret;
 
-	ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc);
-	if (IS_ERR(ipu_crtc->dc)) {
-		ret = PTR_ERR(ipu_crtc->dc);
-		goto err_out;
+	ret = get_ipu(ipu_crtc, np);
+	if (ret) {
+		dev_warn(ipu_crtc->dev, "could not get ipu\n");
+		return ret;
+	}
+
+	/* get our port */
+	ipu_crtc->port = of_get_child_by_name(np, "port");
+	if (!ipu_crtc->port) {
+		dev_err(ipu_crtc->dev, "could not get port\n");
+		return -ENODEV;
 	}
 
-	ipu_crtc->di = ipu_di_get(ipu, pdata->di);
+	ret = of_property_read_u32(np, "di", &di);
+	if (ret < 0)
+		goto err_out;
+
+	ipu_crtc->di = ipu_di_get(ipu_crtc->ipu, di);
 	if (IS_ERR(ipu_crtc->di)) {
 		ret = PTR_ERR(ipu_crtc->di);
 		goto err_out;
 	}
 
+	if (!of_find_property(np, "dual-plane", NULL)) {
+		dev_info(ipu_crtc->dev, "single plane mode\n");
+		ipu_crtc->ch = &sync_single_plane;
+	} else {
+		dev_info(ipu_crtc->dev, "dual plane mode\n");
+		ipu_crtc->ch = &sync_dual_plane;
+	}
+
+	ipu_crtc->dc = ipu_dc_get(ipu_crtc->ipu, ipu_crtc->ch->dc);
+	if (IS_ERR(ipu_crtc->dc)) {
+		ret = PTR_ERR(ipu_crtc->dc);
+		goto err_out;
+	}
+
 	return 0;
 err_out:
 	ipu_put_resources(ipu_crtc);
@@ -345,14 +461,13 @@  err_out:
 }
 
 static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
-	struct ipu_client_platformdata *pdata, struct drm_device *drm)
+			 struct drm_device *drm)
 {
-	struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
-	int dp = -EINVAL;
+	struct device_node *np = ipu_crtc->dev->of_node;
 	int ret;
 	int id;
 
-	ret = ipu_get_resources(ipu_crtc, pdata);
+	ret = ipu_get_resources(ipu_crtc, np);
 	if (ret) {
 		dev_err(ipu_crtc->dev, "getting resources failed with %d.\n",
 				ret);
@@ -360,17 +475,18 @@  static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
 	}
 
 	ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc,
-			&ipu_crtc_helper_funcs, ipu_crtc->dev->of_node);
+			       &ipu_crtc_helper_funcs, ipu_crtc->port);
 	if (ret) {
 		dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret);
 		goto err_put_resources;
 	}
 
-	if (pdata->dp >= 0)
-		dp = IPU_DP_FLOW_SYNC_BG;
 	id = imx_drm_crtc_id(ipu_crtc->imx_crtc);
-	ipu_crtc->plane[0] = ipu_plane_init(ipu_crtc->base.dev, ipu,
-					    pdata->dma[0], dp, BIT(id), true);
+	ipu_crtc->plane[0] = ipu_plane_init(ipu_crtc->base.dev,
+					    ipu_crtc->ipu,
+					    ipu_crtc->ch->dma[0],
+					    ipu_crtc->ch->dp[0],
+					    BIT(id), true);
 	ret = ipu_plane_get_resources(ipu_crtc->plane[0]);
 	if (ret) {
 		dev_err(ipu_crtc->dev, "getting plane 0 resources failed with %d.\n",
@@ -379,10 +495,11 @@  static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
 	}
 
 	/* If this crtc is using the DP, add an overlay plane */
-	if (pdata->dp >= 0 && pdata->dma[1] > 0) {
-		ipu_crtc->plane[1] = ipu_plane_init(ipu_crtc->base.dev, ipu,
-						    pdata->dma[1],
-						    IPU_DP_FLOW_SYNC_FG,
+	if (ipu_crtc->ch->dp[1] >= 0) {
+		ipu_crtc->plane[1] = ipu_plane_init(ipu_crtc->base.dev,
+						    ipu_crtc->ipu,
+						    ipu_crtc->ch->dma[1],
+						    ipu_crtc->ch->dp[1],
 						    BIT(id), false);
 		if (IS_ERR(ipu_crtc->plane[1]))
 			ipu_crtc->plane[1] = NULL;
@@ -408,31 +525,8 @@  err_put_resources:
 	return ret;
 }
 
-static struct device_node *ipu_drm_get_port_by_id(struct device_node *parent,
-						  int port_id)
-{
-	struct device_node *port;
-	int id, ret;
-
-	port = of_get_child_by_name(parent, "port");
-	while (port) {
-		ret = of_property_read_u32(port, "reg", &id);
-		if (!ret && id == port_id)
-			return port;
-
-		do {
-			port = of_get_next_child(parent, port);
-			if (!port)
-				return NULL;
-		} while (of_node_cmp(port->name, "port"));
-	}
-
-	return NULL;
-}
-
 static int ipu_drm_bind(struct device *dev, struct device *master, void *data)
 {
-	struct ipu_client_platformdata *pdata = dev->platform_data;
 	struct drm_device *drm = data;
 	struct ipu_crtc *ipu_crtc;
 	int ret;
@@ -443,7 +537,7 @@  static int ipu_drm_bind(struct device *dev, struct device *master, void *data)
 
 	ipu_crtc->dev = dev;
 
-	ret = ipu_crtc_init(ipu_crtc, pdata, drm);
+	ret = ipu_crtc_init(ipu_crtc, drm);
 	if (ret)
 		return ret;
 
@@ -471,23 +565,8 @@  static const struct component_ops ipu_crtc_ops = {
 static int ipu_drm_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct ipu_client_platformdata *pdata = dev->platform_data;
 	int ret;
 
-	if (!dev->platform_data)
-		return -EINVAL;
-
-	if (!dev->of_node) {
-		/* Associate crtc device with the corresponding DI port node */
-		dev->of_node = ipu_drm_get_port_by_id(dev->parent->of_node,
-						      pdata->di + 2);
-		if (!dev->of_node) {
-			dev_err(dev, "missing port@%d node in %s\n",
-				pdata->di + 2, dev->parent->of_node->full_name);
-			return -ENODEV;
-		}
-	}
-
 	ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
 	if (ret)
 		return ret;
@@ -501,9 +580,16 @@  static int ipu_drm_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static struct of_device_id ipu_drm_dt_ids[] = {
+	{ .compatible = "fsl,imx-ipuv3-crtc" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ipu_drm_dt_ids);
+
 static struct platform_driver ipu_drm_driver = {
 	.driver = {
 		.name = "imx-ipuv3-crtc",
+		.of_match_table = ipu_drm_dt_ids,
 	},
 	.probe = ipu_drm_probe,
 	.remove = ipu_drm_remove,