587 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_graph.h>13#include <linux/phy/phy.h>14#include <linux/platform_device.h>15#include <linux/pm_runtime.h>16#include <linux/regmap.h>17 18#include <drm/drm_atomic_state_helper.h>19#include <drm/drm_bridge.h>20#include <drm/drm_connector.h>21#include <drm/drm_fourcc.h>22#include <drm/drm_of.h>23#include <drm/drm_print.h>24 25#include "imx-ldb-helper.h"26 27#define LDB_CH0_10BIT_EN BIT(22)28#define LDB_CH1_10BIT_EN BIT(23)29#define LDB_CH0_DATA_WIDTH_24BIT BIT(24)30#define LDB_CH1_DATA_WIDTH_24BIT BIT(26)31#define LDB_CH0_DATA_WIDTH_30BIT (2 << 24)32#define LDB_CH1_DATA_WIDTH_30BIT (2 << 26)33 34#define SS_CTRL 0x2035#define CH_HSYNC_M(id) BIT(0 + ((id) * 2))36#define CH_VSYNC_M(id) BIT(1 + ((id) * 2))37#define CH_PHSYNC(id) BIT(0 + ((id) * 2))38#define CH_PVSYNC(id) BIT(1 + ((id) * 2))39 40#define DRIVER_NAME "imx8qm-ldb"41 42struct imx8qm_ldb_channel {43 struct ldb_channel base;44 struct phy *phy;45};46 47struct imx8qm_ldb {48 struct ldb base;49 struct device *dev;50 struct imx8qm_ldb_channel channel[MAX_LDB_CHAN_NUM];51 struct clk *clk_pixel;52 struct clk *clk_bypass;53 int active_chno;54};55 56static inline struct imx8qm_ldb_channel *57base_to_imx8qm_ldb_channel(struct ldb_channel *base)58{59 return container_of(base, struct imx8qm_ldb_channel, base);60}61 62static inline struct imx8qm_ldb *base_to_imx8qm_ldb(struct ldb *base)63{64 return container_of(base, struct imx8qm_ldb, base);65}66 67static void imx8qm_ldb_set_phy_cfg(struct imx8qm_ldb *imx8qm_ldb,68 unsigned long di_clk,69 bool is_split, bool is_slave,70 struct phy_configure_opts_lvds *phy_cfg)71{72 phy_cfg->bits_per_lane_and_dclk_cycle = 7;73 phy_cfg->lanes = 4;74 phy_cfg->differential_clk_rate = is_split ? di_clk / 2 : di_clk;75 phy_cfg->is_slave = is_slave;76}77 78static int imx8qm_ldb_bridge_atomic_check(struct drm_bridge *bridge,79 struct drm_bridge_state *bridge_state,80 struct drm_crtc_state *crtc_state,81 struct drm_connector_state *conn_state)82{83 struct ldb_channel *ldb_ch = bridge->driver_private;84 struct ldb *ldb = ldb_ch->ldb;85 struct imx8qm_ldb_channel *imx8qm_ldb_ch =86 base_to_imx8qm_ldb_channel(ldb_ch);87 struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);88 struct drm_display_mode *adj = &crtc_state->adjusted_mode;89 unsigned long di_clk = adj->clock * 1000;90 bool is_split = ldb_channel_is_split_link(ldb_ch);91 union phy_configure_opts opts = { };92 struct phy_configure_opts_lvds *phy_cfg = &opts.lvds;93 int ret;94 95 ret = ldb_bridge_atomic_check_helper(bridge, bridge_state,96 crtc_state, conn_state);97 if (ret)98 return ret;99 100 imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, false, phy_cfg);101 ret = phy_validate(imx8qm_ldb_ch->phy, PHY_MODE_LVDS, 0, &opts);102 if (ret < 0) {103 DRM_DEV_DEBUG_DRIVER(imx8qm_ldb->dev,104 "failed to validate PHY: %d\n", ret);105 return ret;106 }107 108 if (is_split) {109 imx8qm_ldb_ch =110 &imx8qm_ldb->channel[imx8qm_ldb->active_chno ^ 1];111 imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, true,112 phy_cfg);113 ret = phy_validate(imx8qm_ldb_ch->phy, PHY_MODE_LVDS, 0, &opts);114 if (ret < 0) {115 DRM_DEV_DEBUG_DRIVER(imx8qm_ldb->dev,116 "failed to validate slave PHY: %d\n",117 ret);118 return ret;119 }120 }121 122 return ret;123}124 125static void126imx8qm_ldb_bridge_mode_set(struct drm_bridge *bridge,127 const struct drm_display_mode *mode,128 const struct drm_display_mode *adjusted_mode)129{130 struct ldb_channel *ldb_ch = bridge->driver_private;131 struct ldb *ldb = ldb_ch->ldb;132 struct imx8qm_ldb_channel *imx8qm_ldb_ch =133 base_to_imx8qm_ldb_channel(ldb_ch);134 struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);135 struct device *dev = imx8qm_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(imx8qm_ldb_ch->phy);148 if (ret < 0)149 DRM_DEV_ERROR(dev, "failed to initialize PHY: %d\n", ret);150 151 clk_set_rate(imx8qm_ldb->clk_bypass, di_clk);152 clk_set_rate(imx8qm_ldb->clk_pixel, di_clk);153 154 imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, false, phy_cfg);155 ret = phy_configure(imx8qm_ldb_ch->phy, &opts);156 if (ret < 0)157 DRM_DEV_ERROR(dev, "failed to configure PHY: %d\n", ret);158 159 if (is_split) {160 imx8qm_ldb_ch =161 &imx8qm_ldb->channel[imx8qm_ldb->active_chno ^ 1];162 imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, true,163 phy_cfg);164 ret = phy_configure(imx8qm_ldb_ch->phy, &opts);165 if (ret < 0)166 DRM_DEV_ERROR(dev, "failed to configure slave PHY: %d\n",167 ret);168 }169 170 /* input VSYNC signal from pixel link is active low */171 if (ldb_ch->chno == 0 || is_split)172 ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;173 if (ldb_ch->chno == 1 || is_split)174 ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;175 176 switch (ldb_ch->out_bus_format) {177 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:178 break;179 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:180 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:181 if (ldb_ch->chno == 0 || is_split)182 ldb->ldb_ctrl |= LDB_CH0_DATA_WIDTH_24BIT;183 if (ldb_ch->chno == 1 || is_split)184 ldb->ldb_ctrl |= LDB_CH1_DATA_WIDTH_24BIT;185 break;186 }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 203static void204imx8qm_ldb_bridge_atomic_enable(struct drm_bridge *bridge,205 struct drm_bridge_state *old_bridge_state)206{207 struct ldb_channel *ldb_ch = bridge->driver_private;208 struct ldb *ldb = ldb_ch->ldb;209 struct imx8qm_ldb_channel *imx8qm_ldb_ch =210 base_to_imx8qm_ldb_channel(ldb_ch);211 struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);212 struct device *dev = imx8qm_ldb->dev;213 bool is_split = ldb_channel_is_split_link(ldb_ch);214 int ret;215 216 clk_prepare_enable(imx8qm_ldb->clk_pixel);217 clk_prepare_enable(imx8qm_ldb->clk_bypass);218 219 /* both DI0 and DI1 connect with pixel link, so ok to use DI0 only */220 if (ldb_ch->chno == 0 || is_split) {221 ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;222 ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0;223 }224 if (ldb_ch->chno == 1 || is_split) {225 ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;226 ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI0;227 }228 229 if (is_split) {230 ret = phy_power_on(imx8qm_ldb->channel[0].phy);231 if (ret)232 DRM_DEV_ERROR(dev,233 "failed to power on channel0 PHY: %d\n",234 ret);235 236 ret = phy_power_on(imx8qm_ldb->channel[1].phy);237 if (ret)238 DRM_DEV_ERROR(dev,239 "failed to power on channel1 PHY: %d\n",240 ret);241 } else {242 ret = phy_power_on(imx8qm_ldb_ch->phy);243 if (ret)244 DRM_DEV_ERROR(dev, "failed to power on PHY: %d\n", ret);245 }246 247 ldb_bridge_enable_helper(bridge);248}249 250static void251imx8qm_ldb_bridge_atomic_disable(struct drm_bridge *bridge,252 struct drm_bridge_state *old_bridge_state)253{254 struct ldb_channel *ldb_ch = bridge->driver_private;255 struct ldb *ldb = ldb_ch->ldb;256 struct imx8qm_ldb_channel *imx8qm_ldb_ch =257 base_to_imx8qm_ldb_channel(ldb_ch);258 struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);259 struct device *dev = imx8qm_ldb->dev;260 bool is_split = ldb_channel_is_split_link(ldb_ch);261 int ret;262 263 ldb_bridge_disable_helper(bridge);264 265 if (is_split) {266 ret = phy_power_off(imx8qm_ldb->channel[0].phy);267 if (ret)268 DRM_DEV_ERROR(dev,269 "failed to power off channel0 PHY: %d\n",270 ret);271 ret = phy_power_off(imx8qm_ldb->channel[1].phy);272 if (ret)273 DRM_DEV_ERROR(dev,274 "failed to power off channel1 PHY: %d\n",275 ret);276 } else {277 ret = phy_power_off(imx8qm_ldb_ch->phy);278 if (ret)279 DRM_DEV_ERROR(dev, "failed to power off PHY: %d\n", ret);280 }281 282 clk_disable_unprepare(imx8qm_ldb->clk_bypass);283 clk_disable_unprepare(imx8qm_ldb->clk_pixel);284 285 ret = pm_runtime_put(dev);286 if (ret < 0)287 DRM_DEV_ERROR(dev, "failed to put runtime PM: %d\n", ret);288}289 290static const u32 imx8qm_ldb_bus_output_fmts[] = {291 MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,292 MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,293 MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,294 MEDIA_BUS_FMT_FIXED,295};296 297static bool imx8qm_ldb_bus_output_fmt_supported(u32 fmt)298{299 int i;300 301 for (i = 0; i < ARRAY_SIZE(imx8qm_ldb_bus_output_fmts); i++) {302 if (imx8qm_ldb_bus_output_fmts[i] == fmt)303 return true;304 }305 306 return false;307}308 309static u32 *310imx8qm_ldb_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,311 struct drm_bridge_state *bridge_state,312 struct drm_crtc_state *crtc_state,313 struct drm_connector_state *conn_state,314 u32 output_fmt,315 unsigned int *num_input_fmts)316{317 struct drm_display_info *di;318 const struct drm_format_info *finfo;319 u32 *input_fmts;320 321 if (!imx8qm_ldb_bus_output_fmt_supported(output_fmt))322 return NULL;323 324 *num_input_fmts = 1;325 326 input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);327 if (!input_fmts)328 return NULL;329 330 switch (output_fmt) {331 case MEDIA_BUS_FMT_FIXED:332 di = &conn_state->connector->display_info;333 334 /*335 * Look at the first bus format to determine input format.336 * Default to MEDIA_BUS_FMT_RGB888_1X36_CPADLO, if no match.337 */338 if (di->num_bus_formats) {339 finfo = drm_format_info(di->bus_formats[0]);340 341 input_fmts[0] = finfo->depth == 18 ?342 MEDIA_BUS_FMT_RGB666_1X36_CPADLO :343 MEDIA_BUS_FMT_RGB888_1X36_CPADLO;344 } else {345 input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X36_CPADLO;346 }347 break;348 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:349 input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X36_CPADLO;350 break;351 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:352 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:353 input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X36_CPADLO;354 break;355 default:356 kfree(input_fmts);357 input_fmts = NULL;358 break;359 }360 361 return input_fmts;362}363 364static u32 *365imx8qm_ldb_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,366 struct drm_bridge_state *bridge_state,367 struct drm_crtc_state *crtc_state,368 struct drm_connector_state *conn_state,369 unsigned int *num_output_fmts)370{371 *num_output_fmts = ARRAY_SIZE(imx8qm_ldb_bus_output_fmts);372 return kmemdup(imx8qm_ldb_bus_output_fmts,373 sizeof(imx8qm_ldb_bus_output_fmts), GFP_KERNEL);374}375 376static enum drm_mode_status377imx8qm_ldb_bridge_mode_valid(struct drm_bridge *bridge,378 const struct drm_display_info *info,379 const struct drm_display_mode *mode)380{381 struct ldb_channel *ldb_ch = bridge->driver_private;382 bool is_single = ldb_channel_is_single_link(ldb_ch);383 384 if (mode->clock > 300000)385 return MODE_CLOCK_HIGH;386 387 if (mode->clock > 150000 && is_single)388 return MODE_CLOCK_HIGH;389 390 return MODE_OK;391}392 393static const struct drm_bridge_funcs imx8qm_ldb_bridge_funcs = {394 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,395 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,396 .atomic_reset = drm_atomic_helper_bridge_reset,397 .mode_valid = imx8qm_ldb_bridge_mode_valid,398 .attach = ldb_bridge_attach_helper,399 .atomic_check = imx8qm_ldb_bridge_atomic_check,400 .mode_set = imx8qm_ldb_bridge_mode_set,401 .atomic_enable = imx8qm_ldb_bridge_atomic_enable,402 .atomic_disable = imx8qm_ldb_bridge_atomic_disable,403 .atomic_get_input_bus_fmts =404 imx8qm_ldb_bridge_atomic_get_input_bus_fmts,405 .atomic_get_output_bus_fmts =406 imx8qm_ldb_bridge_atomic_get_output_bus_fmts,407};408 409static int imx8qm_ldb_get_phy(struct imx8qm_ldb *imx8qm_ldb)410{411 struct imx8qm_ldb_channel *imx8qm_ldb_ch;412 struct ldb_channel *ldb_ch;413 struct device *dev = imx8qm_ldb->dev;414 int i, ret;415 416 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {417 imx8qm_ldb_ch = &imx8qm_ldb->channel[i];418 ldb_ch = &imx8qm_ldb_ch->base;419 420 if (!ldb_ch->is_available)421 continue;422 423 imx8qm_ldb_ch->phy = devm_of_phy_get(dev, ldb_ch->np,424 "lvds_phy");425 if (IS_ERR(imx8qm_ldb_ch->phy)) {426 ret = PTR_ERR(imx8qm_ldb_ch->phy);427 if (ret != -EPROBE_DEFER)428 DRM_DEV_ERROR(dev,429 "failed to get channel%d PHY: %d\n",430 i, ret);431 return ret;432 }433 }434 435 return 0;436}437 438static int imx8qm_ldb_probe(struct platform_device *pdev)439{440 struct device *dev = &pdev->dev;441 struct imx8qm_ldb *imx8qm_ldb;442 struct imx8qm_ldb_channel *imx8qm_ldb_ch;443 struct ldb *ldb;444 struct ldb_channel *ldb_ch;445 struct device_node *port1, *port2;446 int pixel_order;447 int ret, i;448 449 imx8qm_ldb = devm_kzalloc(dev, sizeof(*imx8qm_ldb), GFP_KERNEL);450 if (!imx8qm_ldb)451 return -ENOMEM;452 453 imx8qm_ldb->clk_pixel = devm_clk_get(dev, "pixel");454 if (IS_ERR(imx8qm_ldb->clk_pixel)) {455 ret = PTR_ERR(imx8qm_ldb->clk_pixel);456 if (ret != -EPROBE_DEFER)457 DRM_DEV_ERROR(dev,458 "failed to get pixel clock: %d\n", ret);459 return ret;460 }461 462 imx8qm_ldb->clk_bypass = devm_clk_get(dev, "bypass");463 if (IS_ERR(imx8qm_ldb->clk_bypass)) {464 ret = PTR_ERR(imx8qm_ldb->clk_bypass);465 if (ret != -EPROBE_DEFER)466 DRM_DEV_ERROR(dev,467 "failed to get bypass clock: %d\n", ret);468 return ret;469 }470 471 imx8qm_ldb->dev = dev;472 473 ldb = &imx8qm_ldb->base;474 ldb->dev = dev;475 ldb->ctrl_reg = 0xe0;476 477 for (i = 0; i < MAX_LDB_CHAN_NUM; i++)478 ldb->channel[i] = &imx8qm_ldb->channel[i].base;479 480 ret = ldb_init_helper(ldb);481 if (ret)482 return ret;483 484 if (ldb->available_ch_cnt == 0) {485 DRM_DEV_DEBUG_DRIVER(dev, "no available channel\n");486 return 0;487 }488 489 if (ldb->available_ch_cnt == 2) {490 port1 = of_graph_get_port_by_id(ldb->channel[0]->np, 1);491 port2 = of_graph_get_port_by_id(ldb->channel[1]->np, 1);492 pixel_order =493 drm_of_lvds_get_dual_link_pixel_order(port1, port2);494 of_node_put(port1);495 of_node_put(port2);496 497 if (pixel_order != DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS) {498 DRM_DEV_ERROR(dev, "invalid dual link pixel order: %d\n",499 pixel_order);500 return -EINVAL;501 }502 503 imx8qm_ldb->active_chno = 0;504 imx8qm_ldb_ch = &imx8qm_ldb->channel[0];505 ldb_ch = &imx8qm_ldb_ch->base;506 ldb_ch->link_type = pixel_order;507 } else {508 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {509 imx8qm_ldb_ch = &imx8qm_ldb->channel[i];510 ldb_ch = &imx8qm_ldb_ch->base;511 512 if (ldb_ch->is_available) {513 imx8qm_ldb->active_chno = ldb_ch->chno;514 break;515 }516 }517 }518 519 ret = imx8qm_ldb_get_phy(imx8qm_ldb);520 if (ret)521 return ret;522 523 ret = ldb_find_next_bridge_helper(ldb);524 if (ret)525 return ret;526 527 platform_set_drvdata(pdev, imx8qm_ldb);528 pm_runtime_enable(dev);529 530 ldb_add_bridge_helper(ldb, &imx8qm_ldb_bridge_funcs);531 532 return ret;533}534 535static void imx8qm_ldb_remove(struct platform_device *pdev)536{537 struct imx8qm_ldb *imx8qm_ldb = platform_get_drvdata(pdev);538 struct ldb *ldb = &imx8qm_ldb->base;539 540 ldb_remove_bridge_helper(ldb);541 542 pm_runtime_disable(&pdev->dev);543}544 545static int __maybe_unused imx8qm_ldb_runtime_suspend(struct device *dev)546{547 return 0;548}549 550static int __maybe_unused imx8qm_ldb_runtime_resume(struct device *dev)551{552 struct imx8qm_ldb *imx8qm_ldb = dev_get_drvdata(dev);553 struct ldb *ldb = &imx8qm_ldb->base;554 555 /* disable LDB by resetting the control register to POR default */556 regmap_write(ldb->regmap, ldb->ctrl_reg, 0);557 558 return 0;559}560 561static const struct dev_pm_ops imx8qm_ldb_pm_ops = {562 SET_RUNTIME_PM_OPS(imx8qm_ldb_runtime_suspend,563 imx8qm_ldb_runtime_resume, NULL)564};565 566static const struct of_device_id imx8qm_ldb_dt_ids[] = {567 { .compatible = "fsl,imx8qm-ldb" },568 { /* sentinel */ }569};570MODULE_DEVICE_TABLE(of, imx8qm_ldb_dt_ids);571 572static struct platform_driver imx8qm_ldb_driver = {573 .probe = imx8qm_ldb_probe,574 .remove_new = imx8qm_ldb_remove,575 .driver = {576 .pm = &imx8qm_ldb_pm_ops,577 .name = DRIVER_NAME,578 .of_match_table = imx8qm_ldb_dt_ids,579 },580};581module_platform_driver(imx8qm_ldb_driver);582 583MODULE_DESCRIPTION("i.MX8QM LVDS Display Bridge(LDB)/Pixel Mapper bridge driver");584MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");585MODULE_LICENSE("GPL v2");586MODULE_ALIAS("platform:" DRIVER_NAME);587