Lines Matching +full:imx8qxp +full:- +full:mipi +full:- +full:dphy
1 // SPDX-License-Identifier: GPL-2.0+
9 #include <linux/clk-provider.h>
22 #include <dt-bindings/firmware/imx/rsrc.h>
35 /* DPHY registers */
63 ((x) < 32) ? 0xe0 | ((x) - 16) : \
64 ((x) < 64) ? 0xc0 | ((x) - 32) : \
65 ((x) < 128) ? 0x80 | ((x) - 64) : \
66 ((x) - 128))
67 #define CN(x) (((x) == 1) ? 0x1f : (((CN_BUF) >> ((x) - 1)) & 0x1f))
68 #define CO(x) ((CO_BUF) >> (8 - (x)) & 0x03)
91 bool is_combo; /* MIPI DPHY and LVDS PHY combo */
109 /* DPHY PLL parameters */
113 /* DPHY register values */
139 .name = "mipi-dphy",
147 ret = regmap_write(priv->regmap, reg, value); in phy_write()
149 dev_err(&phy->dev, "Failed to write DPHY reg %d: %d\n", reg, in phy_write()
178 c = a - (b * whole); in get_best_ratio()
190 struct mixel_dphy_priv *priv = dev_get_drvdata(phy->dev.parent); in mixel_dphy_config_from_opts()
191 unsigned long ref_clk = clk_get_rate(priv->phy_ref_clk); in mixel_dphy_config_from_opts()
197 if (dphy_opts->hs_clk_rate > DATA_RATE_MAX_SPEED || in mixel_dphy_config_from_opts()
198 dphy_opts->hs_clk_rate < DATA_RATE_MIN_SPEED) in mixel_dphy_config_from_opts()
199 return -EINVAL; in mixel_dphy_config_from_opts()
201 numerator = dphy_opts->hs_clk_rate; in mixel_dphy_config_from_opts()
205 dev_err(&phy->dev, "Invalid %d/%d for %ld/%ld\n", in mixel_dphy_config_from_opts()
207 dphy_opts->hs_clk_rate, ref_clk); in mixel_dphy_config_from_opts()
208 return -EINVAL; in mixel_dphy_config_from_opts()
223 cfg->cn = denominator >> i; in mixel_dphy_config_from_opts()
224 cfg->co = 1 << i; in mixel_dphy_config_from_opts()
225 cfg->cm = numerator; in mixel_dphy_config_from_opts()
227 if (cfg->cm < 16 || cfg->cm > 255 || in mixel_dphy_config_from_opts()
228 cfg->cn < 1 || cfg->cn > 32 || in mixel_dphy_config_from_opts()
229 cfg->co < 1 || cfg->co > 8) { in mixel_dphy_config_from_opts()
230 dev_err(&phy->dev, "Invalid CM/CN/CO values: %u/%u/%u\n", in mixel_dphy_config_from_opts()
231 cfg->cm, cfg->cn, cfg->co); in mixel_dphy_config_from_opts()
232 dev_err(&phy->dev, "for hs_clk/ref_clk=%ld/%ld ~ %d/%d\n", in mixel_dphy_config_from_opts()
233 dphy_opts->hs_clk_rate, ref_clk, in mixel_dphy_config_from_opts()
235 return -EINVAL; in mixel_dphy_config_from_opts()
238 dev_dbg(&phy->dev, "hs_clk/ref_clk=%ld/%ld ~ %d/%d\n", in mixel_dphy_config_from_opts()
239 dphy_opts->hs_clk_rate, ref_clk, numerator, denominator); in mixel_dphy_config_from_opts()
243 do_div(tmp, dphy_opts->lp_clk_rate); /* ps */ in mixel_dphy_config_from_opts()
245 return -EINVAL; in mixel_dphy_config_from_opts()
248 dev_dbg(&phy->dev, "LP clock %lu, period: %u ps\n", in mixel_dphy_config_from_opts()
249 dphy_opts->lp_clk_rate, lp_t); in mixel_dphy_config_from_opts()
252 if (2 * dphy_opts->hs_prepare > 5 * lp_t) { in mixel_dphy_config_from_opts()
253 dev_err(&phy->dev, in mixel_dphy_config_from_opts()
255 dphy_opts->hs_prepare, lp_t); in mixel_dphy_config_from_opts()
256 return -EINVAL; in mixel_dphy_config_from_opts()
259 if (dphy_opts->hs_prepare < lp_t) { in mixel_dphy_config_from_opts()
262 tmp = 2 * (dphy_opts->hs_prepare - lp_t); in mixel_dphy_config_from_opts()
266 cfg->m_prg_hs_prepare = n; in mixel_dphy_config_from_opts()
269 if (2 * dphy_opts->clk_prepare > 3 * lp_t) { in mixel_dphy_config_from_opts()
270 dev_err(&phy->dev, in mixel_dphy_config_from_opts()
272 dphy_opts->clk_prepare, lp_t); in mixel_dphy_config_from_opts()
273 return -EINVAL; in mixel_dphy_config_from_opts()
276 cfg->mc_prg_hs_prepare = dphy_opts->clk_prepare > lp_t ? 1 : 0; in mixel_dphy_config_from_opts()
279 n = (144 * (dphy_opts->hs_clk_rate / 1000000) - 47500) / 10000; in mixel_dphy_config_from_opts()
280 cfg->m_prg_hs_zero = n < 1 ? 1 : n; in mixel_dphy_config_from_opts()
283 n = (34 * (dphy_opts->hs_clk_rate / 1000000) - 2500) / 1000; in mixel_dphy_config_from_opts()
284 cfg->mc_prg_hs_zero = n < 1 ? 1 : n; in mixel_dphy_config_from_opts()
287 n = (103 * (dphy_opts->hs_clk_rate / 1000000) + 10000) / 10000; in mixel_dphy_config_from_opts()
292 cfg->m_prg_hs_trail = n; in mixel_dphy_config_from_opts()
293 cfg->mc_prg_hs_trail = n; in mixel_dphy_config_from_opts()
296 if (dphy_opts->hs_clk_rate < MBPS(80)) in mixel_dphy_config_from_opts()
297 cfg->rxhs_settle = 0x0d; in mixel_dphy_config_from_opts()
298 else if (dphy_opts->hs_clk_rate < MBPS(90)) in mixel_dphy_config_from_opts()
299 cfg->rxhs_settle = 0x0c; in mixel_dphy_config_from_opts()
300 else if (dphy_opts->hs_clk_rate < MBPS(125)) in mixel_dphy_config_from_opts()
301 cfg->rxhs_settle = 0x0b; in mixel_dphy_config_from_opts()
302 else if (dphy_opts->hs_clk_rate < MBPS(150)) in mixel_dphy_config_from_opts()
303 cfg->rxhs_settle = 0x0a; in mixel_dphy_config_from_opts()
304 else if (dphy_opts->hs_clk_rate < MBPS(225)) in mixel_dphy_config_from_opts()
305 cfg->rxhs_settle = 0x09; in mixel_dphy_config_from_opts()
306 else if (dphy_opts->hs_clk_rate < MBPS(500)) in mixel_dphy_config_from_opts()
307 cfg->rxhs_settle = 0x08; in mixel_dphy_config_from_opts()
309 cfg->rxhs_settle = 0x07; in mixel_dphy_config_from_opts()
311 dev_dbg(&phy->dev, "phy_config: %u %u %u %u %u %u %u\n", in mixel_dphy_config_from_opts()
312 cfg->m_prg_hs_prepare, cfg->mc_prg_hs_prepare, in mixel_dphy_config_from_opts()
313 cfg->m_prg_hs_zero, cfg->mc_prg_hs_zero, in mixel_dphy_config_from_opts()
314 cfg->m_prg_hs_trail, cfg->mc_prg_hs_trail, in mixel_dphy_config_from_opts()
315 cfg->rxhs_settle); in mixel_dphy_config_from_opts()
324 phy_write(phy, priv->cfg.m_prg_hs_prepare, DPHY_M_PRG_HS_PREPARE); in mixel_phy_set_hs_timings()
325 phy_write(phy, priv->cfg.mc_prg_hs_prepare, DPHY_MC_PRG_HS_PREPARE); in mixel_phy_set_hs_timings()
326 phy_write(phy, priv->cfg.m_prg_hs_zero, DPHY_M_PRG_HS_ZERO); in mixel_phy_set_hs_timings()
327 phy_write(phy, priv->cfg.mc_prg_hs_zero, DPHY_MC_PRG_HS_ZERO); in mixel_phy_set_hs_timings()
328 phy_write(phy, priv->cfg.m_prg_hs_trail, DPHY_M_PRG_HS_TRAIL); in mixel_phy_set_hs_timings()
329 phy_write(phy, priv->cfg.mc_prg_hs_trail, DPHY_MC_PRG_HS_TRAIL); in mixel_phy_set_hs_timings()
330 phy_write(phy, priv->cfg.rxhs_settle, priv->devdata->reg_rxhs_settle); in mixel_phy_set_hs_timings()
335 struct mixel_dphy_priv *priv = dev_get_drvdata(phy->dev.parent); in mixel_dphy_set_pll_params()
337 if (priv->cfg.cm < 16 || priv->cfg.cm > 255 || in mixel_dphy_set_pll_params()
338 priv->cfg.cn < 1 || priv->cfg.cn > 32 || in mixel_dphy_set_pll_params()
339 priv->cfg.co < 1 || priv->cfg.co > 8) { in mixel_dphy_set_pll_params()
340 dev_err(&phy->dev, "Invalid CM/CN/CO values! (%u/%u/%u)\n", in mixel_dphy_set_pll_params()
341 priv->cfg.cm, priv->cfg.cn, priv->cfg.co); in mixel_dphy_set_pll_params()
342 return -EINVAL; in mixel_dphy_set_pll_params()
344 dev_dbg(&phy->dev, "Using CM:%u CN:%u CO:%u\n", in mixel_dphy_set_pll_params()
345 priv->cfg.cm, priv->cfg.cn, priv->cfg.co); in mixel_dphy_set_pll_params()
346 phy_write(phy, CM(priv->cfg.cm), DPHY_CM); in mixel_dphy_set_pll_params()
347 phy_write(phy, CN(priv->cfg.cn), DPHY_CN); in mixel_dphy_set_pll_params()
348 phy_write(phy, CO(priv->cfg.co), DPHY_CO); in mixel_dphy_set_pll_params()
359 ret = mixel_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); in mixel_dphy_configure_mipi_dphy()
364 memcpy(&priv->cfg, &cfg, sizeof(struct mixel_dphy_cfg)); in mixel_dphy_configure_mipi_dphy()
367 phy_write(phy, 0x01, priv->devdata->reg_tx_rcal); in mixel_dphy_configure_mipi_dphy()
368 phy_write(phy, 0x00, priv->devdata->reg_auto_pd_en); in mixel_dphy_configure_mipi_dphy()
369 phy_write(phy, 0x02, priv->devdata->reg_rxlprp); in mixel_dphy_configure_mipi_dphy()
370 phy_write(phy, 0x02, priv->devdata->reg_rxcdrp); in mixel_dphy_configure_mipi_dphy()
385 struct phy_configure_opts_lvds *lvds_opts = &opts->lvds; in mixel_dphy_configure_lvds_phy()
392 priv->is_slave = lvds_opts->is_slave; in mixel_dphy_configure_lvds_phy()
395 regmap_write(priv->lvds_regmap, PHY_CTRL, in mixel_dphy_configure_lvds_phy()
399 rsc = priv->id ? IMX_SC_R_MIPI_1 : IMX_SC_R_MIPI_0; in mixel_dphy_configure_lvds_phy()
400 ret = imx_sc_misc_set_control(priv->ipc_handle, rsc, IMX_SC_C_DUAL_MODE, in mixel_dphy_configure_lvds_phy()
401 lvds_opts->is_slave); in mixel_dphy_configure_lvds_phy()
403 dev_err(&phy->dev, "Failed to configure MODE8: %d\n", ret); in mixel_dphy_configure_lvds_phy()
411 * ----- 640MHz ~ 1500MHz ------------ --------------- in mixel_dphy_configure_lvds_phy()
412 * | VCO | ----------------> | CO divider | -> | LVDS data rate| in mixel_dphy_configure_lvds_phy()
413 * ----- FVCO ------------ --------------- in mixel_dphy_configure_lvds_phy()
416 data_rate = 7 * lvds_opts->differential_clk_rate; in mixel_dphy_configure_lvds_phy()
425 dev_err(&phy->dev, "VCO frequency %lu is out of range\n", fvco); in mixel_dphy_configure_lvds_phy()
426 return -ERANGE; in mixel_dphy_configure_lvds_phy()
436 clk_set_rate(priv->phy_ref_clk, lvds_opts->differential_clk_rate); in mixel_dphy_configure_lvds_phy()
444 dev_err(&phy->dev, "No configuration options\n"); in mixel_dphy_configure()
445 return -EINVAL; in mixel_dphy_configure()
448 if (phy->attrs.mode == PHY_MODE_MIPI_DPHY) in mixel_dphy_configure()
450 else if (phy->attrs.mode == PHY_MODE_LVDS) in mixel_dphy_configure()
453 dev_err(&phy->dev, in mixel_dphy_configure()
454 "Failed to configure PHY with invalid PHY mode: %d\n", phy->attrs.mode); in mixel_dphy_configure()
456 return -EINVAL; in mixel_dphy_configure()
462 struct phy_configure_opts_lvds *lvds_cfg = &opts->lvds; in mixel_dphy_validate_lvds_phy()
464 if (lvds_cfg->bits_per_lane_and_dclk_cycle != 7) { in mixel_dphy_validate_lvds_phy()
465 dev_err(&phy->dev, "Invalid bits per LVDS data lane: %u\n", in mixel_dphy_validate_lvds_phy()
466 lvds_cfg->bits_per_lane_and_dclk_cycle); in mixel_dphy_validate_lvds_phy()
467 return -EINVAL; in mixel_dphy_validate_lvds_phy()
470 if (lvds_cfg->lanes != 4) { in mixel_dphy_validate_lvds_phy()
471 dev_err(&phy->dev, "Invalid LVDS data lanes: %u\n", lvds_cfg->lanes); in mixel_dphy_validate_lvds_phy()
472 return -EINVAL; in mixel_dphy_validate_lvds_phy()
475 if (lvds_cfg->differential_clk_rate < MIN_LVDS_REFCLK_FREQ || in mixel_dphy_validate_lvds_phy()
476 lvds_cfg->differential_clk_rate > MAX_LVDS_REFCLK_FREQ) { in mixel_dphy_validate_lvds_phy()
477 dev_err(&phy->dev, in mixel_dphy_validate_lvds_phy()
479 lvds_cfg->differential_clk_rate); in mixel_dphy_validate_lvds_phy()
480 return -EINVAL; in mixel_dphy_validate_lvds_phy()
492 return mixel_dphy_config_from_opts(phy, &opts->mipi_dphy, in mixel_dphy_validate()
498 dev_err(&phy->dev, in mixel_dphy_validate()
500 return -EINVAL; in mixel_dphy_validate()
527 ret = regmap_read_poll_timeout(priv->regmap, DPHY_LOCK, locked, in mixel_dphy_power_on_mipi_dphy()
531 dev_err(&phy->dev, "Could not get DPHY lock (%d)!\n", ret); in mixel_dphy_power_on_mipi_dphy()
545 regmap_update_bits(priv->lvds_regmap, PHY_CTRL, LVDS_EN, LVDS_EN); in mixel_dphy_power_on_lvds_phy()
551 if (priv->is_slave) in mixel_dphy_power_on_lvds_phy()
554 ret = regmap_read_poll_timeout(priv->regmap, DPHY_LOCK, locked, in mixel_dphy_power_on_lvds_phy()
558 dev_err(&phy->dev, "Could not get LVDS PHY lock (%d)!\n", ret); in mixel_dphy_power_on_lvds_phy()
570 ret = clk_prepare_enable(priv->phy_ref_clk); in mixel_dphy_power_on()
574 if (phy->attrs.mode == PHY_MODE_MIPI_DPHY) { in mixel_dphy_power_on()
576 } else if (phy->attrs.mode == PHY_MODE_LVDS) { in mixel_dphy_power_on()
579 dev_err(&phy->dev, in mixel_dphy_power_on()
581 phy->attrs.mode); in mixel_dphy_power_on()
582 ret = -EINVAL; in mixel_dphy_power_on()
590 clk_disable_unprepare(priv->phy_ref_clk); in mixel_dphy_power_on()
601 if (phy->attrs.mode == PHY_MODE_LVDS) in mixel_dphy_power_off()
602 regmap_update_bits(priv->lvds_regmap, PHY_CTRL, LVDS_EN, 0); in mixel_dphy_power_off()
604 clk_disable_unprepare(priv->phy_ref_clk); in mixel_dphy_power_off()
614 if (priv->devdata->is_combo && mode != PHY_MODE_LVDS) { in mixel_dphy_set_mode()
615 dev_err(&phy->dev, "Failed to set PHY mode for combo PHY\n"); in mixel_dphy_set_mode()
616 return -EINVAL; in mixel_dphy_set_mode()
619 if (!priv->devdata->is_combo && mode != PHY_MODE_MIPI_DPHY) { in mixel_dphy_set_mode()
620 dev_err(&phy->dev, "Failed to set PHY mode to MIPI DPHY\n"); in mixel_dphy_set_mode()
621 return -EINVAL; in mixel_dphy_set_mode()
624 if (priv->devdata->is_combo) { in mixel_dphy_set_mode()
625 u32 rsc = priv->id ? IMX_SC_R_MIPI_1 : IMX_SC_R_MIPI_0; in mixel_dphy_set_mode()
627 ret = imx_sc_misc_set_control(priv->ipc_handle, in mixel_dphy_set_mode()
631 dev_err(&phy->dev, in mixel_dphy_set_mode()
652 { .compatible = "fsl,imx8mq-mipi-dphy",
654 { .compatible = "fsl,imx8qxp-mipi-dphy",
662 struct device *dev = &pdev->dev; in mixel_dphy_probe()
663 struct device_node *np = dev->of_node; in mixel_dphy_probe()
671 return -ENODEV; in mixel_dphy_probe()
675 return -ENOMEM; in mixel_dphy_probe()
677 priv->devdata = of_device_get_match_data(&pdev->dev); in mixel_dphy_probe()
678 if (!priv->devdata) in mixel_dphy_probe()
679 return -EINVAL; in mixel_dphy_probe()
685 priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, in mixel_dphy_probe()
687 if (IS_ERR(priv->regmap)) { in mixel_dphy_probe()
688 dev_err(dev, "Couldn't create the DPHY regmap\n"); in mixel_dphy_probe()
689 return PTR_ERR(priv->regmap); in mixel_dphy_probe()
692 priv->phy_ref_clk = devm_clk_get(&pdev->dev, "phy_ref"); in mixel_dphy_probe()
693 if (IS_ERR(priv->phy_ref_clk)) { in mixel_dphy_probe()
695 return PTR_ERR(priv->phy_ref_clk); in mixel_dphy_probe()
698 clk_get_rate(priv->phy_ref_clk)); in mixel_dphy_probe()
700 if (priv->devdata->is_combo) { in mixel_dphy_probe()
701 priv->lvds_regmap = in mixel_dphy_probe()
703 if (IS_ERR(priv->lvds_regmap)) { in mixel_dphy_probe()
704 ret = PTR_ERR(priv->lvds_regmap); in mixel_dphy_probe()
709 priv->id = of_alias_get_id(np, "mipi-dphy"); in mixel_dphy_probe()
710 if (priv->id < 0) { in mixel_dphy_probe()
712 priv->id); in mixel_dphy_probe()
713 return priv->id; in mixel_dphy_probe()
716 ret = imx_scu_get_handle(&priv->ipc_handle); in mixel_dphy_probe()
741 .name = "mixel-mipi-dphy",
748 MODULE_DESCRIPTION("Mixel MIPI-DSI PHY driver");