Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 
0003 /*
0004  * Copyright 2020 NXP
0005  */
0006 
0007 #include <linux/firmware/imx/svc/misc.h>
0008 #include <linux/media-bus-format.h>
0009 #include <linux/mfd/syscon.h>
0010 #include <linux/module.h>
0011 #include <linux/of.h>
0012 #include <linux/of_device.h>
0013 #include <linux/of_graph.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/pm_runtime.h>
0016 #include <linux/regmap.h>
0017 
0018 #include <drm/drm_atomic_state_helper.h>
0019 #include <drm/drm_bridge.h>
0020 #include <drm/drm_of.h>
0021 #include <drm/drm_print.h>
0022 
0023 #include <dt-bindings/firmware/imx/rsrc.h>
0024 
0025 #define PXL2DPI_CTRL    0x40
0026 #define  CFG1_16BIT 0x0
0027 #define  CFG2_16BIT 0x1
0028 #define  CFG3_16BIT 0x2
0029 #define  CFG1_18BIT 0x3
0030 #define  CFG2_18BIT 0x4
0031 #define  CFG_24BIT  0x5
0032 
0033 #define DRIVER_NAME "imx8qxp-pxl2dpi"
0034 
0035 struct imx8qxp_pxl2dpi {
0036     struct regmap *regmap;
0037     struct drm_bridge bridge;
0038     struct drm_bridge *next_bridge;
0039     struct drm_bridge *companion;
0040     struct device *dev;
0041     struct imx_sc_ipc *ipc_handle;
0042     u32 sc_resource;
0043     u32 in_bus_format;
0044     u32 out_bus_format;
0045     u32 pl_sel;
0046 };
0047 
0048 #define bridge_to_p2d(b)    container_of(b, struct imx8qxp_pxl2dpi, bridge)
0049 
0050 static int imx8qxp_pxl2dpi_bridge_attach(struct drm_bridge *bridge,
0051                      enum drm_bridge_attach_flags flags)
0052 {
0053     struct imx8qxp_pxl2dpi *p2d = bridge->driver_private;
0054 
0055     if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
0056         DRM_DEV_ERROR(p2d->dev,
0057                   "do not support creating a drm_connector\n");
0058         return -EINVAL;
0059     }
0060 
0061     if (!bridge->encoder) {
0062         DRM_DEV_ERROR(p2d->dev, "missing encoder\n");
0063         return -ENODEV;
0064     }
0065 
0066     return drm_bridge_attach(bridge->encoder,
0067                  p2d->next_bridge, bridge,
0068                  DRM_BRIDGE_ATTACH_NO_CONNECTOR);
0069 }
0070 
0071 static int
0072 imx8qxp_pxl2dpi_bridge_atomic_check(struct drm_bridge *bridge,
0073                     struct drm_bridge_state *bridge_state,
0074                     struct drm_crtc_state *crtc_state,
0075                     struct drm_connector_state *conn_state)
0076 {
0077     struct imx8qxp_pxl2dpi *p2d = bridge->driver_private;
0078 
0079     p2d->in_bus_format = bridge_state->input_bus_cfg.format;
0080     p2d->out_bus_format = bridge_state->output_bus_cfg.format;
0081 
0082     return 0;
0083 }
0084 
0085 static void
0086 imx8qxp_pxl2dpi_bridge_mode_set(struct drm_bridge *bridge,
0087                 const struct drm_display_mode *mode,
0088                 const struct drm_display_mode *adjusted_mode)
0089 {
0090     struct imx8qxp_pxl2dpi *p2d = bridge->driver_private;
0091     struct imx8qxp_pxl2dpi *companion_p2d;
0092     int ret;
0093 
0094     ret = pm_runtime_get_sync(p2d->dev);
0095     if (ret < 0)
0096         DRM_DEV_ERROR(p2d->dev,
0097                   "failed to get runtime PM sync: %d\n", ret);
0098 
0099     ret = imx_sc_misc_set_control(p2d->ipc_handle, p2d->sc_resource,
0100                       IMX_SC_C_PXL_LINK_SEL, p2d->pl_sel);
0101     if (ret)
0102         DRM_DEV_ERROR(p2d->dev,
0103                   "failed to set pixel link selection(%u): %d\n",
0104                             p2d->pl_sel, ret);
0105 
0106     switch (p2d->out_bus_format) {
0107     case MEDIA_BUS_FMT_RGB888_1X24:
0108         regmap_write(p2d->regmap, PXL2DPI_CTRL, CFG_24BIT);
0109         break;
0110     case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
0111         regmap_write(p2d->regmap, PXL2DPI_CTRL, CFG2_18BIT);
0112         break;
0113     default:
0114         DRM_DEV_ERROR(p2d->dev,
0115                   "unsupported output bus format 0x%08x\n",
0116                             p2d->out_bus_format);
0117     }
0118 
0119     if (p2d->companion) {
0120         companion_p2d = bridge_to_p2d(p2d->companion);
0121 
0122         companion_p2d->in_bus_format = p2d->in_bus_format;
0123         companion_p2d->out_bus_format = p2d->out_bus_format;
0124 
0125         p2d->companion->funcs->mode_set(p2d->companion, mode,
0126                             adjusted_mode);
0127     }
0128 }
0129 
0130 static void
0131 imx8qxp_pxl2dpi_bridge_atomic_disable(struct drm_bridge *bridge,
0132                       struct drm_bridge_state *old_bridge_state)
0133 {
0134     struct imx8qxp_pxl2dpi *p2d = bridge->driver_private;
0135     int ret;
0136 
0137     ret = pm_runtime_put(p2d->dev);
0138     if (ret < 0)
0139         DRM_DEV_ERROR(p2d->dev, "failed to put runtime PM: %d\n", ret);
0140 
0141     if (p2d->companion)
0142         p2d->companion->funcs->atomic_disable(p2d->companion,
0143                             old_bridge_state);
0144 }
0145 
0146 static const u32 imx8qxp_pxl2dpi_bus_output_fmts[] = {
0147     MEDIA_BUS_FMT_RGB888_1X24,
0148     MEDIA_BUS_FMT_RGB666_1X24_CPADHI,
0149 };
0150 
0151 static bool imx8qxp_pxl2dpi_bus_output_fmt_supported(u32 fmt)
0152 {
0153     int i;
0154 
0155     for (i = 0; i < ARRAY_SIZE(imx8qxp_pxl2dpi_bus_output_fmts); i++) {
0156         if (imx8qxp_pxl2dpi_bus_output_fmts[i] == fmt)
0157             return true;
0158     }
0159 
0160     return false;
0161 }
0162 
0163 static u32 *
0164 imx8qxp_pxl2dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
0165                          struct drm_bridge_state *bridge_state,
0166                          struct drm_crtc_state *crtc_state,
0167                          struct drm_connector_state *conn_state,
0168                          u32 output_fmt,
0169                          unsigned int *num_input_fmts)
0170 {
0171     u32 *input_fmts;
0172 
0173     if (!imx8qxp_pxl2dpi_bus_output_fmt_supported(output_fmt))
0174         return NULL;
0175 
0176     *num_input_fmts = 1;
0177 
0178     input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
0179     if (!input_fmts)
0180         return NULL;
0181 
0182     switch (output_fmt) {
0183     case MEDIA_BUS_FMT_RGB888_1X24:
0184         input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X36_CPADLO;
0185         break;
0186     case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
0187         input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X36_CPADLO;
0188         break;
0189     default:
0190         kfree(input_fmts);
0191         input_fmts = NULL;
0192         break;
0193     }
0194 
0195     return input_fmts;
0196 }
0197 
0198 static u32 *
0199 imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
0200                           struct drm_bridge_state *bridge_state,
0201                           struct drm_crtc_state *crtc_state,
0202                           struct drm_connector_state *conn_state,
0203                           unsigned int *num_output_fmts)
0204 {
0205     *num_output_fmts = ARRAY_SIZE(imx8qxp_pxl2dpi_bus_output_fmts);
0206     return kmemdup(imx8qxp_pxl2dpi_bus_output_fmts,
0207             sizeof(imx8qxp_pxl2dpi_bus_output_fmts), GFP_KERNEL);
0208 }
0209 
0210 static const struct drm_bridge_funcs imx8qxp_pxl2dpi_bridge_funcs = {
0211     .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
0212     .atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
0213     .atomic_reset       = drm_atomic_helper_bridge_reset,
0214     .attach         = imx8qxp_pxl2dpi_bridge_attach,
0215     .atomic_check       = imx8qxp_pxl2dpi_bridge_atomic_check,
0216     .mode_set       = imx8qxp_pxl2dpi_bridge_mode_set,
0217     .atomic_disable     = imx8qxp_pxl2dpi_bridge_atomic_disable,
0218     .atomic_get_input_bus_fmts =
0219             imx8qxp_pxl2dpi_bridge_atomic_get_input_bus_fmts,
0220     .atomic_get_output_bus_fmts =
0221             imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts,
0222 };
0223 
0224 static struct device_node *
0225 imx8qxp_pxl2dpi_get_available_ep_from_port(struct imx8qxp_pxl2dpi *p2d,
0226                        u32 port_id)
0227 {
0228     struct device_node *port, *ep;
0229     int ep_cnt;
0230 
0231     port = of_graph_get_port_by_id(p2d->dev->of_node, port_id);
0232     if (!port) {
0233         DRM_DEV_ERROR(p2d->dev, "failed to get port@%u\n", port_id);
0234         return ERR_PTR(-ENODEV);
0235     }
0236 
0237     ep_cnt = of_get_available_child_count(port);
0238     if (ep_cnt == 0) {
0239         DRM_DEV_ERROR(p2d->dev, "no available endpoints of port@%u\n",
0240                   port_id);
0241         ep = ERR_PTR(-ENODEV);
0242         goto out;
0243     } else if (ep_cnt > 1) {
0244         DRM_DEV_ERROR(p2d->dev,
0245                   "invalid available endpoints of port@%u\n",
0246                   port_id);
0247         ep = ERR_PTR(-EINVAL);
0248         goto out;
0249     }
0250 
0251     ep = of_get_next_available_child(port, NULL);
0252     if (!ep) {
0253         DRM_DEV_ERROR(p2d->dev,
0254                   "failed to get available endpoint of port@%u\n",
0255                   port_id);
0256         ep = ERR_PTR(-ENODEV);
0257         goto out;
0258     }
0259 out:
0260     of_node_put(port);
0261     return ep;
0262 }
0263 
0264 static struct drm_bridge *
0265 imx8qxp_pxl2dpi_find_next_bridge(struct imx8qxp_pxl2dpi *p2d)
0266 {
0267     struct device_node *ep, *remote;
0268     struct drm_bridge *next_bridge;
0269     int ret;
0270 
0271     ep = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1);
0272     if (IS_ERR(ep)) {
0273         ret = PTR_ERR(ep);
0274         return ERR_PTR(ret);
0275     }
0276 
0277     remote = of_graph_get_remote_port_parent(ep);
0278     if (!remote || !of_device_is_available(remote)) {
0279         DRM_DEV_ERROR(p2d->dev, "no available remote\n");
0280         next_bridge = ERR_PTR(-ENODEV);
0281         goto out;
0282     } else if (!of_device_is_available(remote->parent)) {
0283         DRM_DEV_ERROR(p2d->dev, "remote parent is not available\n");
0284         next_bridge = ERR_PTR(-ENODEV);
0285         goto out;
0286     }
0287 
0288     next_bridge = of_drm_find_bridge(remote);
0289     if (!next_bridge) {
0290         next_bridge = ERR_PTR(-EPROBE_DEFER);
0291         goto out;
0292     }
0293 out:
0294     of_node_put(remote);
0295     of_node_put(ep);
0296 
0297     return next_bridge;
0298 }
0299 
0300 static int imx8qxp_pxl2dpi_set_pixel_link_sel(struct imx8qxp_pxl2dpi *p2d)
0301 {
0302     struct device_node *ep;
0303     struct of_endpoint endpoint;
0304     int ret;
0305 
0306     ep = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 0);
0307     if (IS_ERR(ep))
0308         return PTR_ERR(ep);
0309 
0310     ret = of_graph_parse_endpoint(ep, &endpoint);
0311     if (ret) {
0312         DRM_DEV_ERROR(p2d->dev,
0313                   "failed to parse endpoint of port@0: %d\n", ret);
0314         goto out;
0315     }
0316 
0317     p2d->pl_sel = endpoint.id;
0318 out:
0319     of_node_put(ep);
0320 
0321     return ret;
0322 }
0323 
0324 static int imx8qxp_pxl2dpi_parse_dt_companion(struct imx8qxp_pxl2dpi *p2d)
0325 {
0326     struct imx8qxp_pxl2dpi *companion_p2d;
0327     struct device *dev = p2d->dev;
0328     struct device_node *companion;
0329     struct device_node *port1, *port2;
0330     const struct of_device_id *match;
0331     int dual_link;
0332     int ret = 0;
0333 
0334     /* Locate the companion PXL2DPI for dual-link operation, if any. */
0335     companion = of_parse_phandle(dev->of_node, "fsl,companion-pxl2dpi", 0);
0336     if (!companion)
0337         return 0;
0338 
0339     if (!of_device_is_available(companion)) {
0340         DRM_DEV_ERROR(dev, "companion PXL2DPI is not available\n");
0341         ret = -ENODEV;
0342         goto out;
0343     }
0344 
0345     /*
0346      * Sanity check: the companion bridge must have the same compatible
0347      * string.
0348      */
0349     match = of_match_device(dev->driver->of_match_table, dev);
0350     if (!of_device_is_compatible(companion, match->compatible)) {
0351         DRM_DEV_ERROR(dev, "companion PXL2DPI is incompatible\n");
0352         ret = -ENXIO;
0353         goto out;
0354     }
0355 
0356     p2d->companion = of_drm_find_bridge(companion);
0357     if (!p2d->companion) {
0358         ret = -EPROBE_DEFER;
0359         DRM_DEV_DEBUG_DRIVER(p2d->dev,
0360                      "failed to find companion bridge: %d\n",
0361                      ret);
0362         goto out;
0363     }
0364 
0365     companion_p2d = bridge_to_p2d(p2d->companion);
0366 
0367     /*
0368      * We need to work out if the sink is expecting us to function in
0369      * dual-link mode.  We do this by looking at the DT port nodes that
0370      * the next bridges are connected to.  If they are marked as expecting
0371      * even pixels and odd pixels than we need to use the companion PXL2DPI.
0372      */
0373     port1 = of_graph_get_port_by_id(p2d->next_bridge->of_node, 1);
0374     port2 = of_graph_get_port_by_id(companion_p2d->next_bridge->of_node, 1);
0375     dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);
0376     of_node_put(port1);
0377     of_node_put(port2);
0378 
0379     if (dual_link < 0) {
0380         ret = dual_link;
0381         DRM_DEV_ERROR(dev, "failed to get dual link pixel order: %d\n",
0382                   ret);
0383         goto out;
0384     }
0385 
0386     DRM_DEV_DEBUG_DRIVER(dev,
0387                  "dual-link configuration detected (companion bridge %pOF)\n",
0388                  companion);
0389 out:
0390     of_node_put(companion);
0391     return ret;
0392 }
0393 
0394 static int imx8qxp_pxl2dpi_bridge_probe(struct platform_device *pdev)
0395 {
0396     struct imx8qxp_pxl2dpi *p2d;
0397     struct device *dev = &pdev->dev;
0398     struct device_node *np = dev->of_node;
0399     int ret;
0400 
0401     p2d = devm_kzalloc(dev, sizeof(*p2d), GFP_KERNEL);
0402     if (!p2d)
0403         return -ENOMEM;
0404 
0405     p2d->regmap = syscon_node_to_regmap(np->parent);
0406     if (IS_ERR(p2d->regmap)) {
0407         ret = PTR_ERR(p2d->regmap);
0408         if (ret != -EPROBE_DEFER)
0409             DRM_DEV_ERROR(dev, "failed to get regmap: %d\n", ret);
0410         return ret;
0411     }
0412 
0413     ret = imx_scu_get_handle(&p2d->ipc_handle);
0414     if (ret) {
0415         if (ret != -EPROBE_DEFER)
0416             DRM_DEV_ERROR(dev, "failed to get SCU ipc handle: %d\n",
0417                       ret);
0418         return ret;
0419     }
0420 
0421     p2d->dev = dev;
0422 
0423     ret = of_property_read_u32(np, "fsl,sc-resource", &p2d->sc_resource);
0424     if (ret) {
0425         DRM_DEV_ERROR(dev, "failed to get SC resource %d\n", ret);
0426         return ret;
0427     }
0428 
0429     p2d->next_bridge = imx8qxp_pxl2dpi_find_next_bridge(p2d);
0430     if (IS_ERR(p2d->next_bridge)) {
0431         ret = PTR_ERR(p2d->next_bridge);
0432         if (ret != -EPROBE_DEFER)
0433             DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",
0434                       ret);
0435         return ret;
0436     }
0437 
0438     ret = imx8qxp_pxl2dpi_set_pixel_link_sel(p2d);
0439     if (ret)
0440         return ret;
0441 
0442     ret = imx8qxp_pxl2dpi_parse_dt_companion(p2d);
0443     if (ret)
0444         return ret;
0445 
0446     platform_set_drvdata(pdev, p2d);
0447     pm_runtime_enable(dev);
0448 
0449     p2d->bridge.driver_private = p2d;
0450     p2d->bridge.funcs = &imx8qxp_pxl2dpi_bridge_funcs;
0451     p2d->bridge.of_node = np;
0452 
0453     drm_bridge_add(&p2d->bridge);
0454 
0455     return ret;
0456 }
0457 
0458 static int imx8qxp_pxl2dpi_bridge_remove(struct platform_device *pdev)
0459 {
0460     struct imx8qxp_pxl2dpi *p2d = platform_get_drvdata(pdev);
0461 
0462     drm_bridge_remove(&p2d->bridge);
0463 
0464     pm_runtime_disable(&pdev->dev);
0465 
0466     return 0;
0467 }
0468 
0469 static const struct of_device_id imx8qxp_pxl2dpi_dt_ids[] = {
0470     { .compatible = "fsl,imx8qxp-pxl2dpi", },
0471     { /* sentinel */ }
0472 };
0473 MODULE_DEVICE_TABLE(of, imx8qxp_pxl2dpi_dt_ids);
0474 
0475 static struct platform_driver imx8qxp_pxl2dpi_bridge_driver = {
0476     .probe  = imx8qxp_pxl2dpi_bridge_probe,
0477     .remove = imx8qxp_pxl2dpi_bridge_remove,
0478     .driver = {
0479         .of_match_table = imx8qxp_pxl2dpi_dt_ids,
0480         .name = DRIVER_NAME,
0481     },
0482 };
0483 module_platform_driver(imx8qxp_pxl2dpi_bridge_driver);
0484 
0485 MODULE_DESCRIPTION("i.MX8QXP pixel link to DPI bridge driver");
0486 MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
0487 MODULE_LICENSE("GPL v2");
0488 MODULE_ALIAS("platform:" DRIVER_NAME);