Lines Matching +full:imx8qxp +full:- +full:ldb

1 // SPDX-License-Identifier: GPL-2.0+
8 #include <linux/media-bus-format.h>
26 #include "imx-ldb-helper.h"
36 #define DRIVER_NAME "imx8qxp-ldb"
45 struct ldb base;
60 static inline struct imx8qxp_ldb *base_to_imx8qxp_ldb(struct ldb *base) in base_to_imx8qxp_ldb()
69 phy_cfg->bits_per_lane_and_dclk_cycle = 7; in imx8qxp_ldb_set_phy_cfg()
70 phy_cfg->lanes = 4; in imx8qxp_ldb_set_phy_cfg()
73 phy_cfg->differential_clk_rate = di_clk / 2; in imx8qxp_ldb_set_phy_cfg()
74 phy_cfg->is_slave = !imx8qxp_ldb->companion; in imx8qxp_ldb_set_phy_cfg()
76 phy_cfg->differential_clk_rate = di_clk; in imx8qxp_ldb_set_phy_cfg()
77 phy_cfg->is_slave = false; in imx8qxp_ldb_set_phy_cfg()
87 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_atomic_check()
88 struct ldb *ldb = ldb_ch->ldb; in imx8qxp_ldb_bridge_atomic_check() local
91 struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb); in imx8qxp_ldb_bridge_atomic_check()
92 struct drm_bridge *companion = imx8qxp_ldb->companion; in imx8qxp_ldb_bridge_atomic_check()
93 struct drm_display_mode *adj = &crtc_state->adjusted_mode; in imx8qxp_ldb_bridge_atomic_check()
94 unsigned long di_clk = adj->clock * 1000; in imx8qxp_ldb_bridge_atomic_check()
106 ret = phy_validate(imx8qxp_ldb_ch->phy, PHY_MODE_LVDS, 0, &opts); in imx8qxp_ldb_bridge_atomic_check()
108 DRM_DEV_DEBUG_DRIVER(imx8qxp_ldb->dev, in imx8qxp_ldb_bridge_atomic_check()
114 ret = companion->funcs->atomic_check(companion, in imx8qxp_ldb_bridge_atomic_check()
128 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_mode_set()
130 struct ldb *ldb = ldb_ch->ldb; in imx8qxp_ldb_bridge_mode_set() local
133 struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb); in imx8qxp_ldb_bridge_mode_set()
134 struct drm_bridge *companion = imx8qxp_ldb->companion; in imx8qxp_ldb_bridge_mode_set()
135 struct device *dev = imx8qxp_ldb->dev; in imx8qxp_ldb_bridge_mode_set()
136 unsigned long di_clk = adjusted_mode->clock * 1000; in imx8qxp_ldb_bridge_mode_set()
140 u32 chno = ldb_ch->chno; in imx8qxp_ldb_bridge_mode_set()
147 ret = phy_init(imx8qxp_ldb_ch->phy); in imx8qxp_ldb_bridge_mode_set()
151 ret = phy_set_mode(imx8qxp_ldb_ch->phy, PHY_MODE_LVDS); in imx8qxp_ldb_bridge_mode_set()
158 companion_ldb_ch->in_bus_format = ldb_ch->in_bus_format; in imx8qxp_ldb_bridge_mode_set()
159 companion_ldb_ch->out_bus_format = ldb_ch->out_bus_format; in imx8qxp_ldb_bridge_mode_set()
162 clk_set_rate(imx8qxp_ldb->clk_bypass, di_clk); in imx8qxp_ldb_bridge_mode_set()
163 clk_set_rate(imx8qxp_ldb->clk_pixel, di_clk); in imx8qxp_ldb_bridge_mode_set()
166 ret = phy_configure(imx8qxp_ldb_ch->phy, &opts); in imx8qxp_ldb_bridge_mode_set()
171 ldb->ldb_ctrl &= ~LDB_CH_SEL; in imx8qxp_ldb_bridge_mode_set()
173 ldb->ldb_ctrl |= LDB_CH_SEL; in imx8qxp_ldb_bridge_mode_set()
176 if (imx8qxp_ldb_ch->di_id == 0) in imx8qxp_ldb_bridge_mode_set()
177 ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW; in imx8qxp_ldb_bridge_mode_set()
179 ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW; in imx8qxp_ldb_bridge_mode_set()
186 regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl); in imx8qxp_ldb_bridge_mode_set()
190 if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) in imx8qxp_ldb_bridge_mode_set()
191 regmap_update_bits(ldb->regmap, SS_CTRL, CH_VSYNC_M(chno), 0); in imx8qxp_ldb_bridge_mode_set()
192 else if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) in imx8qxp_ldb_bridge_mode_set()
193 regmap_update_bits(ldb->regmap, SS_CTRL, in imx8qxp_ldb_bridge_mode_set()
196 if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) in imx8qxp_ldb_bridge_mode_set()
197 regmap_update_bits(ldb->regmap, SS_CTRL, CH_HSYNC_M(chno), 0); in imx8qxp_ldb_bridge_mode_set()
198 else if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) in imx8qxp_ldb_bridge_mode_set()
199 regmap_update_bits(ldb->regmap, SS_CTRL, in imx8qxp_ldb_bridge_mode_set()
203 companion->funcs->mode_set(companion, mode, adjusted_mode); in imx8qxp_ldb_bridge_mode_set()
210 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_atomic_pre_enable()
211 struct ldb *ldb = ldb_ch->ldb; in imx8qxp_ldb_bridge_atomic_pre_enable() local
212 struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb); in imx8qxp_ldb_bridge_atomic_pre_enable()
213 struct drm_bridge *companion = imx8qxp_ldb->companion; in imx8qxp_ldb_bridge_atomic_pre_enable()
216 clk_prepare_enable(imx8qxp_ldb->clk_pixel); in imx8qxp_ldb_bridge_atomic_pre_enable()
217 clk_prepare_enable(imx8qxp_ldb->clk_bypass); in imx8qxp_ldb_bridge_atomic_pre_enable()
220 companion->funcs->atomic_pre_enable(companion, old_bridge_state); in imx8qxp_ldb_bridge_atomic_pre_enable()
227 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_atomic_enable()
228 struct ldb *ldb = ldb_ch->ldb; in imx8qxp_ldb_bridge_atomic_enable() local
231 struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb); in imx8qxp_ldb_bridge_atomic_enable()
232 struct drm_bridge *companion = imx8qxp_ldb->companion; in imx8qxp_ldb_bridge_atomic_enable()
233 struct device *dev = imx8qxp_ldb->dev; in imx8qxp_ldb_bridge_atomic_enable()
237 if (ldb_ch->chno == 0 || is_split) { in imx8qxp_ldb_bridge_atomic_enable()
238 ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; in imx8qxp_ldb_bridge_atomic_enable()
239 ldb->ldb_ctrl |= imx8qxp_ldb_ch->di_id == 0 ? in imx8qxp_ldb_bridge_atomic_enable()
242 if (ldb_ch->chno == 1 || is_split) { in imx8qxp_ldb_bridge_atomic_enable()
243 ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK; in imx8qxp_ldb_bridge_atomic_enable()
244 ldb->ldb_ctrl |= imx8qxp_ldb_ch->di_id == 0 ? in imx8qxp_ldb_bridge_atomic_enable()
250 ret = phy_power_on(imx8qxp_ldb_ch->phy); in imx8qxp_ldb_bridge_atomic_enable()
255 companion->funcs->atomic_enable(companion, old_bridge_state); in imx8qxp_ldb_bridge_atomic_enable()
262 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_atomic_disable()
263 struct ldb *ldb = ldb_ch->ldb; in imx8qxp_ldb_bridge_atomic_disable() local
266 struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb); in imx8qxp_ldb_bridge_atomic_disable()
267 struct drm_bridge *companion = imx8qxp_ldb->companion; in imx8qxp_ldb_bridge_atomic_disable()
268 struct device *dev = imx8qxp_ldb->dev; in imx8qxp_ldb_bridge_atomic_disable()
272 ret = phy_power_off(imx8qxp_ldb_ch->phy); in imx8qxp_ldb_bridge_atomic_disable()
276 ret = phy_exit(imx8qxp_ldb_ch->phy); in imx8qxp_ldb_bridge_atomic_disable()
282 clk_disable_unprepare(imx8qxp_ldb->clk_bypass); in imx8qxp_ldb_bridge_atomic_disable()
283 clk_disable_unprepare(imx8qxp_ldb->clk_pixel); in imx8qxp_ldb_bridge_atomic_disable()
286 companion->funcs->atomic_disable(companion, old_bridge_state); in imx8qxp_ldb_bridge_atomic_disable()
335 di = &conn_state->connector->display_info; in imx8qxp_ldb_bridge_atomic_get_input_bus_fmts()
341 if (di->num_bus_formats) { in imx8qxp_ldb_bridge_atomic_get_input_bus_fmts()
342 finfo = drm_format_info(di->bus_formats[0]); in imx8qxp_ldb_bridge_atomic_get_input_bus_fmts()
344 input_fmts[0] = finfo->depth == 18 ? in imx8qxp_ldb_bridge_atomic_get_input_bus_fmts()
384 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_mode_valid()
387 if (mode->clock > 170000) in imx8qxp_ldb_bridge_mode_valid()
390 if (mode->clock > 150000 && is_single) in imx8qxp_ldb_bridge_mode_valid()
416 &imx8qxp_ldb->channel[imx8qxp_ldb->active_chno]; in imx8qxp_ldb_set_di_id()
417 struct ldb_channel *ldb_ch = &imx8qxp_ldb_ch->base; in imx8qxp_ldb_set_di_id()
419 struct device *dev = imx8qxp_ldb->dev; in imx8qxp_ldb_set_di_id()
423 ep = of_graph_get_endpoint_by_regs(ldb_ch->np, 0, -1); in imx8qxp_ldb_set_di_id()
426 return -EINVAL; in imx8qxp_ldb_set_di_id()
433 return -EINVAL; in imx8qxp_ldb_set_di_id()
444 imx8qxp_ldb_ch->di_id = endpoint.id; in imx8qxp_ldb_set_di_id()
452 if ((link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS && ldb_ch->chno != 0) || in imx8qxp_ldb_check_chno_and_dual_link()
453 (link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS && ldb_ch->chno != 1)) in imx8qxp_ldb_check_chno_and_dual_link()
454 return -EINVAL; in imx8qxp_ldb_check_chno_and_dual_link()
462 &imx8qxp_ldb->channel[imx8qxp_ldb->active_chno]; in imx8qxp_ldb_parse_dt_companion()
463 struct ldb_channel *ldb_ch = &imx8qxp_ldb_ch->base; in imx8qxp_ldb_parse_dt_companion()
469 struct device *dev = imx8qxp_ldb->dev; in imx8qxp_ldb_parse_dt_companion()
475 /* Locate the companion LDB for dual-link operation, if any. */ in imx8qxp_ldb_parse_dt_companion()
476 companion = of_parse_phandle(dev->of_node, "fsl,companion-ldb", 0); in imx8qxp_ldb_parse_dt_companion()
481 DRM_DEV_ERROR(dev, "companion LDB is not available\n"); in imx8qxp_ldb_parse_dt_companion()
482 ret = -ENODEV; in imx8qxp_ldb_parse_dt_companion()
490 match = of_match_device(dev->driver->of_match_table, dev); in imx8qxp_ldb_parse_dt_companion()
491 if (!of_device_is_compatible(companion, match->compatible)) { in imx8qxp_ldb_parse_dt_companion()
492 DRM_DEV_ERROR(dev, "companion LDB is incompatible\n"); in imx8qxp_ldb_parse_dt_companion()
493 ret = -ENXIO; in imx8qxp_ldb_parse_dt_companion()
499 if (ret || i > MAX_LDB_CHAN_NUM - 1) { in imx8qxp_ldb_parse_dt_companion()
502 ret = -EINVAL; in imx8qxp_ldb_parse_dt_companion()
511 if (i == (ldb_ch->chno ^ 0x1)) { in imx8qxp_ldb_parse_dt_companion()
519 "failed to find companion LDB channel port\n"); in imx8qxp_ldb_parse_dt_companion()
520 ret = -EINVAL; in imx8qxp_ldb_parse_dt_companion()
526 * dual-link mode. We do this by looking at the DT port nodes we are in imx8qxp_ldb_parse_dt_companion()
528 * even pixels than we need to enable LDB split mode. in imx8qxp_ldb_parse_dt_companion()
530 port1 = of_graph_get_port_by_id(ldb_ch->np, 1); in imx8qxp_ldb_parse_dt_companion()
538 ldb_ch->link_type = LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS; in imx8qxp_ldb_parse_dt_companion()
541 ldb_ch->link_type = LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS; in imx8qxp_ldb_parse_dt_companion()
554 ldb_ch->chno, dual_link); in imx8qxp_ldb_parse_dt_companion()
558 imx8qxp_ldb->companion = of_drm_find_bridge(companion_port); in imx8qxp_ldb_parse_dt_companion()
559 if (!imx8qxp_ldb->companion) { in imx8qxp_ldb_parse_dt_companion()
560 ret = -EPROBE_DEFER; in imx8qxp_ldb_parse_dt_companion()
568 "dual-link configuration detected (companion bridge %pOF)\n", in imx8qxp_ldb_parse_dt_companion()
571 companion_ldb_ch = bridge_to_ldb_ch(imx8qxp_ldb->companion); in imx8qxp_ldb_parse_dt_companion()
572 companion_ldb_ch->link_type = ldb_ch->link_type; in imx8qxp_ldb_parse_dt_companion()
581 struct device *dev = &pdev->dev; in imx8qxp_ldb_probe()
584 struct ldb *ldb; in imx8qxp_ldb_probe() local
590 return -ENOMEM; in imx8qxp_ldb_probe()
592 imx8qxp_ldb->clk_pixel = devm_clk_get(dev, "pixel"); in imx8qxp_ldb_probe()
593 if (IS_ERR(imx8qxp_ldb->clk_pixel)) { in imx8qxp_ldb_probe()
594 ret = PTR_ERR(imx8qxp_ldb->clk_pixel); in imx8qxp_ldb_probe()
595 if (ret != -EPROBE_DEFER) in imx8qxp_ldb_probe()
601 imx8qxp_ldb->clk_bypass = devm_clk_get(dev, "bypass"); in imx8qxp_ldb_probe()
602 if (IS_ERR(imx8qxp_ldb->clk_bypass)) { in imx8qxp_ldb_probe()
603 ret = PTR_ERR(imx8qxp_ldb->clk_bypass); in imx8qxp_ldb_probe()
604 if (ret != -EPROBE_DEFER) in imx8qxp_ldb_probe()
610 imx8qxp_ldb->dev = dev; in imx8qxp_ldb_probe()
612 ldb = &imx8qxp_ldb->base; in imx8qxp_ldb_probe()
613 ldb->dev = dev; in imx8qxp_ldb_probe()
614 ldb->ctrl_reg = 0xe0; in imx8qxp_ldb_probe()
617 ldb->channel[i] = &imx8qxp_ldb->channel[i].base; in imx8qxp_ldb_probe()
619 ret = ldb_init_helper(ldb); in imx8qxp_ldb_probe()
623 if (ldb->available_ch_cnt == 0) { in imx8qxp_ldb_probe()
626 } else if (ldb->available_ch_cnt > 1) { in imx8qxp_ldb_probe()
628 ldb->available_ch_cnt); in imx8qxp_ldb_probe()
629 return -EINVAL; in imx8qxp_ldb_probe()
633 imx8qxp_ldb_ch = &imx8qxp_ldb->channel[i]; in imx8qxp_ldb_probe()
634 ldb_ch = &imx8qxp_ldb_ch->base; in imx8qxp_ldb_probe()
636 if (ldb_ch->is_available) { in imx8qxp_ldb_probe()
637 imx8qxp_ldb->active_chno = ldb_ch->chno; in imx8qxp_ldb_probe()
642 imx8qxp_ldb_ch->phy = devm_of_phy_get(dev, ldb_ch->np, "lvds_phy"); in imx8qxp_ldb_probe()
643 if (IS_ERR(imx8qxp_ldb_ch->phy)) { in imx8qxp_ldb_probe()
644 ret = PTR_ERR(imx8qxp_ldb_ch->phy); in imx8qxp_ldb_probe()
645 if (ret != -EPROBE_DEFER) in imx8qxp_ldb_probe()
647 imx8qxp_ldb->active_chno, ret); in imx8qxp_ldb_probe()
651 ret = ldb_find_next_bridge_helper(ldb); in imx8qxp_ldb_probe()
666 ldb_add_bridge_helper(ldb, &imx8qxp_ldb_bridge_funcs); in imx8qxp_ldb_probe()
674 struct ldb *ldb = &imx8qxp_ldb->base; in imx8qxp_ldb_remove() local
676 ldb_remove_bridge_helper(ldb); in imx8qxp_ldb_remove()
678 pm_runtime_disable(&pdev->dev); in imx8qxp_ldb_remove()
689 struct ldb *ldb = &imx8qxp_ldb->base; in imx8qxp_ldb_runtime_resume() local
691 /* disable LDB by resetting the control register to POR default */ in imx8qxp_ldb_runtime_resume()
692 regmap_write(ldb->regmap, ldb->ctrl_reg, 0); in imx8qxp_ldb_runtime_resume()
703 { .compatible = "fsl,imx8qxp-ldb" },
719 MODULE_DESCRIPTION("i.MX8QXP LVDS Display Bridge(LDB)/Pixel Mapper bridge driver");