@@ -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));
@@ -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,
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(-)