723 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 * Copyright 2020 NXP5 */6 7#include <linux/clk.h>8#include <linux/media-bus-format.h>9#include <linux/mfd/syscon.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/of_device.h>13#include <linux/of_graph.h>14#include <linux/phy/phy.h>15#include <linux/platform_device.h>16#include <linux/pm_runtime.h>17#include <linux/regmap.h>18 19#include <drm/drm_atomic_state_helper.h>20#include <drm/drm_bridge.h>21#include <drm/drm_connector.h>22#include <drm/drm_fourcc.h>23#include <drm/drm_of.h>24#include <drm/drm_print.h>25 26#include "imx-ldb-helper.h"27 28#define LDB_CH_SEL BIT(28)29 30#define SS_CTRL 0x2031#define CH_HSYNC_M(id) BIT(0 + ((id) * 2))32#define CH_VSYNC_M(id) BIT(1 + ((id) * 2))33#define CH_PHSYNC(id) BIT(0 + ((id) * 2))34#define CH_PVSYNC(id) BIT(1 + ((id) * 2))35 36#define DRIVER_NAME "imx8qxp-ldb"37 38struct imx8qxp_ldb_channel {39 struct ldb_channel base;40 struct phy *phy;41 unsigned int di_id;42};43 44struct imx8qxp_ldb {45 struct ldb base;46 struct device *dev;47 struct imx8qxp_ldb_channel channel[MAX_LDB_CHAN_NUM];48 struct clk *clk_pixel;49 struct clk *clk_bypass;50 struct drm_bridge *companion;51 int active_chno;52};53 54static inline struct imx8qxp_ldb_channel *55base_to_imx8qxp_ldb_channel(struct ldb_channel *base)56{57 return container_of(base, struct imx8qxp_ldb_channel, base);58}59 60static inline struct imx8qxp_ldb *base_to_imx8qxp_ldb(struct ldb *base)61{62 return container_of(base, struct imx8qxp_ldb, base);63}64 65static void imx8qxp_ldb_set_phy_cfg(struct imx8qxp_ldb *imx8qxp_ldb,66 unsigned long di_clk, bool is_split,67 struct phy_configure_opts_lvds *phy_cfg)68{69 phy_cfg->bits_per_lane_and_dclk_cycle = 7;70 phy_cfg->lanes = 4;71 72 if (is_split) {73 phy_cfg->differential_clk_rate = di_clk / 2;74 phy_cfg->is_slave = !imx8qxp_ldb->companion;75 } else {76 phy_cfg->differential_clk_rate = di_clk;77 phy_cfg->is_slave = false;78 }79}80 81static int82imx8qxp_ldb_bridge_atomic_check(struct drm_bridge *bridge,83 struct drm_bridge_state *bridge_state,84 struct drm_crtc_state *crtc_state,85 struct drm_connector_state *conn_state)86{87 struct ldb_channel *ldb_ch = bridge->driver_private;88 struct ldb *ldb = ldb_ch->ldb;89 struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =90 base_to_imx8qxp_ldb_channel(ldb_ch);91 struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);92 struct drm_bridge *companion = imx8qxp_ldb->companion;93 struct drm_display_mode *adj = &crtc_state->adjusted_mode;94 unsigned long di_clk = adj->clock * 1000;95 bool is_split = ldb_channel_is_split_link(ldb_ch);96 union phy_configure_opts opts = { };97 struct phy_configure_opts_lvds *phy_cfg = &opts.lvds;98 int ret;99 100 ret = ldb_bridge_atomic_check_helper(bridge, bridge_state,101 crtc_state, conn_state);102 if (ret)103 return ret;104 105 imx8qxp_ldb_set_phy_cfg(imx8qxp_ldb, di_clk, is_split, phy_cfg);106 ret = phy_validate(imx8qxp_ldb_ch->phy, PHY_MODE_LVDS, 0, &opts);107 if (ret < 0) {108 DRM_DEV_DEBUG_DRIVER(imx8qxp_ldb->dev,109 "failed to validate PHY: %d\n", ret);110 return ret;111 }112 113 if (is_split && companion) {114 ret = companion->funcs->atomic_check(companion,115 bridge_state, crtc_state, conn_state);116 if (ret)117 return ret;118 }119 120 return ret;121}122 123static void124imx8qxp_ldb_bridge_mode_set(struct drm_bridge *bridge,125 const struct drm_display_mode *mode,126 const struct drm_display_mode *adjusted_mode)127{128 struct ldb_channel *ldb_ch = bridge->driver_private;129 struct ldb_channel *companion_ldb_ch;130 struct ldb *ldb = ldb_ch->ldb;131 struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =132 base_to_imx8qxp_ldb_channel(ldb_ch);133 struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);134 struct drm_bridge *companion = imx8qxp_ldb->companion;135 struct device *dev = imx8qxp_ldb->dev;136 unsigned long di_clk = adjusted_mode->clock * 1000;137 bool is_split = ldb_channel_is_split_link(ldb_ch);138 union phy_configure_opts opts = { };139 struct phy_configure_opts_lvds *phy_cfg = &opts.lvds;140 u32 chno = ldb_ch->chno;141 int ret;142 143 ret = pm_runtime_get_sync(dev);144 if (ret < 0)145 DRM_DEV_ERROR(dev, "failed to get runtime PM sync: %d\n", ret);146 147 ret = phy_init(imx8qxp_ldb_ch->phy);148 if (ret < 0)149 DRM_DEV_ERROR(dev, "failed to initialize PHY: %d\n", ret);150 151 ret = phy_set_mode(imx8qxp_ldb_ch->phy, PHY_MODE_LVDS);152 if (ret < 0)153 DRM_DEV_ERROR(dev, "failed to set PHY mode: %d\n", ret);154 155 if (is_split && companion) {156 companion_ldb_ch = bridge_to_ldb_ch(companion);157 158 companion_ldb_ch->in_bus_format = ldb_ch->in_bus_format;159 companion_ldb_ch->out_bus_format = ldb_ch->out_bus_format;160 }161 162 clk_set_rate(imx8qxp_ldb->clk_bypass, di_clk);163 clk_set_rate(imx8qxp_ldb->clk_pixel, di_clk);164 165 imx8qxp_ldb_set_phy_cfg(imx8qxp_ldb, di_clk, is_split, phy_cfg);166 ret = phy_configure(imx8qxp_ldb_ch->phy, &opts);167 if (ret < 0)168 DRM_DEV_ERROR(dev, "failed to configure PHY: %d\n", ret);169 170 if (chno == 0)171 ldb->ldb_ctrl &= ~LDB_CH_SEL;172 else173 ldb->ldb_ctrl |= LDB_CH_SEL;174 175 /* input VSYNC signal from pixel link is active low */176 if (imx8qxp_ldb_ch->di_id == 0)177 ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;178 else179 ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;180 181 /*182 * For split mode, settle input VSYNC signal polarity and183 * channel selection down early.184 */185 if (is_split)186 regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl);187 188 ldb_bridge_mode_set_helper(bridge, mode, adjusted_mode);189 190 if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)191 regmap_update_bits(ldb->regmap, SS_CTRL, CH_VSYNC_M(chno), 0);192 else if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)193 regmap_update_bits(ldb->regmap, SS_CTRL,194 CH_VSYNC_M(chno), CH_PVSYNC(chno));195 196 if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)197 regmap_update_bits(ldb->regmap, SS_CTRL, CH_HSYNC_M(chno), 0);198 else if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)199 regmap_update_bits(ldb->regmap, SS_CTRL,200 CH_HSYNC_M(chno), CH_PHSYNC(chno));201 202 if (is_split && companion)203 companion->funcs->mode_set(companion, mode, adjusted_mode);204}205 206static void207imx8qxp_ldb_bridge_atomic_pre_enable(struct drm_bridge *bridge,208 struct drm_bridge_state *old_bridge_state)209{210 struct ldb_channel *ldb_ch = bridge->driver_private;211 struct ldb *ldb = ldb_ch->ldb;212 struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);213 struct drm_bridge *companion = imx8qxp_ldb->companion;214 bool is_split = ldb_channel_is_split_link(ldb_ch);215 216 clk_prepare_enable(imx8qxp_ldb->clk_pixel);217 clk_prepare_enable(imx8qxp_ldb->clk_bypass);218 219 if (is_split && companion)220 companion->funcs->atomic_pre_enable(companion, old_bridge_state);221}222 223static void224imx8qxp_ldb_bridge_atomic_enable(struct drm_bridge *bridge,225 struct drm_bridge_state *old_bridge_state)226{227 struct ldb_channel *ldb_ch = bridge->driver_private;228 struct ldb *ldb = ldb_ch->ldb;229 struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =230 base_to_imx8qxp_ldb_channel(ldb_ch);231 struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);232 struct drm_bridge *companion = imx8qxp_ldb->companion;233 struct device *dev = imx8qxp_ldb->dev;234 bool is_split = ldb_channel_is_split_link(ldb_ch);235 int ret;236 237 if (ldb_ch->chno == 0 || is_split) {238 ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;239 ldb->ldb_ctrl |= imx8qxp_ldb_ch->di_id == 0 ?240 LDB_CH0_MODE_EN_TO_DI0 : LDB_CH0_MODE_EN_TO_DI1;241 }242 if (ldb_ch->chno == 1 || is_split) {243 ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;244 ldb->ldb_ctrl |= imx8qxp_ldb_ch->di_id == 0 ?245 LDB_CH1_MODE_EN_TO_DI0 : LDB_CH1_MODE_EN_TO_DI1;246 }247 248 ldb_bridge_enable_helper(bridge);249 250 ret = phy_power_on(imx8qxp_ldb_ch->phy);251 if (ret)252 DRM_DEV_ERROR(dev, "failed to power on PHY: %d\n", ret);253 254 if (is_split && companion)255 companion->funcs->atomic_enable(companion, old_bridge_state);256}257 258static void259imx8qxp_ldb_bridge_atomic_disable(struct drm_bridge *bridge,260 struct drm_bridge_state *old_bridge_state)261{262 struct ldb_channel *ldb_ch = bridge->driver_private;263 struct ldb *ldb = ldb_ch->ldb;264 struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =265 base_to_imx8qxp_ldb_channel(ldb_ch);266 struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);267 struct drm_bridge *companion = imx8qxp_ldb->companion;268 struct device *dev = imx8qxp_ldb->dev;269 bool is_split = ldb_channel_is_split_link(ldb_ch);270 int ret;271 272 ret = phy_power_off(imx8qxp_ldb_ch->phy);273 if (ret)274 DRM_DEV_ERROR(dev, "failed to power off PHY: %d\n", ret);275 276 ret = phy_exit(imx8qxp_ldb_ch->phy);277 if (ret < 0)278 DRM_DEV_ERROR(dev, "failed to teardown PHY: %d\n", ret);279 280 ldb_bridge_disable_helper(bridge);281 282 clk_disable_unprepare(imx8qxp_ldb->clk_bypass);283 clk_disable_unprepare(imx8qxp_ldb->clk_pixel);284 285 if (is_split && companion)286 companion->funcs->atomic_disable(companion, old_bridge_state);287 288 ret = pm_runtime_put(dev);289 if (ret < 0)290 DRM_DEV_ERROR(dev, "failed to put runtime PM: %d\n", ret);291}292 293static const u32 imx8qxp_ldb_bus_output_fmts[] = {294 MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,295 MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,296 MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,297 MEDIA_BUS_FMT_FIXED,298};299 300static bool imx8qxp_ldb_bus_output_fmt_supported(u32 fmt)301{302 int i;303 304 for (i = 0; i < ARRAY_SIZE(imx8qxp_ldb_bus_output_fmts); i++) {305 if (imx8qxp_ldb_bus_output_fmts[i] == fmt)306 return true;307 }308 309 return false;310}311 312static u32 *313imx8qxp_ldb_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,314 struct drm_bridge_state *bridge_state,315 struct drm_crtc_state *crtc_state,316 struct drm_connector_state *conn_state,317 u32 output_fmt,318 unsigned int *num_input_fmts)319{320 struct drm_display_info *di;321 const struct drm_format_info *finfo;322 u32 *input_fmts;323 324 if (!imx8qxp_ldb_bus_output_fmt_supported(output_fmt))325 return NULL;326 327 *num_input_fmts = 1;328 329 input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);330 if (!input_fmts)331 return NULL;332 333 switch (output_fmt) {334 case MEDIA_BUS_FMT_FIXED:335 di = &conn_state->connector->display_info;336 337 /*338 * Look at the first bus format to determine input format.339 * Default to MEDIA_BUS_FMT_RGB888_1X24, if no match.340 */341 if (di->num_bus_formats) {342 finfo = drm_format_info(di->bus_formats[0]);343 344 input_fmts[0] = finfo->depth == 18 ?345 MEDIA_BUS_FMT_RGB666_1X24_CPADHI :346 MEDIA_BUS_FMT_RGB888_1X24;347 } else {348 input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;349 }350 break;351 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:352 input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;353 break;354 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:355 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:356 input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;357 break;358 default:359 kfree(input_fmts);360 input_fmts = NULL;361 break;362 }363 364 return input_fmts;365}366 367static u32 *368imx8qxp_ldb_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,369 struct drm_bridge_state *bridge_state,370 struct drm_crtc_state *crtc_state,371 struct drm_connector_state *conn_state,372 unsigned int *num_output_fmts)373{374 *num_output_fmts = ARRAY_SIZE(imx8qxp_ldb_bus_output_fmts);375 return kmemdup(imx8qxp_ldb_bus_output_fmts,376 sizeof(imx8qxp_ldb_bus_output_fmts), GFP_KERNEL);377}378 379static enum drm_mode_status380imx8qxp_ldb_bridge_mode_valid(struct drm_bridge *bridge,381 const struct drm_display_info *info,382 const struct drm_display_mode *mode)383{384 struct ldb_channel *ldb_ch = bridge->driver_private;385 bool is_single = ldb_channel_is_single_link(ldb_ch);386 387 if (mode->clock > 170000)388 return MODE_CLOCK_HIGH;389 390 if (mode->clock > 150000 && is_single)391 return MODE_CLOCK_HIGH;392 393 return MODE_OK;394}395 396static const struct drm_bridge_funcs imx8qxp_ldb_bridge_funcs = {397 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,398 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,399 .atomic_reset = drm_atomic_helper_bridge_reset,400 .mode_valid = imx8qxp_ldb_bridge_mode_valid,401 .attach = ldb_bridge_attach_helper,402 .atomic_check = imx8qxp_ldb_bridge_atomic_check,403 .mode_set = imx8qxp_ldb_bridge_mode_set,404 .atomic_pre_enable = imx8qxp_ldb_bridge_atomic_pre_enable,405 .atomic_enable = imx8qxp_ldb_bridge_atomic_enable,406 .atomic_disable = imx8qxp_ldb_bridge_atomic_disable,407 .atomic_get_input_bus_fmts =408 imx8qxp_ldb_bridge_atomic_get_input_bus_fmts,409 .atomic_get_output_bus_fmts =410 imx8qxp_ldb_bridge_atomic_get_output_bus_fmts,411};412 413static int imx8qxp_ldb_set_di_id(struct imx8qxp_ldb *imx8qxp_ldb)414{415 struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =416 &imx8qxp_ldb->channel[imx8qxp_ldb->active_chno];417 struct ldb_channel *ldb_ch = &imx8qxp_ldb_ch->base;418 struct device_node *ep, *remote;419 struct device *dev = imx8qxp_ldb->dev;420 struct of_endpoint endpoint;421 int ret;422 423 ep = of_graph_get_endpoint_by_regs(ldb_ch->np, 0, -1);424 if (!ep) {425 DRM_DEV_ERROR(dev, "failed to get port0 endpoint\n");426 return -EINVAL;427 }428 429 remote = of_graph_get_remote_endpoint(ep);430 of_node_put(ep);431 if (!remote) {432 DRM_DEV_ERROR(dev, "failed to get port0 remote endpoint\n");433 return -EINVAL;434 }435 436 ret = of_graph_parse_endpoint(remote, &endpoint);437 of_node_put(remote);438 if (ret) {439 DRM_DEV_ERROR(dev, "failed to parse port0 remote endpoint: %d\n",440 ret);441 return ret;442 }443 444 imx8qxp_ldb_ch->di_id = endpoint.id;445 446 return 0;447}448 449static int450imx8qxp_ldb_check_chno_and_dual_link(struct ldb_channel *ldb_ch, int link)451{452 if ((link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS && ldb_ch->chno != 0) ||453 (link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS && ldb_ch->chno != 1))454 return -EINVAL;455 456 return 0;457}458 459static int imx8qxp_ldb_parse_dt_companion(struct imx8qxp_ldb *imx8qxp_ldb)460{461 struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =462 &imx8qxp_ldb->channel[imx8qxp_ldb->active_chno];463 struct ldb_channel *ldb_ch = &imx8qxp_ldb_ch->base;464 struct ldb_channel *companion_ldb_ch;465 struct device_node *companion;466 struct device_node *child;467 struct device_node *companion_port = NULL;468 struct device_node *port1, *port2;469 struct device *dev = imx8qxp_ldb->dev;470 const struct of_device_id *match;471 u32 i;472 int dual_link;473 int ret;474 475 /* Locate the companion LDB for dual-link operation, if any. */476 companion = of_parse_phandle(dev->of_node, "fsl,companion-ldb", 0);477 if (!companion)478 return 0;479 480 if (!of_device_is_available(companion)) {481 DRM_DEV_ERROR(dev, "companion LDB is not available\n");482 ret = -ENODEV;483 goto out;484 }485 486 /*487 * Sanity check: the companion bridge must have the same compatible488 * string.489 */490 match = of_match_device(dev->driver->of_match_table, dev);491 if (!of_device_is_compatible(companion, match->compatible)) {492 DRM_DEV_ERROR(dev, "companion LDB is incompatible\n");493 ret = -ENXIO;494 goto out;495 }496 497 for_each_available_child_of_node(companion, child) {498 ret = of_property_read_u32(child, "reg", &i);499 if (ret || i > MAX_LDB_CHAN_NUM - 1) {500 DRM_DEV_ERROR(dev,501 "invalid channel node address: %u\n", i);502 ret = -EINVAL;503 of_node_put(child);504 goto out;505 }506 507 /*508 * Channel numbers have to be different, because channel0509 * transmits odd pixels and channel1 transmits even pixels.510 */511 if (i == (ldb_ch->chno ^ 0x1)) {512 companion_port = child;513 break;514 }515 }516 517 if (!companion_port) {518 DRM_DEV_ERROR(dev,519 "failed to find companion LDB channel port\n");520 ret = -EINVAL;521 goto out;522 }523 524 /*525 * We need to work out if the sink is expecting us to function in526 * dual-link mode. We do this by looking at the DT port nodes we are527 * connected to. If they are marked as expecting odd pixels and528 * even pixels than we need to enable LDB split mode.529 */530 port1 = of_graph_get_port_by_id(ldb_ch->np, 1);531 port2 = of_graph_get_port_by_id(companion_port, 1);532 dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);533 of_node_put(port1);534 of_node_put(port2);535 536 switch (dual_link) {537 case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:538 ldb_ch->link_type = LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;539 break;540 case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:541 ldb_ch->link_type = LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS;542 break;543 default:544 ret = dual_link;545 DRM_DEV_ERROR(dev,546 "failed to get dual link pixel order: %d\n", ret);547 goto out;548 }549 550 ret = imx8qxp_ldb_check_chno_and_dual_link(ldb_ch, dual_link);551 if (ret < 0) {552 DRM_DEV_ERROR(dev,553 "unmatched channel number(%u) vs dual link(%d)\n",554 ldb_ch->chno, dual_link);555 goto out;556 }557 558 imx8qxp_ldb->companion = of_drm_find_bridge(companion_port);559 if (!imx8qxp_ldb->companion) {560 ret = -EPROBE_DEFER;561 DRM_DEV_DEBUG_DRIVER(dev,562 "failed to find bridge for companion bridge: %d\n",563 ret);564 goto out;565 }566 567 DRM_DEV_DEBUG_DRIVER(dev,568 "dual-link configuration detected (companion bridge %pOF)\n",569 companion);570 571 companion_ldb_ch = bridge_to_ldb_ch(imx8qxp_ldb->companion);572 companion_ldb_ch->link_type = ldb_ch->link_type;573out:574 of_node_put(companion_port);575 of_node_put(companion);576 return ret;577}578 579static int imx8qxp_ldb_probe(struct platform_device *pdev)580{581 struct device *dev = &pdev->dev;582 struct imx8qxp_ldb *imx8qxp_ldb;583 struct imx8qxp_ldb_channel *imx8qxp_ldb_ch;584 struct ldb *ldb;585 struct ldb_channel *ldb_ch;586 int ret, i;587 588 imx8qxp_ldb = devm_kzalloc(dev, sizeof(*imx8qxp_ldb), GFP_KERNEL);589 if (!imx8qxp_ldb)590 return -ENOMEM;591 592 imx8qxp_ldb->clk_pixel = devm_clk_get(dev, "pixel");593 if (IS_ERR(imx8qxp_ldb->clk_pixel)) {594 ret = PTR_ERR(imx8qxp_ldb->clk_pixel);595 if (ret != -EPROBE_DEFER)596 DRM_DEV_ERROR(dev,597 "failed to get pixel clock: %d\n", ret);598 return ret;599 }600 601 imx8qxp_ldb->clk_bypass = devm_clk_get(dev, "bypass");602 if (IS_ERR(imx8qxp_ldb->clk_bypass)) {603 ret = PTR_ERR(imx8qxp_ldb->clk_bypass);604 if (ret != -EPROBE_DEFER)605 DRM_DEV_ERROR(dev,606 "failed to get bypass clock: %d\n", ret);607 return ret;608 }609 610 imx8qxp_ldb->dev = dev;611 612 ldb = &imx8qxp_ldb->base;613 ldb->dev = dev;614 ldb->ctrl_reg = 0xe0;615 616 for (i = 0; i < MAX_LDB_CHAN_NUM; i++)617 ldb->channel[i] = &imx8qxp_ldb->channel[i].base;618 619 ret = ldb_init_helper(ldb);620 if (ret)621 return ret;622 623 if (ldb->available_ch_cnt == 0) {624 DRM_DEV_DEBUG_DRIVER(dev, "no available channel\n");625 return 0;626 } else if (ldb->available_ch_cnt > 1) {627 DRM_DEV_ERROR(dev, "invalid available channel number(%u)\n",628 ldb->available_ch_cnt);629 return -EINVAL;630 }631 632 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {633 imx8qxp_ldb_ch = &imx8qxp_ldb->channel[i];634 ldb_ch = &imx8qxp_ldb_ch->base;635 636 if (ldb_ch->is_available) {637 imx8qxp_ldb->active_chno = ldb_ch->chno;638 break;639 }640 }641 642 imx8qxp_ldb_ch->phy = devm_of_phy_get(dev, ldb_ch->np, "lvds_phy");643 if (IS_ERR(imx8qxp_ldb_ch->phy)) {644 ret = PTR_ERR(imx8qxp_ldb_ch->phy);645 if (ret != -EPROBE_DEFER)646 DRM_DEV_ERROR(dev, "failed to get channel%d PHY: %d\n",647 imx8qxp_ldb->active_chno, ret);648 return ret;649 }650 651 ret = ldb_find_next_bridge_helper(ldb);652 if (ret)653 return ret;654 655 ret = imx8qxp_ldb_set_di_id(imx8qxp_ldb);656 if (ret)657 return ret;658 659 ret = imx8qxp_ldb_parse_dt_companion(imx8qxp_ldb);660 if (ret)661 return ret;662 663 platform_set_drvdata(pdev, imx8qxp_ldb);664 pm_runtime_enable(dev);665 666 ldb_add_bridge_helper(ldb, &imx8qxp_ldb_bridge_funcs);667 668 return ret;669}670 671static void imx8qxp_ldb_remove(struct platform_device *pdev)672{673 struct imx8qxp_ldb *imx8qxp_ldb = platform_get_drvdata(pdev);674 struct ldb *ldb = &imx8qxp_ldb->base;675 676 ldb_remove_bridge_helper(ldb);677 678 pm_runtime_disable(&pdev->dev);679}680 681static int __maybe_unused imx8qxp_ldb_runtime_suspend(struct device *dev)682{683 return 0;684}685 686static int __maybe_unused imx8qxp_ldb_runtime_resume(struct device *dev)687{688 struct imx8qxp_ldb *imx8qxp_ldb = dev_get_drvdata(dev);689 struct ldb *ldb = &imx8qxp_ldb->base;690 691 /* disable LDB by resetting the control register to POR default */692 regmap_write(ldb->regmap, ldb->ctrl_reg, 0);693 694 return 0;695}696 697static const struct dev_pm_ops imx8qxp_ldb_pm_ops = {698 SET_RUNTIME_PM_OPS(imx8qxp_ldb_runtime_suspend,699 imx8qxp_ldb_runtime_resume, NULL)700};701 702static const struct of_device_id imx8qxp_ldb_dt_ids[] = {703 { .compatible = "fsl,imx8qxp-ldb" },704 { /* sentinel */ }705};706MODULE_DEVICE_TABLE(of, imx8qxp_ldb_dt_ids);707 708static struct platform_driver imx8qxp_ldb_driver = {709 .probe = imx8qxp_ldb_probe,710 .remove_new = imx8qxp_ldb_remove,711 .driver = {712 .pm = &imx8qxp_ldb_pm_ops,713 .name = DRIVER_NAME,714 .of_match_table = imx8qxp_ldb_dt_ids,715 },716};717module_platform_driver(imx8qxp_ldb_driver);718 719MODULE_DESCRIPTION("i.MX8QXP LVDS Display Bridge(LDB)/Pixel Mapper bridge driver");720MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");721MODULE_LICENSE("GPL v2");722MODULE_ALIAS("platform:" DRIVER_NAME);723