Path: blob/master/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
26517 views
// SPDX-License-Identifier: GPL-2.0+12/*3* Copyright 2020,2022 NXP4*/56#include <linux/firmware/imx/svc/misc.h>7#include <linux/media-bus-format.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/of_graph.h>11#include <linux/platform_device.h>1213#include <drm/drm_atomic_state_helper.h>14#include <drm/drm_bridge.h>15#include <drm/drm_print.h>1617#include <dt-bindings/firmware/imx/rsrc.h>1819#define DRIVER_NAME "imx8qxp-display-pixel-link"20#define PL_MAX_MST_ADDR 321#define PL_MAX_NEXT_BRIDGES 22223struct imx8qxp_pixel_link {24struct drm_bridge bridge;25struct drm_bridge *next_bridge;26struct device *dev;27struct imx_sc_ipc *ipc_handle;28u8 stream_id;29u8 dc_id;30u32 sink_rsc;31u32 mst_addr;32u8 mst_addr_ctrl;33u8 mst_en_ctrl;34u8 mst_vld_ctrl;35u8 sync_ctrl;36};3738static void imx8qxp_pixel_link_enable_mst_en(struct imx8qxp_pixel_link *pl)39{40int ret;4142ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,43pl->mst_en_ctrl, true);44if (ret)45DRM_DEV_ERROR(pl->dev,46"failed to enable DC%u stream%u pixel link mst_en: %d\n",47pl->dc_id, pl->stream_id, ret);48}4950static void imx8qxp_pixel_link_enable_mst_vld(struct imx8qxp_pixel_link *pl)51{52int ret;5354ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,55pl->mst_vld_ctrl, true);56if (ret)57DRM_DEV_ERROR(pl->dev,58"failed to enable DC%u stream%u pixel link mst_vld: %d\n",59pl->dc_id, pl->stream_id, ret);60}6162static void imx8qxp_pixel_link_enable_sync(struct imx8qxp_pixel_link *pl)63{64int ret;6566ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,67pl->sync_ctrl, true);68if (ret)69DRM_DEV_ERROR(pl->dev,70"failed to enable DC%u stream%u pixel link sync: %d\n",71pl->dc_id, pl->stream_id, ret);72}7374static int imx8qxp_pixel_link_disable_mst_en(struct imx8qxp_pixel_link *pl)75{76int ret;7778ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,79pl->mst_en_ctrl, false);80if (ret)81DRM_DEV_ERROR(pl->dev,82"failed to disable DC%u stream%u pixel link mst_en: %d\n",83pl->dc_id, pl->stream_id, ret);8485return ret;86}8788static int imx8qxp_pixel_link_disable_mst_vld(struct imx8qxp_pixel_link *pl)89{90int ret;9192ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,93pl->mst_vld_ctrl, false);94if (ret)95DRM_DEV_ERROR(pl->dev,96"failed to disable DC%u stream%u pixel link mst_vld: %d\n",97pl->dc_id, pl->stream_id, ret);9899return ret;100}101102static int imx8qxp_pixel_link_disable_sync(struct imx8qxp_pixel_link *pl)103{104int ret;105106ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,107pl->sync_ctrl, false);108if (ret)109DRM_DEV_ERROR(pl->dev,110"failed to disable DC%u stream%u pixel link sync: %d\n",111pl->dc_id, pl->stream_id, ret);112113return ret;114}115116static void imx8qxp_pixel_link_set_mst_addr(struct imx8qxp_pixel_link *pl)117{118int ret;119120ret = imx_sc_misc_set_control(pl->ipc_handle,121pl->sink_rsc, pl->mst_addr_ctrl,122pl->mst_addr);123if (ret)124DRM_DEV_ERROR(pl->dev,125"failed to set DC%u stream%u pixel link mst addr(%u): %d\n",126pl->dc_id, pl->stream_id, pl->mst_addr, ret);127}128129static int imx8qxp_pixel_link_bridge_attach(struct drm_bridge *bridge,130struct drm_encoder *encoder,131enum drm_bridge_attach_flags flags)132{133struct imx8qxp_pixel_link *pl = bridge->driver_private;134135if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {136DRM_DEV_ERROR(pl->dev,137"do not support creating a drm_connector\n");138return -EINVAL;139}140141return drm_bridge_attach(encoder,142pl->next_bridge, bridge,143DRM_BRIDGE_ATTACH_NO_CONNECTOR);144}145146static void147imx8qxp_pixel_link_bridge_mode_set(struct drm_bridge *bridge,148const struct drm_display_mode *mode,149const struct drm_display_mode *adjusted_mode)150{151struct imx8qxp_pixel_link *pl = bridge->driver_private;152153imx8qxp_pixel_link_set_mst_addr(pl);154}155156static void imx8qxp_pixel_link_bridge_atomic_enable(struct drm_bridge *bridge,157struct drm_atomic_state *state)158{159struct imx8qxp_pixel_link *pl = bridge->driver_private;160161imx8qxp_pixel_link_enable_mst_en(pl);162imx8qxp_pixel_link_enable_mst_vld(pl);163imx8qxp_pixel_link_enable_sync(pl);164}165166static void imx8qxp_pixel_link_bridge_atomic_disable(struct drm_bridge *bridge,167struct drm_atomic_state *state)168{169struct imx8qxp_pixel_link *pl = bridge->driver_private;170171imx8qxp_pixel_link_disable_mst_en(pl);172imx8qxp_pixel_link_disable_mst_vld(pl);173imx8qxp_pixel_link_disable_sync(pl);174}175176static const u32 imx8qxp_pixel_link_bus_output_fmts[] = {177MEDIA_BUS_FMT_RGB888_1X36_CPADLO,178MEDIA_BUS_FMT_RGB666_1X36_CPADLO,179};180181static bool imx8qxp_pixel_link_bus_output_fmt_supported(u32 fmt)182{183int i;184185for (i = 0; i < ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts); i++) {186if (imx8qxp_pixel_link_bus_output_fmts[i] == fmt)187return true;188}189190return false;191}192193static u32 *194imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,195struct drm_bridge_state *bridge_state,196struct drm_crtc_state *crtc_state,197struct drm_connector_state *conn_state,198u32 output_fmt,199unsigned int *num_input_fmts)200{201u32 *input_fmts;202203if (!imx8qxp_pixel_link_bus_output_fmt_supported(output_fmt))204return NULL;205206*num_input_fmts = 1;207208input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);209if (!input_fmts)210return NULL;211212input_fmts[0] = output_fmt;213214return input_fmts;215}216217static u32 *218imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,219struct drm_bridge_state *bridge_state,220struct drm_crtc_state *crtc_state,221struct drm_connector_state *conn_state,222unsigned int *num_output_fmts)223{224*num_output_fmts = ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);225return kmemdup(imx8qxp_pixel_link_bus_output_fmts,226sizeof(imx8qxp_pixel_link_bus_output_fmts), GFP_KERNEL);227}228229static const struct drm_bridge_funcs imx8qxp_pixel_link_bridge_funcs = {230.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,231.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,232.atomic_reset = drm_atomic_helper_bridge_reset,233.attach = imx8qxp_pixel_link_bridge_attach,234.mode_set = imx8qxp_pixel_link_bridge_mode_set,235.atomic_enable = imx8qxp_pixel_link_bridge_atomic_enable,236.atomic_disable = imx8qxp_pixel_link_bridge_atomic_disable,237.atomic_get_input_bus_fmts =238imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts,239.atomic_get_output_bus_fmts =240imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts,241};242243static int imx8qxp_pixel_link_disable_all_controls(struct imx8qxp_pixel_link *pl)244{245int ret;246247ret = imx8qxp_pixel_link_disable_mst_en(pl);248if (ret)249return ret;250251ret = imx8qxp_pixel_link_disable_mst_vld(pl);252if (ret)253return ret;254255return imx8qxp_pixel_link_disable_sync(pl);256}257258static struct drm_bridge *259imx8qxp_pixel_link_find_next_bridge(struct imx8qxp_pixel_link *pl)260{261struct device_node *np = pl->dev->of_node;262struct device_node *port, *remote;263struct drm_bridge *next_bridge[PL_MAX_NEXT_BRIDGES];264u32 port_id;265bool found_port = false;266int reg, ep_cnt = 0;267/* select the first next bridge by default */268int bridge_sel = 0;269270for (port_id = 1; port_id <= PL_MAX_MST_ADDR + 1; port_id++) {271port = of_graph_get_port_by_id(np, port_id);272if (!port)273continue;274275if (of_device_is_available(port)) {276found_port = true;277of_node_put(port);278break;279}280281of_node_put(port);282}283284if (!found_port) {285DRM_DEV_ERROR(pl->dev, "no available output port\n");286return ERR_PTR(-ENODEV);287}288289for (reg = 0; reg < PL_MAX_NEXT_BRIDGES; reg++) {290remote = of_graph_get_remote_node(np, port_id, reg);291if (!remote)292continue;293294if (!of_device_is_available(remote->parent)) {295DRM_DEV_DEBUG(pl->dev,296"port%u endpoint%u remote parent is not available\n",297port_id, reg);298of_node_put(remote);299continue;300}301302next_bridge[ep_cnt] = of_drm_find_bridge(remote);303if (!next_bridge[ep_cnt]) {304of_node_put(remote);305return ERR_PTR(-EPROBE_DEFER);306}307308/* specially select the next bridge with companion PXL2DPI */309if (of_property_present(remote, "fsl,companion-pxl2dpi"))310bridge_sel = ep_cnt;311312ep_cnt++;313314of_node_put(remote);315}316317pl->mst_addr = port_id - 1;318319return next_bridge[bridge_sel];320}321322static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev)323{324struct imx8qxp_pixel_link *pl;325struct device *dev = &pdev->dev;326struct device_node *np = dev->of_node;327int ret;328329pl = devm_drm_bridge_alloc(dev, struct imx8qxp_pixel_link, bridge,330&imx8qxp_pixel_link_bridge_funcs);331if (IS_ERR(pl))332return PTR_ERR(pl);333334ret = imx_scu_get_handle(&pl->ipc_handle);335if (ret) {336if (ret != -EPROBE_DEFER)337DRM_DEV_ERROR(dev, "failed to get SCU ipc handle: %d\n",338ret);339return ret;340}341342ret = of_property_read_u8(np, "fsl,dc-id", &pl->dc_id);343if (ret) {344DRM_DEV_ERROR(dev, "failed to get DC index: %d\n", ret);345return ret;346}347348ret = of_property_read_u8(np, "fsl,dc-stream-id", &pl->stream_id);349if (ret) {350DRM_DEV_ERROR(dev, "failed to get DC stream index: %d\n", ret);351return ret;352}353354pl->dev = dev;355356pl->sink_rsc = pl->dc_id ? IMX_SC_R_DC_1 : IMX_SC_R_DC_0;357358if (pl->stream_id == 0) {359pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST1_ADDR;360pl->mst_en_ctrl = IMX_SC_C_PXL_LINK_MST1_ENB;361pl->mst_vld_ctrl = IMX_SC_C_PXL_LINK_MST1_VLD;362pl->sync_ctrl = IMX_SC_C_SYNC_CTRL0;363} else {364pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST2_ADDR;365pl->mst_en_ctrl = IMX_SC_C_PXL_LINK_MST2_ENB;366pl->mst_vld_ctrl = IMX_SC_C_PXL_LINK_MST2_VLD;367pl->sync_ctrl = IMX_SC_C_SYNC_CTRL1;368}369370/* disable all controls to POR default */371ret = imx8qxp_pixel_link_disable_all_controls(pl);372if (ret)373return ret;374375pl->next_bridge = imx8qxp_pixel_link_find_next_bridge(pl);376if (IS_ERR(pl->next_bridge)) {377ret = PTR_ERR(pl->next_bridge);378if (ret != -EPROBE_DEFER)379DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",380ret);381return ret;382}383384platform_set_drvdata(pdev, pl);385386pl->bridge.driver_private = pl;387pl->bridge.of_node = np;388389drm_bridge_add(&pl->bridge);390391return ret;392}393394static void imx8qxp_pixel_link_bridge_remove(struct platform_device *pdev)395{396struct imx8qxp_pixel_link *pl = platform_get_drvdata(pdev);397398drm_bridge_remove(&pl->bridge);399}400401static const struct of_device_id imx8qxp_pixel_link_dt_ids[] = {402{ .compatible = "fsl,imx8qm-dc-pixel-link", },403{ .compatible = "fsl,imx8qxp-dc-pixel-link", },404{ /* sentinel */ }405};406MODULE_DEVICE_TABLE(of, imx8qxp_pixel_link_dt_ids);407408static struct platform_driver imx8qxp_pixel_link_bridge_driver = {409.probe = imx8qxp_pixel_link_bridge_probe,410.remove = imx8qxp_pixel_link_bridge_remove,411.driver = {412.of_match_table = imx8qxp_pixel_link_dt_ids,413.name = DRIVER_NAME,414},415};416module_platform_driver(imx8qxp_pixel_link_bridge_driver);417418MODULE_DESCRIPTION("i.MX8QXP/QM display pixel link bridge driver");419MODULE_AUTHOR("Liu Ying <[email protected]>");420MODULE_LICENSE("GPL v2");421MODULE_ALIAS("platform:" DRIVER_NAME);422423424