424 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 * Copyright 2020,2022 NXP5 */6 7#include <linux/firmware/imx/svc/misc.h>8#include <linux/media-bus-format.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/of_graph.h>12#include <linux/platform_device.h>13 14#include <drm/drm_atomic_state_helper.h>15#include <drm/drm_bridge.h>16#include <drm/drm_print.h>17 18#include <dt-bindings/firmware/imx/rsrc.h>19 20#define DRIVER_NAME "imx8qxp-display-pixel-link"21#define PL_MAX_MST_ADDR 322#define PL_MAX_NEXT_BRIDGES 223 24struct imx8qxp_pixel_link {25 struct drm_bridge bridge;26 struct drm_bridge *next_bridge;27 struct device *dev;28 struct imx_sc_ipc *ipc_handle;29 u8 stream_id;30 u8 dc_id;31 u32 sink_rsc;32 u32 mst_addr;33 u8 mst_addr_ctrl;34 u8 mst_en_ctrl;35 u8 mst_vld_ctrl;36 u8 sync_ctrl;37};38 39static void imx8qxp_pixel_link_enable_mst_en(struct imx8qxp_pixel_link *pl)40{41 int ret;42 43 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,44 pl->mst_en_ctrl, true);45 if (ret)46 DRM_DEV_ERROR(pl->dev,47 "failed to enable DC%u stream%u pixel link mst_en: %d\n",48 pl->dc_id, pl->stream_id, ret);49}50 51static void imx8qxp_pixel_link_enable_mst_vld(struct imx8qxp_pixel_link *pl)52{53 int ret;54 55 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,56 pl->mst_vld_ctrl, true);57 if (ret)58 DRM_DEV_ERROR(pl->dev,59 "failed to enable DC%u stream%u pixel link mst_vld: %d\n",60 pl->dc_id, pl->stream_id, ret);61}62 63static void imx8qxp_pixel_link_enable_sync(struct imx8qxp_pixel_link *pl)64{65 int ret;66 67 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,68 pl->sync_ctrl, true);69 if (ret)70 DRM_DEV_ERROR(pl->dev,71 "failed to enable DC%u stream%u pixel link sync: %d\n",72 pl->dc_id, pl->stream_id, ret);73}74 75static int imx8qxp_pixel_link_disable_mst_en(struct imx8qxp_pixel_link *pl)76{77 int ret;78 79 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,80 pl->mst_en_ctrl, false);81 if (ret)82 DRM_DEV_ERROR(pl->dev,83 "failed to disable DC%u stream%u pixel link mst_en: %d\n",84 pl->dc_id, pl->stream_id, ret);85 86 return ret;87}88 89static int imx8qxp_pixel_link_disable_mst_vld(struct imx8qxp_pixel_link *pl)90{91 int ret;92 93 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,94 pl->mst_vld_ctrl, false);95 if (ret)96 DRM_DEV_ERROR(pl->dev,97 "failed to disable DC%u stream%u pixel link mst_vld: %d\n",98 pl->dc_id, pl->stream_id, ret);99 100 return ret;101}102 103static int imx8qxp_pixel_link_disable_sync(struct imx8qxp_pixel_link *pl)104{105 int ret;106 107 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,108 pl->sync_ctrl, false);109 if (ret)110 DRM_DEV_ERROR(pl->dev,111 "failed to disable DC%u stream%u pixel link sync: %d\n",112 pl->dc_id, pl->stream_id, ret);113 114 return ret;115}116 117static void imx8qxp_pixel_link_set_mst_addr(struct imx8qxp_pixel_link *pl)118{119 int ret;120 121 ret = imx_sc_misc_set_control(pl->ipc_handle,122 pl->sink_rsc, pl->mst_addr_ctrl,123 pl->mst_addr);124 if (ret)125 DRM_DEV_ERROR(pl->dev,126 "failed to set DC%u stream%u pixel link mst addr(%u): %d\n",127 pl->dc_id, pl->stream_id, pl->mst_addr, ret);128}129 130static int imx8qxp_pixel_link_bridge_attach(struct drm_bridge *bridge,131 enum drm_bridge_attach_flags flags)132{133 struct imx8qxp_pixel_link *pl = bridge->driver_private;134 135 if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {136 DRM_DEV_ERROR(pl->dev,137 "do not support creating a drm_connector\n");138 return -EINVAL;139 }140 141 return drm_bridge_attach(bridge->encoder,142 pl->next_bridge, bridge,143 DRM_BRIDGE_ATTACH_NO_CONNECTOR);144}145 146static void147imx8qxp_pixel_link_bridge_mode_set(struct drm_bridge *bridge,148 const struct drm_display_mode *mode,149 const struct drm_display_mode *adjusted_mode)150{151 struct imx8qxp_pixel_link *pl = bridge->driver_private;152 153 imx8qxp_pixel_link_set_mst_addr(pl);154}155 156static void157imx8qxp_pixel_link_bridge_atomic_enable(struct drm_bridge *bridge,158 struct drm_bridge_state *old_bridge_state)159{160 struct imx8qxp_pixel_link *pl = bridge->driver_private;161 162 imx8qxp_pixel_link_enable_mst_en(pl);163 imx8qxp_pixel_link_enable_mst_vld(pl);164 imx8qxp_pixel_link_enable_sync(pl);165}166 167static void168imx8qxp_pixel_link_bridge_atomic_disable(struct drm_bridge *bridge,169 struct drm_bridge_state *old_bridge_state)170{171 struct imx8qxp_pixel_link *pl = bridge->driver_private;172 173 imx8qxp_pixel_link_disable_mst_en(pl);174 imx8qxp_pixel_link_disable_mst_vld(pl);175 imx8qxp_pixel_link_disable_sync(pl);176}177 178static const u32 imx8qxp_pixel_link_bus_output_fmts[] = {179 MEDIA_BUS_FMT_RGB888_1X36_CPADLO,180 MEDIA_BUS_FMT_RGB666_1X36_CPADLO,181};182 183static bool imx8qxp_pixel_link_bus_output_fmt_supported(u32 fmt)184{185 int i;186 187 for (i = 0; i < ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts); i++) {188 if (imx8qxp_pixel_link_bus_output_fmts[i] == fmt)189 return true;190 }191 192 return false;193}194 195static u32 *196imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,197 struct drm_bridge_state *bridge_state,198 struct drm_crtc_state *crtc_state,199 struct drm_connector_state *conn_state,200 u32 output_fmt,201 unsigned int *num_input_fmts)202{203 u32 *input_fmts;204 205 if (!imx8qxp_pixel_link_bus_output_fmt_supported(output_fmt))206 return NULL;207 208 *num_input_fmts = 1;209 210 input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);211 if (!input_fmts)212 return NULL;213 214 input_fmts[0] = output_fmt;215 216 return input_fmts;217}218 219static u32 *220imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,221 struct drm_bridge_state *bridge_state,222 struct drm_crtc_state *crtc_state,223 struct drm_connector_state *conn_state,224 unsigned int *num_output_fmts)225{226 *num_output_fmts = ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);227 return kmemdup(imx8qxp_pixel_link_bus_output_fmts,228 sizeof(imx8qxp_pixel_link_bus_output_fmts), GFP_KERNEL);229}230 231static const struct drm_bridge_funcs imx8qxp_pixel_link_bridge_funcs = {232 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,233 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,234 .atomic_reset = drm_atomic_helper_bridge_reset,235 .attach = imx8qxp_pixel_link_bridge_attach,236 .mode_set = imx8qxp_pixel_link_bridge_mode_set,237 .atomic_enable = imx8qxp_pixel_link_bridge_atomic_enable,238 .atomic_disable = imx8qxp_pixel_link_bridge_atomic_disable,239 .atomic_get_input_bus_fmts =240 imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts,241 .atomic_get_output_bus_fmts =242 imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts,243};244 245static int imx8qxp_pixel_link_disable_all_controls(struct imx8qxp_pixel_link *pl)246{247 int ret;248 249 ret = imx8qxp_pixel_link_disable_mst_en(pl);250 if (ret)251 return ret;252 253 ret = imx8qxp_pixel_link_disable_mst_vld(pl);254 if (ret)255 return ret;256 257 return imx8qxp_pixel_link_disable_sync(pl);258}259 260static struct drm_bridge *261imx8qxp_pixel_link_find_next_bridge(struct imx8qxp_pixel_link *pl)262{263 struct device_node *np = pl->dev->of_node;264 struct device_node *port, *remote;265 struct drm_bridge *next_bridge[PL_MAX_NEXT_BRIDGES];266 u32 port_id;267 bool found_port = false;268 int reg, ep_cnt = 0;269 /* select the first next bridge by default */270 int bridge_sel = 0;271 272 for (port_id = 1; port_id <= PL_MAX_MST_ADDR + 1; port_id++) {273 port = of_graph_get_port_by_id(np, port_id);274 if (!port)275 continue;276 277 if (of_device_is_available(port)) {278 found_port = true;279 of_node_put(port);280 break;281 }282 283 of_node_put(port);284 }285 286 if (!found_port) {287 DRM_DEV_ERROR(pl->dev, "no available output port\n");288 return ERR_PTR(-ENODEV);289 }290 291 for (reg = 0; reg < PL_MAX_NEXT_BRIDGES; reg++) {292 remote = of_graph_get_remote_node(np, port_id, reg);293 if (!remote)294 continue;295 296 if (!of_device_is_available(remote->parent)) {297 DRM_DEV_DEBUG(pl->dev,298 "port%u endpoint%u remote parent is not available\n",299 port_id, reg);300 of_node_put(remote);301 continue;302 }303 304 next_bridge[ep_cnt] = of_drm_find_bridge(remote);305 if (!next_bridge[ep_cnt]) {306 of_node_put(remote);307 return ERR_PTR(-EPROBE_DEFER);308 }309 310 /* specially select the next bridge with companion PXL2DPI */311 if (of_property_present(remote, "fsl,companion-pxl2dpi"))312 bridge_sel = ep_cnt;313 314 ep_cnt++;315 316 of_node_put(remote);317 }318 319 pl->mst_addr = port_id - 1;320 321 return next_bridge[bridge_sel];322}323 324static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev)325{326 struct imx8qxp_pixel_link *pl;327 struct device *dev = &pdev->dev;328 struct device_node *np = dev->of_node;329 int ret;330 331 pl = devm_kzalloc(dev, sizeof(*pl), GFP_KERNEL);332 if (!pl)333 return -ENOMEM;334 335 ret = imx_scu_get_handle(&pl->ipc_handle);336 if (ret) {337 if (ret != -EPROBE_DEFER)338 DRM_DEV_ERROR(dev, "failed to get SCU ipc handle: %d\n",339 ret);340 return ret;341 }342 343 ret = of_property_read_u8(np, "fsl,dc-id", &pl->dc_id);344 if (ret) {345 DRM_DEV_ERROR(dev, "failed to get DC index: %d\n", ret);346 return ret;347 }348 349 ret = of_property_read_u8(np, "fsl,dc-stream-id", &pl->stream_id);350 if (ret) {351 DRM_DEV_ERROR(dev, "failed to get DC stream index: %d\n", ret);352 return ret;353 }354 355 pl->dev = dev;356 357 pl->sink_rsc = pl->dc_id ? IMX_SC_R_DC_1 : IMX_SC_R_DC_0;358 359 if (pl->stream_id == 0) {360 pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST1_ADDR;361 pl->mst_en_ctrl = IMX_SC_C_PXL_LINK_MST1_ENB;362 pl->mst_vld_ctrl = IMX_SC_C_PXL_LINK_MST1_VLD;363 pl->sync_ctrl = IMX_SC_C_SYNC_CTRL0;364 } else {365 pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST2_ADDR;366 pl->mst_en_ctrl = IMX_SC_C_PXL_LINK_MST2_ENB;367 pl->mst_vld_ctrl = IMX_SC_C_PXL_LINK_MST2_VLD;368 pl->sync_ctrl = IMX_SC_C_SYNC_CTRL1;369 }370 371 /* disable all controls to POR default */372 ret = imx8qxp_pixel_link_disable_all_controls(pl);373 if (ret)374 return ret;375 376 pl->next_bridge = imx8qxp_pixel_link_find_next_bridge(pl);377 if (IS_ERR(pl->next_bridge)) {378 ret = PTR_ERR(pl->next_bridge);379 if (ret != -EPROBE_DEFER)380 DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",381 ret);382 return ret;383 }384 385 platform_set_drvdata(pdev, pl);386 387 pl->bridge.driver_private = pl;388 pl->bridge.funcs = &imx8qxp_pixel_link_bridge_funcs;389 pl->bridge.of_node = np;390 391 drm_bridge_add(&pl->bridge);392 393 return ret;394}395 396static void imx8qxp_pixel_link_bridge_remove(struct platform_device *pdev)397{398 struct imx8qxp_pixel_link *pl = platform_get_drvdata(pdev);399 400 drm_bridge_remove(&pl->bridge);401}402 403static const struct of_device_id imx8qxp_pixel_link_dt_ids[] = {404 { .compatible = "fsl,imx8qm-dc-pixel-link", },405 { .compatible = "fsl,imx8qxp-dc-pixel-link", },406 { /* sentinel */ }407};408MODULE_DEVICE_TABLE(of, imx8qxp_pixel_link_dt_ids);409 410static struct platform_driver imx8qxp_pixel_link_bridge_driver = {411 .probe = imx8qxp_pixel_link_bridge_probe,412 .remove_new = imx8qxp_pixel_link_bridge_remove,413 .driver = {414 .of_match_table = imx8qxp_pixel_link_dt_ids,415 .name = DRIVER_NAME,416 },417};418module_platform_driver(imx8qxp_pixel_link_bridge_driver);419 420MODULE_DESCRIPTION("i.MX8QXP/QM display pixel link bridge driver");421MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");422MODULE_LICENSE("GPL v2");423MODULE_ALIAS("platform:" DRIVER_NAME);424