Lines Matching +full:dual +full:- +full:channel
1 // SPDX-License-Identifier: GPL-2.0+
8 #include <linux/media-bus-format.h>
25 #include "imx-ldb-helper.h"
35 #define DRIVER_NAME "imx8qxp-ldb"
46 struct imx8qxp_ldb_channel channel[MAX_LDB_CHAN_NUM]; member
68 phy_cfg->bits_per_lane_and_dclk_cycle = 7; in imx8qxp_ldb_set_phy_cfg()
69 phy_cfg->lanes = 4; in imx8qxp_ldb_set_phy_cfg()
72 phy_cfg->differential_clk_rate = di_clk / 2; in imx8qxp_ldb_set_phy_cfg()
73 phy_cfg->is_slave = !imx8qxp_ldb->companion; in imx8qxp_ldb_set_phy_cfg()
75 phy_cfg->differential_clk_rate = di_clk; in imx8qxp_ldb_set_phy_cfg()
76 phy_cfg->is_slave = false; in imx8qxp_ldb_set_phy_cfg()
86 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_atomic_check()
87 struct ldb *ldb = ldb_ch->ldb; in imx8qxp_ldb_bridge_atomic_check()
91 struct drm_bridge *companion = imx8qxp_ldb->companion; in imx8qxp_ldb_bridge_atomic_check()
92 struct drm_display_mode *adj = &crtc_state->adjusted_mode; in imx8qxp_ldb_bridge_atomic_check()
93 unsigned long di_clk = adj->clock * 1000; in imx8qxp_ldb_bridge_atomic_check()
105 ret = phy_validate(imx8qxp_ldb_ch->phy, PHY_MODE_LVDS, 0, &opts); in imx8qxp_ldb_bridge_atomic_check()
107 DRM_DEV_DEBUG_DRIVER(imx8qxp_ldb->dev, in imx8qxp_ldb_bridge_atomic_check()
113 ret = companion->funcs->atomic_check(companion, in imx8qxp_ldb_bridge_atomic_check()
127 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_mode_set()
129 struct ldb *ldb = ldb_ch->ldb; in imx8qxp_ldb_bridge_mode_set()
133 struct drm_bridge *companion = imx8qxp_ldb->companion; in imx8qxp_ldb_bridge_mode_set()
134 struct device *dev = imx8qxp_ldb->dev; in imx8qxp_ldb_bridge_mode_set()
135 unsigned long di_clk = adjusted_mode->clock * 1000; in imx8qxp_ldb_bridge_mode_set()
139 u32 chno = ldb_ch->chno; in imx8qxp_ldb_bridge_mode_set()
146 ret = phy_init(imx8qxp_ldb_ch->phy); in imx8qxp_ldb_bridge_mode_set()
150 ret = phy_set_mode(imx8qxp_ldb_ch->phy, PHY_MODE_LVDS); in imx8qxp_ldb_bridge_mode_set()
157 companion_ldb_ch->in_bus_format = ldb_ch->in_bus_format; in imx8qxp_ldb_bridge_mode_set()
158 companion_ldb_ch->out_bus_format = ldb_ch->out_bus_format; in imx8qxp_ldb_bridge_mode_set()
161 clk_set_rate(imx8qxp_ldb->clk_bypass, di_clk); in imx8qxp_ldb_bridge_mode_set()
162 clk_set_rate(imx8qxp_ldb->clk_pixel, di_clk); in imx8qxp_ldb_bridge_mode_set()
165 ret = phy_configure(imx8qxp_ldb_ch->phy, &opts); in imx8qxp_ldb_bridge_mode_set()
170 ldb->ldb_ctrl &= ~LDB_CH_SEL; in imx8qxp_ldb_bridge_mode_set()
172 ldb->ldb_ctrl |= LDB_CH_SEL; in imx8qxp_ldb_bridge_mode_set()
175 if (imx8qxp_ldb_ch->di_id == 0) in imx8qxp_ldb_bridge_mode_set()
176 ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW; in imx8qxp_ldb_bridge_mode_set()
178 ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW; in imx8qxp_ldb_bridge_mode_set()
182 * channel selection down early. in imx8qxp_ldb_bridge_mode_set()
185 regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl); in imx8qxp_ldb_bridge_mode_set()
189 if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) in imx8qxp_ldb_bridge_mode_set()
190 regmap_update_bits(ldb->regmap, SS_CTRL, CH_VSYNC_M(chno), 0); in imx8qxp_ldb_bridge_mode_set()
191 else if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) in imx8qxp_ldb_bridge_mode_set()
192 regmap_update_bits(ldb->regmap, SS_CTRL, in imx8qxp_ldb_bridge_mode_set()
195 if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) in imx8qxp_ldb_bridge_mode_set()
196 regmap_update_bits(ldb->regmap, SS_CTRL, CH_HSYNC_M(chno), 0); in imx8qxp_ldb_bridge_mode_set()
197 else if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) in imx8qxp_ldb_bridge_mode_set()
198 regmap_update_bits(ldb->regmap, SS_CTRL, in imx8qxp_ldb_bridge_mode_set()
202 companion->funcs->mode_set(companion, mode, adjusted_mode); in imx8qxp_ldb_bridge_mode_set()
209 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_atomic_pre_enable()
210 struct ldb *ldb = ldb_ch->ldb; in imx8qxp_ldb_bridge_atomic_pre_enable()
212 struct drm_bridge *companion = imx8qxp_ldb->companion; in imx8qxp_ldb_bridge_atomic_pre_enable()
215 clk_prepare_enable(imx8qxp_ldb->clk_pixel); in imx8qxp_ldb_bridge_atomic_pre_enable()
216 clk_prepare_enable(imx8qxp_ldb->clk_bypass); in imx8qxp_ldb_bridge_atomic_pre_enable()
219 companion->funcs->atomic_pre_enable(companion, old_bridge_state); in imx8qxp_ldb_bridge_atomic_pre_enable()
226 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_atomic_enable()
227 struct ldb *ldb = ldb_ch->ldb; in imx8qxp_ldb_bridge_atomic_enable()
231 struct drm_bridge *companion = imx8qxp_ldb->companion; in imx8qxp_ldb_bridge_atomic_enable()
232 struct device *dev = imx8qxp_ldb->dev; in imx8qxp_ldb_bridge_atomic_enable()
236 if (ldb_ch->chno == 0 || is_split) { in imx8qxp_ldb_bridge_atomic_enable()
237 ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; in imx8qxp_ldb_bridge_atomic_enable()
238 ldb->ldb_ctrl |= imx8qxp_ldb_ch->di_id == 0 ? in imx8qxp_ldb_bridge_atomic_enable()
241 if (ldb_ch->chno == 1 || is_split) { in imx8qxp_ldb_bridge_atomic_enable()
242 ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK; in imx8qxp_ldb_bridge_atomic_enable()
243 ldb->ldb_ctrl |= imx8qxp_ldb_ch->di_id == 0 ? in imx8qxp_ldb_bridge_atomic_enable()
249 ret = phy_power_on(imx8qxp_ldb_ch->phy); in imx8qxp_ldb_bridge_atomic_enable()
254 companion->funcs->atomic_enable(companion, old_bridge_state); in imx8qxp_ldb_bridge_atomic_enable()
261 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_atomic_disable()
262 struct ldb *ldb = ldb_ch->ldb; in imx8qxp_ldb_bridge_atomic_disable()
266 struct drm_bridge *companion = imx8qxp_ldb->companion; in imx8qxp_ldb_bridge_atomic_disable()
267 struct device *dev = imx8qxp_ldb->dev; in imx8qxp_ldb_bridge_atomic_disable()
271 ret = phy_power_off(imx8qxp_ldb_ch->phy); in imx8qxp_ldb_bridge_atomic_disable()
275 ret = phy_exit(imx8qxp_ldb_ch->phy); in imx8qxp_ldb_bridge_atomic_disable()
281 clk_disable_unprepare(imx8qxp_ldb->clk_bypass); in imx8qxp_ldb_bridge_atomic_disable()
282 clk_disable_unprepare(imx8qxp_ldb->clk_pixel); in imx8qxp_ldb_bridge_atomic_disable()
285 companion->funcs->atomic_disable(companion, old_bridge_state); in imx8qxp_ldb_bridge_atomic_disable()
334 di = &conn_state->connector->display_info; in imx8qxp_ldb_bridge_atomic_get_input_bus_fmts()
340 if (di->num_bus_formats) { in imx8qxp_ldb_bridge_atomic_get_input_bus_fmts()
341 finfo = drm_format_info(di->bus_formats[0]); in imx8qxp_ldb_bridge_atomic_get_input_bus_fmts()
343 input_fmts[0] = finfo->depth == 18 ? in imx8qxp_ldb_bridge_atomic_get_input_bus_fmts()
383 struct ldb_channel *ldb_ch = bridge->driver_private; in imx8qxp_ldb_bridge_mode_valid()
386 if (mode->clock > 170000) in imx8qxp_ldb_bridge_mode_valid()
389 if (mode->clock > 150000 && is_single) in imx8qxp_ldb_bridge_mode_valid()
415 &imx8qxp_ldb->channel[imx8qxp_ldb->active_chno]; in imx8qxp_ldb_set_di_id()
416 struct ldb_channel *ldb_ch = &imx8qxp_ldb_ch->base; in imx8qxp_ldb_set_di_id()
418 struct device *dev = imx8qxp_ldb->dev; in imx8qxp_ldb_set_di_id()
422 ep = of_graph_get_endpoint_by_regs(ldb_ch->np, 0, -1); in imx8qxp_ldb_set_di_id()
425 return -EINVAL; in imx8qxp_ldb_set_di_id()
432 return -EINVAL; in imx8qxp_ldb_set_di_id()
443 imx8qxp_ldb_ch->di_id = endpoint.id; in imx8qxp_ldb_set_di_id()
451 if ((link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS && ldb_ch->chno != 0) || in imx8qxp_ldb_check_chno_and_dual_link()
452 (link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS && ldb_ch->chno != 1)) in imx8qxp_ldb_check_chno_and_dual_link()
453 return -EINVAL; in imx8qxp_ldb_check_chno_and_dual_link()
461 &imx8qxp_ldb->channel[imx8qxp_ldb->active_chno]; in imx8qxp_ldb_parse_dt_companion()
462 struct ldb_channel *ldb_ch = &imx8qxp_ldb_ch->base; in imx8qxp_ldb_parse_dt_companion()
468 struct device *dev = imx8qxp_ldb->dev; in imx8qxp_ldb_parse_dt_companion()
474 /* Locate the companion LDB for dual-link operation, if any. */ in imx8qxp_ldb_parse_dt_companion()
475 companion = of_parse_phandle(dev->of_node, "fsl,companion-ldb", 0); in imx8qxp_ldb_parse_dt_companion()
481 ret = -ENODEV; in imx8qxp_ldb_parse_dt_companion()
489 match = of_match_device(dev->driver->of_match_table, dev); in imx8qxp_ldb_parse_dt_companion()
490 if (!of_device_is_compatible(companion, match->compatible)) { in imx8qxp_ldb_parse_dt_companion()
492 ret = -ENXIO; in imx8qxp_ldb_parse_dt_companion()
498 if (ret || i > MAX_LDB_CHAN_NUM - 1) { in imx8qxp_ldb_parse_dt_companion()
500 "invalid channel node address: %u\n", i); in imx8qxp_ldb_parse_dt_companion()
501 ret = -EINVAL; in imx8qxp_ldb_parse_dt_companion()
507 * Channel numbers have to be different, because channel0 in imx8qxp_ldb_parse_dt_companion()
510 if (i == (ldb_ch->chno ^ 0x1)) { in imx8qxp_ldb_parse_dt_companion()
518 "failed to find companion LDB channel port\n"); in imx8qxp_ldb_parse_dt_companion()
519 ret = -EINVAL; in imx8qxp_ldb_parse_dt_companion()
525 * dual-link mode. We do this by looking at the DT port nodes we are in imx8qxp_ldb_parse_dt_companion()
529 port1 = of_graph_get_port_by_id(ldb_ch->np, 1); in imx8qxp_ldb_parse_dt_companion()
537 ldb_ch->link_type = LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS; in imx8qxp_ldb_parse_dt_companion()
540 ldb_ch->link_type = LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS; in imx8qxp_ldb_parse_dt_companion()
545 "failed to get dual link pixel order: %d\n", ret); in imx8qxp_ldb_parse_dt_companion()
552 "unmatched channel number(%u) vs dual link(%d)\n", in imx8qxp_ldb_parse_dt_companion()
553 ldb_ch->chno, dual_link); in imx8qxp_ldb_parse_dt_companion()
557 imx8qxp_ldb->companion = of_drm_find_bridge(companion_port); in imx8qxp_ldb_parse_dt_companion()
558 if (!imx8qxp_ldb->companion) { in imx8qxp_ldb_parse_dt_companion()
559 ret = -EPROBE_DEFER; in imx8qxp_ldb_parse_dt_companion()
567 "dual-link configuration detected (companion bridge %pOF)\n", in imx8qxp_ldb_parse_dt_companion()
570 companion_ldb_ch = bridge_to_ldb_ch(imx8qxp_ldb->companion); in imx8qxp_ldb_parse_dt_companion()
571 companion_ldb_ch->link_type = ldb_ch->link_type; in imx8qxp_ldb_parse_dt_companion()
580 struct device *dev = &pdev->dev; in imx8qxp_ldb_probe()
589 return -ENOMEM; in imx8qxp_ldb_probe()
591 imx8qxp_ldb->clk_pixel = devm_clk_get(dev, "pixel"); in imx8qxp_ldb_probe()
592 if (IS_ERR(imx8qxp_ldb->clk_pixel)) { in imx8qxp_ldb_probe()
593 ret = PTR_ERR(imx8qxp_ldb->clk_pixel); in imx8qxp_ldb_probe()
594 if (ret != -EPROBE_DEFER) in imx8qxp_ldb_probe()
600 imx8qxp_ldb->clk_bypass = devm_clk_get(dev, "bypass"); in imx8qxp_ldb_probe()
601 if (IS_ERR(imx8qxp_ldb->clk_bypass)) { in imx8qxp_ldb_probe()
602 ret = PTR_ERR(imx8qxp_ldb->clk_bypass); in imx8qxp_ldb_probe()
603 if (ret != -EPROBE_DEFER) in imx8qxp_ldb_probe()
609 imx8qxp_ldb->dev = dev; in imx8qxp_ldb_probe()
611 ldb = &imx8qxp_ldb->base; in imx8qxp_ldb_probe()
612 ldb->dev = dev; in imx8qxp_ldb_probe()
613 ldb->ctrl_reg = 0xe0; in imx8qxp_ldb_probe()
616 ldb->channel[i] = &imx8qxp_ldb->channel[i].base; in imx8qxp_ldb_probe()
622 if (ldb->available_ch_cnt == 0) { in imx8qxp_ldb_probe()
623 DRM_DEV_DEBUG_DRIVER(dev, "no available channel\n"); in imx8qxp_ldb_probe()
625 } else if (ldb->available_ch_cnt > 1) { in imx8qxp_ldb_probe()
626 DRM_DEV_ERROR(dev, "invalid available channel number(%u)\n", in imx8qxp_ldb_probe()
627 ldb->available_ch_cnt); in imx8qxp_ldb_probe()
628 return -EINVAL; in imx8qxp_ldb_probe()
632 imx8qxp_ldb_ch = &imx8qxp_ldb->channel[i]; in imx8qxp_ldb_probe()
633 ldb_ch = &imx8qxp_ldb_ch->base; in imx8qxp_ldb_probe()
635 if (ldb_ch->is_available) { in imx8qxp_ldb_probe()
636 imx8qxp_ldb->active_chno = ldb_ch->chno; in imx8qxp_ldb_probe()
641 imx8qxp_ldb_ch->phy = devm_of_phy_get(dev, ldb_ch->np, "lvds_phy"); in imx8qxp_ldb_probe()
642 if (IS_ERR(imx8qxp_ldb_ch->phy)) { in imx8qxp_ldb_probe()
643 ret = PTR_ERR(imx8qxp_ldb_ch->phy); in imx8qxp_ldb_probe()
644 if (ret != -EPROBE_DEFER) in imx8qxp_ldb_probe()
645 DRM_DEV_ERROR(dev, "failed to get channel%d PHY: %d\n", in imx8qxp_ldb_probe()
646 imx8qxp_ldb->active_chno, ret); in imx8qxp_ldb_probe()
673 struct ldb *ldb = &imx8qxp_ldb->base; in imx8qxp_ldb_remove()
677 pm_runtime_disable(&pdev->dev); in imx8qxp_ldb_remove()
690 struct ldb *ldb = &imx8qxp_ldb->base; in imx8qxp_ldb_runtime_resume()
693 regmap_write(ldb->regmap, ldb->ctrl_reg, 0); in imx8qxp_ldb_runtime_resume()
704 { .compatible = "fsl,imx8qxp-ldb" },