diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index dcba51853e8a..e4277f996574 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -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 device_node *port; + int id, 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_property_read_u32(ep->parent, "reg", &id); + of_node_put(ep); + return ret ? ret : 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; + struct device_node *np = data; + + /* 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); + } + + return dev->of_node == np; } +static LIST_HEAD(imx_drm_components); + static int imx_drm_add_components(struct device *master, struct master *m) { - struct device_node *np = master->of_node; - unsigned i; + struct imx_drm_component *component; int ret; - 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; - } - - for (i = 0; ; i++) { - struct device_node *node; - - node = of_parse_phandle(np, "connectors", i); - if (!node) - break; - - ret = component_master_add_child(m, compare_of, node); - of_node_put(node); - + 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 aa2102866689..a24d46ad3dce 100644 --- a/drivers/staging/imx-drm/imx-drm.h +++ b/drivers/staging/imx-drm/imx-drm.h @@ -30,7 +30,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); @@ -49,7 +49,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 ab16aba7788d..070f0161a12a 100644 --- a/drivers/staging/imx-drm/imx-hdmi.c +++ b/drivers/staging/imx-drm/imx-hdmi.c @@ -1454,7 +1454,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); diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c index 5168c76cff53..301c4300aa55 100644 --- a/drivers/staging/imx-drm/imx-ldb.c +++ b/drivers/staging/imx-drm/imx-ldb.c @@ -168,7 +168,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 */ @@ -203,7 +203,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/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)