summaryrefslogtreecommitdiffstats
path: root/drivers/staging/imx-drm
diff options
context:
space:
mode:
authorGreg Kroah-Hartman2014-03-07 23:23:34 +0100
committerGreg Kroah-Hartman2014-03-07 23:23:34 +0100
commitbfe24b9cbefc33efdb9bd10132037e8e4840e616 (patch)
tree6336e895c75079bd78a2f1c242c7fb451d6584b7 /drivers/staging/imx-drm
parentStaging: octeon: Fix coding style (diff)
parentstaging: imx-drm: Update TODO (diff)
downloadkernel-qcow2-linux-bfe24b9cbefc33efdb9bd10132037e8e4840e616.tar.gz
kernel-qcow2-linux-bfe24b9cbefc33efdb9bd10132037e8e4840e616.tar.xz
kernel-qcow2-linux-bfe24b9cbefc33efdb9bd10132037e8e4840e616.zip
Merge branch 'imx-drm-staging' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into staging-next
Russell writes: This set of changes reorganises imx-drm's DT bindings by re-using the OF graph parsing code which was located in drivers/media, removing the temporary bindings. The result is that more TODO entries are now removed. While we're not quite done with this yet as there's a few straggling updates to imx-ldb to come, but leaving these out is not detrimental at this point in time - they are more an enhancement. However, this pull has the additional complication that we're sharing seven commits with Mauro's V4L git tree, which move the OF graph parsing code out of drivers/media into drivers/of. Philipp's imx-drm changes depend on these and my previously committed round of imx-drm commits. Hence, the diffstat below is from a test merge with your tree head (17b02809cfa7). Mauro merged those seven commits earlier today as a git pull, so both trees will be sharing exactly the same commit IDs. I've given these changes a spin here on both my Hummingboard and Cubox-i4 (one is iMX6Solo, the other is iMX6Quad based), which includes Xorg using the DRM device directly, and I find nothing wrong. The diffstat does look a little scarey - this is because we're having to update the ARM DT files along with this change, and obviously the dependency on the OF graph parsing code.
Diffstat (limited to 'drivers/staging/imx-drm')
-rw-r--r--drivers/staging/imx-drm/TODO5
-rw-r--r--drivers/staging/imx-drm/imx-drm-core.c217
-rw-r--r--drivers/staging/imx-drm/imx-drm.h5
-rw-r--r--drivers/staging/imx-drm/imx-hdmi.c4
-rw-r--r--drivers/staging/imx-drm/imx-ldb.c4
-rw-r--r--drivers/staging/imx-drm/imx-tve.c2
-rw-r--r--drivers/staging/imx-drm/ipuv3-crtc.c47
7 files changed, 201 insertions, 83 deletions
diff --git a/drivers/staging/imx-drm/TODO b/drivers/staging/imx-drm/TODO
index 6a9da94c9573..29636fb13959 100644
--- a/drivers/staging/imx-drm/TODO
+++ b/drivers/staging/imx-drm/TODO
@@ -1,15 +1,10 @@
TODO:
- get DRM Maintainer review for this code
-- Wait for common display framework to hit mainline and update the IPU
- driver to use it. This will most probably make changes to the devicetree
- bindings necessary.
-- Factor out more code to common helper functions
- decide where to put the base driver. It is not specific to a subsystem
and would be used by DRM/KMS and media/V4L2
Missing features (not necessarily for moving out of staging):
-- Add i.MX6 HDMI support
- Add support for IC (Image converter)
- Add support for CSI (CMOS Sensor interface)
- Add support for VDIC (Video Deinterlacer)
diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c
index 6b91c8e67174..4144a75e5f71 100644
--- a/drivers/staging/imx-drm/imx-drm-core.c
+++ b/drivers/staging/imx-drm/imx-drm-core.c
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/fb.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <drm/drmP.h>
#include <drm/drm_fb_helper.h>
@@ -30,6 +31,11 @@
struct imx_drm_crtc;
+struct imx_drm_component {
+ struct device_node *of_node;
+ struct list_head list;
+};
+
struct imx_drm_device {
struct drm_device *drm;
struct imx_drm_crtc *crtc[MAX_CRTC];
@@ -41,9 +47,7 @@ struct imx_drm_crtc {
struct drm_crtc *crtc;
int pipe;
struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs;
- void *cookie;
- int id;
- int mux_id;
+ struct device_node *port;
};
static int legacyfb_depth = 16;
@@ -341,14 +345,11 @@ err_kms:
/*
* imx_drm_add_crtc - add a new crtc
- *
- * The return value if !NULL is a cookie for the caller to pass to
- * imx_drm_remove_crtc later.
*/
int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
struct imx_drm_crtc **new_crtc,
const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs,
- void *cookie, int id)
+ struct device_node *port)
{
struct imx_drm_device *imxdrm = drm->dev_private;
struct imx_drm_crtc *imx_drm_crtc;
@@ -370,9 +371,7 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs;
imx_drm_crtc->pipe = imxdrm->pipes++;
- imx_drm_crtc->cookie = cookie;
- imx_drm_crtc->id = id;
- imx_drm_crtc->mux_id = imx_drm_crtc->pipe;
+ imx_drm_crtc->port = port;
imx_drm_crtc->crtc = crtc;
imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc;
@@ -416,49 +415,56 @@ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc)
EXPORT_SYMBOL_GPL(imx_drm_remove_crtc);
/*
- * Find the DRM CRTC possible mask for the device node cookie/id.
+ * Find the DRM CRTC possible mask for the connected endpoint.
*
* The encoder possible masks are defined by their position in the
* mode_config crtc_list. This means that CRTCs must not be added
* or removed once the DRM device has been fully initialised.
*/
static uint32_t imx_drm_find_crtc_mask(struct imx_drm_device *imxdrm,
- void *cookie, int id)
+ struct device_node *endpoint)
{
+ struct device_node *port;
unsigned i;
+ port = of_graph_get_remote_port(endpoint);
+ if (!port)
+ return 0;
+ of_node_put(port);
+
for (i = 0; i < MAX_CRTC; i++) {
struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i];
- if (imx_drm_crtc && imx_drm_crtc->id == id &&
- imx_drm_crtc->cookie == cookie)
+ if (imx_drm_crtc && imx_drm_crtc->port == port)
return drm_crtc_mask(imx_drm_crtc->crtc);
}
return 0;
}
+static struct device_node *imx_drm_of_get_next_endpoint(
+ const struct device_node *parent, struct device_node *prev)
+{
+ struct device_node *node = of_graph_get_next_endpoint(parent, prev);
+ of_node_put(prev);
+ return node;
+}
+
int imx_drm_encoder_parse_of(struct drm_device *drm,
struct drm_encoder *encoder, struct device_node *np)
{
struct imx_drm_device *imxdrm = drm->dev_private;
+ struct device_node *ep = NULL;
uint32_t crtc_mask = 0;
- int i, ret = 0;
+ int i;
- for (i = 0; !ret; i++) {
- struct of_phandle_args args;
- uint32_t mask;
- int id;
+ for (i = 0; ; i++) {
+ u32 mask;
- ret = of_parse_phandle_with_args(np, "crtcs", "#crtc-cells", i,
- &args);
- if (ret == -ENOENT)
+ ep = imx_drm_of_get_next_endpoint(np, ep);
+ if (!ep)
break;
- if (ret < 0)
- return ret;
- id = args.args_count > 0 ? args.args[0] : 0;
- mask = imx_drm_find_crtc_mask(imxdrm, args.np, id);
- of_node_put(args.np);
+ mask = imx_drm_find_crtc_mask(imxdrm, ep);
/*
* If we failed to find the CRTC(s) which this encoder is
@@ -472,6 +478,11 @@ int imx_drm_encoder_parse_of(struct drm_device *drm,
crtc_mask |= mask;
}
+ if (ep)
+ of_node_put(ep);
+ if (i == 0)
+ return -ENOENT;
+
encoder->possible_crtcs = crtc_mask;
/* FIXME: this is the mask of outputs which can clone this output. */
@@ -481,11 +492,36 @@ int imx_drm_encoder_parse_of(struct drm_device *drm,
}
EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of);
-int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder)
+/*
+ * @node: device tree node containing encoder input ports
+ * @encoder: drm_encoder
+ */
+int imx_drm_encoder_get_mux_id(struct device_node *node,
+ struct drm_encoder *encoder)
{
struct imx_drm_crtc *imx_crtc = imx_drm_find_crtc(encoder->crtc);
+ struct device_node *ep = NULL;
+ struct of_endpoint endpoint;
+ struct device_node *port;
+ int ret;
- return imx_crtc ? imx_crtc->mux_id : -EINVAL;
+ if (!node || !imx_crtc)
+ return -EINVAL;
+
+ do {
+ ep = imx_drm_of_get_next_endpoint(node, ep);
+ if (!ep)
+ break;
+
+ port = of_graph_get_remote_port(ep);
+ of_node_put(port);
+ if (port == imx_crtc->port) {
+ ret = of_graph_parse_endpoint(ep, &endpoint);
+ return ret ? ret : endpoint.id;
+ }
+ } while (ep);
+
+ return -EINVAL;
}
EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id);
@@ -528,48 +564,29 @@ static struct drm_driver imx_drm_driver = {
.patchlevel = 0,
};
-static int compare_parent_of(struct device *dev, void *data)
-{
- struct of_phandle_args *args = data;
- return dev->parent && dev->parent->of_node == args->np;
-}
-
static int compare_of(struct device *dev, void *data)
{
- return dev->of_node == data;
-}
-
-static int imx_drm_add_components(struct device *master, struct master *m)
-{
- struct device_node *np = master->of_node;
- unsigned i;
- int ret;
+ struct device_node *np = data;
- for (i = 0; ; i++) {
- struct of_phandle_args args;
-
- ret = of_parse_phandle_with_fixed_args(np, "crtcs", 1,
- i, &args);
- if (ret)
- break;
-
- ret = component_master_add_child(m, compare_parent_of, &args);
- of_node_put(args.np);
-
- if (ret)
- return ret;
+ /* Special case for LDB, one device for two channels */
+ if (of_node_cmp(np->name, "lvds-channel") == 0) {
+ np = of_get_parent(np);
+ of_node_put(np);
}
- for (i = 0; ; i++) {
- struct device_node *node;
+ return dev->of_node == np;
+}
- node = of_parse_phandle(np, "connectors", i);
- if (!node)
- break;
+static LIST_HEAD(imx_drm_components);
- ret = component_master_add_child(m, compare_of, node);
- of_node_put(node);
+static int imx_drm_add_components(struct device *master, struct master *m)
+{
+ struct imx_drm_component *component;
+ int ret;
+ list_for_each_entry(component, &imx_drm_components, list) {
+ ret = component_master_add_child(m, compare_of,
+ component->of_node);
if (ret)
return ret;
}
@@ -592,9 +609,81 @@ static const struct component_master_ops imx_drm_ops = {
.unbind = imx_drm_unbind,
};
+static struct imx_drm_component *imx_drm_find_component(struct device *dev,
+ struct device_node *node)
+{
+ struct imx_drm_component *component;
+
+ list_for_each_entry(component, &imx_drm_components, list)
+ if (component->of_node == node)
+ return component;
+
+ return NULL;
+}
+
+static int imx_drm_add_component(struct device *dev, struct device_node *node)
+{
+ struct imx_drm_component *component;
+
+ if (imx_drm_find_component(dev, node))
+ return 0;
+
+ component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL);
+ if (!component)
+ return -ENOMEM;
+
+ component->of_node = node;
+ list_add_tail(&component->list, &imx_drm_components);
+
+ return 0;
+}
+
static int imx_drm_platform_probe(struct platform_device *pdev)
{
+ struct device_node *ep, *port, *remote;
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.
+ */
+ for (i = 0; ; i++) {
+ port = of_parse_phandle(pdev->dev.of_node, "ports", i);
+ if (!port)
+ break;
+
+ ret = imx_drm_add_component(&pdev->dev, port);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (i == 0) {
+ dev_err(&pdev->dev, "missing 'ports' property\n");
+ return -ENODEV;
+ }
+
+ /* Then bind all encoders */
+ for (i = 0; ; i++) {
+ port = of_parse_phandle(pdev->dev.of_node, "ports", i);
+ if (!port)
+ break;
+
+ 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;
+ }
+
+ ret = imx_drm_add_component(&pdev->dev, remote);
+ of_node_put(remote);
+ if (ret < 0)
+ return ret;
+ }
+ of_node_put(port);
+ }
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
@@ -610,7 +699,7 @@ static int imx_drm_platform_remove(struct platform_device *pdev)
}
static const struct of_device_id imx_drm_dt_ids[] = {
- { .compatible = "fsl,imx-drm", },
+ { .compatible = "fsl,imx-display-subsystem", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, imx_drm_dt_ids);
diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h
index 035ab6258ad0..a322bac55414 100644
--- a/drivers/staging/imx-drm/imx-drm.h
+++ b/drivers/staging/imx-drm/imx-drm.h
@@ -26,7 +26,7 @@ struct imx_drm_crtc_helper_funcs {
int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
struct imx_drm_crtc **new_crtc,
const struct imx_drm_crtc_helper_funcs *imx_helper_funcs,
- void *cookie, int id);
+ struct device_node *port);
int imx_drm_remove_crtc(struct imx_drm_crtc *);
int imx_drm_init_drm(struct platform_device *pdev,
int preferred_bpp);
@@ -45,7 +45,8 @@ int imx_drm_panel_format_pins(struct drm_encoder *encoder,
int imx_drm_panel_format(struct drm_encoder *encoder,
u32 interface_pix_fmt);
-int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder);
+int imx_drm_encoder_get_mux_id(struct device_node *node,
+ struct drm_encoder *encoder);
int imx_drm_encoder_parse_of(struct drm_device *drm,
struct drm_encoder *encoder, struct device_node *np);
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index 8384ceab932a..4540a9aa11d8 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -1459,7 +1459,7 @@ static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder)
static void imx_hdmi_encoder_commit(struct drm_encoder *encoder)
{
struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
- int mux = imx_drm_encoder_get_mux_id(encoder);
+ int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
imx_hdmi_set_ipu_di_mux(hdmi, mux);
@@ -1610,7 +1610,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
hdmi->dev_type = device_id->driver_data;
}
- ddc_node = of_parse_phandle(np, "ddc", 0);
+ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
if (ddc_node) {
hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
if (!hdmi->ddc)
diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c
index daa54df4527f..33d2b8832540 100644
--- a/drivers/staging/imx-drm/imx-ldb.c
+++ b/drivers/staging/imx-drm/imx-ldb.c
@@ -170,7 +170,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder)
u32 pixel_fmt;
unsigned long serial_clk;
unsigned long di_clk = mode->clock * 1000;
- int mux = imx_drm_encoder_get_mux_id(encoder);
+ int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder);
if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
/* dual channel LVDS mode */
@@ -205,7 +205,7 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
struct imx_ldb *ldb = imx_ldb_ch->ldb;
int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
- int mux = imx_drm_encoder_get_mux_id(encoder);
+ int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder);
if (dual) {
clk_prepare_enable(ldb->clk[0]);
diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c
index 50b25f1a85d5..575533f4fd64 100644
--- a/drivers/staging/imx-drm/imx-tve.c
+++ b/drivers/staging/imx-drm/imx-tve.c
@@ -582,7 +582,7 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
tve->dev = dev;
spin_lock_init(&tve->lock);
- ddc_node = of_parse_phandle(np, "ddc", 0);
+ ddc_node = of_parse_phandle(np, "i2c-ddc-bus", 0);
if (ddc_node) {
tve->ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c
index e646017c32ac..a8d017854615 100644
--- a/drivers/staging/imx-drm/ipuv3-crtc.c
+++ b/drivers/staging/imx-drm/ipuv3-crtc.c
@@ -350,10 +350,8 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
return ret;
}
- ret = imx_drm_add_crtc(drm, &ipu_crtc->base,
- &ipu_crtc->imx_crtc,
- &ipu_crtc_helper_funcs,
- ipu_crtc->dev->parent->of_node, pdata->di);
+ ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc,
+ &ipu_crtc_helper_funcs, ipu_crtc->dev->of_node);
if (ret) {
dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret);
goto err_put_resources;
@@ -401,6 +399,28 @@ 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;
@@ -441,16 +461,29 @@ 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 (!pdev->dev.platform_data)
+ if (!dev->platform_data)
return -EINVAL;
- ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+ 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;
- return component_add(&pdev->dev, &ipu_crtc_ops);
+ return component_add(dev, &ipu_crtc_ops);
}
static int ipu_drm_remove(struct platform_device *pdev)