Lines Matching +full:hdmi +full:- +full:tx
1 // SPDX-License-Identifier: GPL-2.0-or-later
31 #define DRIVER_NAME "meson-dw-hdmi"
32 #define DRIVER_DESC "Amlogic Meson HDMI-TX DRM driver"
35 * DOC: HDMI Output
37 * HDMI Output is composed of :
39 * - A Synopsys DesignWare HDMI Controller IP
40 * - A TOP control block controlling the Clocks and PHY
41 * - A custom HDMI PHY in order convert video to TMDS signal
46 * | HDMI TOP |<= HPD
49 * | Synopsys HDMI | HDMI PHY |=> TMDS
54 * The HDMI TOP block only supports HPD sensing.
55 * The Synopsys HDMI Controller interrupt is routed
58 * HDMI Controller is done a pair of addr+read/write
60 * The HDMI PHY is configured by registers in the
64 * block and the VPU HDMI mux selects either the ENCI
70 * HDMI controller.
73 * HDMI TX IP version 2.01a with HDCP and I2C & S/PDIF
78 * - HPD Rise & Fall interrupt
79 * - HDMI Controller Interrupt
80 * - HDMI PHY Init for 480i to 1080p60
81 * - VENC & HDMI Clock setup for 480i to 1080p60
82 * - VENC Mode setup for 480i to 1080p60
86 * - PHY, Clock and Mode setup for 2k && 4k modes
87 * - SDDC Scrambling mode for HDMI 2.0a
88 * - HDCP Setup
89 * - CEC Management
145 struct dw_hdmi *hdmi; member
152 return of_device_is_compatible(dw_hdmi->dev->of_node, compat); in dw_hdmi_is_compatible()
166 writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); in dw_hdmi_top_read()
167 writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); in dw_hdmi_top_read()
170 data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG); in dw_hdmi_top_read()
171 data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG); in dw_hdmi_top_read()
181 return readl(dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2)); in dw_hdmi_g12a_top_read()
192 writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); in dw_hdmi_top_write()
193 writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); in dw_hdmi_top_write()
196 writel(data, dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG); in dw_hdmi_top_write()
204 writel(data, dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2)); in dw_hdmi_g12a_top_write()
213 unsigned int data = dw_hdmi->data->top_read(dw_hdmi, addr); in dw_hdmi_top_write_bits()
218 dw_hdmi->data->top_write(dw_hdmi, addr, data); in dw_hdmi_top_write_bits()
230 writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); in dw_hdmi_dwc_read()
231 writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); in dw_hdmi_dwc_read()
234 data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG); in dw_hdmi_dwc_read()
235 data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG); in dw_hdmi_dwc_read()
245 return readb(dw_hdmi->hdmitx + addr); in dw_hdmi_g12a_dwc_read()
256 writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); in dw_hdmi_dwc_write()
257 writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); in dw_hdmi_dwc_write()
260 writel(data, dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG); in dw_hdmi_dwc_write()
268 writeb(data, dw_hdmi->hdmitx + addr); in dw_hdmi_g12a_dwc_write()
277 unsigned int data = dw_hdmi->data->dwc_read(dw_hdmi, addr); in dw_hdmi_dwc_write_bits()
282 dw_hdmi->data->dwc_write(dw_hdmi, addr, data); in dw_hdmi_dwc_write_bits()
292 struct meson_drm *priv = dw_hdmi->priv; in meson_hdmi_phy_setup_mode()
293 unsigned int pixel_clock = mode->clock; in meson_hdmi_phy_setup_mode()
298 if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") || in meson_hdmi_phy_setup_mode()
299 dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi")) { in meson_hdmi_phy_setup_mode()
302 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x333d3282); in meson_hdmi_phy_setup_mode()
303 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2136315b); in meson_hdmi_phy_setup_mode()
306 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303382); in meson_hdmi_phy_setup_mode()
307 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2036315b); in meson_hdmi_phy_setup_mode()
310 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303362); in meson_hdmi_phy_setup_mode()
311 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2016315b); in meson_hdmi_phy_setup_mode()
314 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33604142); in meson_hdmi_phy_setup_mode()
315 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x0016315b); in meson_hdmi_phy_setup_mode()
318 "amlogic,meson-gxbb-dw-hdmi")) { in meson_hdmi_phy_setup_mode()
321 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33353245); in meson_hdmi_phy_setup_mode()
322 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2100115b); in meson_hdmi_phy_setup_mode()
325 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33634283); in meson_hdmi_phy_setup_mode()
326 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0xb000115b); in meson_hdmi_phy_setup_mode()
329 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33632122); in meson_hdmi_phy_setup_mode()
330 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2000115b); in meson_hdmi_phy_setup_mode()
333 "amlogic,meson-g12a-dw-hdmi")) { in meson_hdmi_phy_setup_mode()
336 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x37eb65c4); in meson_hdmi_phy_setup_mode()
337 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b); in meson_hdmi_phy_setup_mode()
338 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x0000080b); in meson_hdmi_phy_setup_mode()
341 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb6262); in meson_hdmi_phy_setup_mode()
342 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b); in meson_hdmi_phy_setup_mode()
343 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003); in meson_hdmi_phy_setup_mode()
346 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb4242); in meson_hdmi_phy_setup_mode()
347 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b); in meson_hdmi_phy_setup_mode()
348 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003); in meson_hdmi_phy_setup_mode()
355 struct meson_drm *priv = dw_hdmi->priv; in meson_dw_hdmi_phy_reset()
358 regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xf); in meson_dw_hdmi_phy_reset()
363 regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xe); in meson_dw_hdmi_phy_reset()
368 static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, in dw_hdmi_phy_init() argument
373 bool is_hdmi2_sink = display->hdmi.scdc.supported; in dw_hdmi_phy_init()
374 struct meson_drm *priv = dw_hdmi->priv; in dw_hdmi_phy_init()
376 readl_relaxed(priv->io_base + _REG(VPU_HDMI_SETTING)); in dw_hdmi_phy_init()
379 DRM_DEBUG_DRIVER("\"%s\" div%d\n", mode->name, in dw_hdmi_phy_init()
380 mode->clock > 340000 ? 40 : 10); in dw_hdmi_phy_init()
388 regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100); in dw_hdmi_phy_init()
391 regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0); in dw_hdmi_phy_init()
394 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_SW_RESET, 0); in dw_hdmi_phy_init()
405 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12)); in dw_hdmi_phy_init()
408 if (mode->clock > 340000 && !mode_is_420) { in dw_hdmi_phy_init()
409 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, in dw_hdmi_phy_init()
411 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, in dw_hdmi_phy_init()
414 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, in dw_hdmi_phy_init()
416 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, in dw_hdmi_phy_init()
421 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1); in dw_hdmi_phy_init()
423 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2); in dw_hdmi_phy_init()
429 regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, in dw_hdmi_phy_init()
433 if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") || in dw_hdmi_phy_init()
434 dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi") || in dw_hdmi_phy_init()
435 dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-g12a-dw-hdmi")) in dw_hdmi_phy_init()
436 regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, in dw_hdmi_phy_init()
439 regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, in dw_hdmi_phy_init()
443 regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0); in dw_hdmi_phy_init()
445 dw_hdmi_set_high_tmds_clock_ratio(hdmi, display); in dw_hdmi_phy_init()
455 if (priv->venc.hdmi_use_enci) in dw_hdmi_phy_init()
456 writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); in dw_hdmi_phy_init()
458 writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); in dw_hdmi_phy_init()
460 /* Temporary Disable HDMI video stream to HDMI-TX */ in dw_hdmi_phy_init()
462 priv->io_base + _REG(VPU_HDMI_SETTING)); in dw_hdmi_phy_init()
464 priv->io_base + _REG(VPU_HDMI_SETTING)); in dw_hdmi_phy_init()
466 /* Re-Enable VENC video stream */ in dw_hdmi_phy_init()
467 if (priv->venc.hdmi_use_enci) in dw_hdmi_phy_init()
468 writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN)); in dw_hdmi_phy_init()
470 writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN)); in dw_hdmi_phy_init()
472 /* Push back HDMI clock settings */ in dw_hdmi_phy_init()
474 priv->io_base + _REG(VPU_HDMI_SETTING)); in dw_hdmi_phy_init()
476 /* Enable and Select HDMI video source for HDMI-TX */ in dw_hdmi_phy_init()
477 if (priv->venc.hdmi_use_enci) in dw_hdmi_phy_init()
479 priv->io_base + _REG(VPU_HDMI_SETTING)); in dw_hdmi_phy_init()
482 priv->io_base + _REG(VPU_HDMI_SETTING)); in dw_hdmi_phy_init()
487 static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, in dw_hdmi_phy_disable() argument
491 struct meson_drm *priv = dw_hdmi->priv; in dw_hdmi_phy_disable()
495 regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0); in dw_hdmi_phy_disable()
498 static enum drm_connector_status dw_hdmi_read_hpd(struct dw_hdmi *hdmi, in dw_hdmi_read_hpd() argument
503 return !!dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_STAT0) ? in dw_hdmi_read_hpd()
507 static void dw_hdmi_setup_hpd(struct dw_hdmi *hdmi, in dw_hdmi_setup_hpd() argument
513 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_HPD_FILTER, in dw_hdmi_setup_hpd()
517 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, in dw_hdmi_setup_hpd()
538 stat = dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_INTR_STAT); in dw_hdmi_top_irq()
539 dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, stat); in dw_hdmi_top_irq()
543 dw_hdmi->irq_stat = stat; in dw_hdmi_top_irq()
547 /* HDMI Controller Interrupt */ in dw_hdmi_top_irq()
560 u32 stat = dw_hdmi->irq_stat; in dw_hdmi_top_thread_irq()
569 dw_hdmi_setup_rx_sense(dw_hdmi->hdmi, hpd_connected, in dw_hdmi_top_thread_irq()
572 drm_helper_hpd_irq_event(dw_hdmi->bridge->dev); in dw_hdmi_top_thread_irq()
573 drm_bridge_hpd_notify(dw_hdmi->bridge, in dw_hdmi_top_thread_irq()
581 /* DW HDMI Regmap */
588 *result = dw_hdmi->data->dwc_read(dw_hdmi, reg); in meson_dw_hdmi_reg_read()
599 dw_hdmi->data->dwc_write(dw_hdmi, reg, val); in meson_dw_hdmi_reg_write()
629 struct meson_drm *priv = meson_dw_hdmi->priv; in meson_dw_hdmi_init()
632 regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100); in meson_dw_hdmi_init()
635 regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0); in meson_dw_hdmi_init()
637 /* Reset HDMITX APB & TX & PHY */ in meson_dw_hdmi_init()
638 reset_control_reset(meson_dw_hdmi->hdmitx_apb); in meson_dw_hdmi_init()
639 reset_control_reset(meson_dw_hdmi->hdmitx_ctrl); in meson_dw_hdmi_init()
640 reset_control_reset(meson_dw_hdmi->hdmitx_phy); in meson_dw_hdmi_init()
645 meson_dw_hdmi->hdmitx + HDMITX_TOP_CTRL_REG); in meson_dw_hdmi_init()
647 meson_dw_hdmi->hdmitx + HDMITX_DWC_CTRL_REG); in meson_dw_hdmi_init()
651 meson_dw_hdmi->data->top_write(meson_dw_hdmi, in meson_dw_hdmi_init()
656 meson_dw_hdmi->data->top_write(meson_dw_hdmi, in meson_dw_hdmi_init()
659 /* Enable HDMI-TX Interrupt */ in meson_dw_hdmi_init()
660 meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, in meson_dw_hdmi_init()
663 meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_MASKN, in meson_dw_hdmi_init()
703 struct meson_drm *priv = drm->dev_private; in meson_dw_hdmi_bind()
710 match = of_device_get_match_data(&pdev->dev); in meson_dw_hdmi_bind()
712 dev_err(&pdev->dev, "failed to get match data\n"); in meson_dw_hdmi_bind()
713 return -ENODEV; in meson_dw_hdmi_bind()
719 return -ENOMEM; in meson_dw_hdmi_bind()
721 meson_dw_hdmi->priv = priv; in meson_dw_hdmi_bind()
722 meson_dw_hdmi->dev = dev; in meson_dw_hdmi_bind()
723 meson_dw_hdmi->data = match; in meson_dw_hdmi_bind()
724 dw_plat_data = &meson_dw_hdmi->dw_plat_data; in meson_dw_hdmi_bind()
726 meson_dw_hdmi->hdmi_supply = devm_regulator_get_optional(dev, "hdmi"); in meson_dw_hdmi_bind()
727 if (IS_ERR(meson_dw_hdmi->hdmi_supply)) { in meson_dw_hdmi_bind()
728 if (PTR_ERR(meson_dw_hdmi->hdmi_supply) == -EPROBE_DEFER) in meson_dw_hdmi_bind()
729 return -EPROBE_DEFER; in meson_dw_hdmi_bind()
730 meson_dw_hdmi->hdmi_supply = NULL; in meson_dw_hdmi_bind()
732 ret = regulator_enable(meson_dw_hdmi->hdmi_supply); in meson_dw_hdmi_bind()
736 meson_dw_hdmi->hdmi_supply); in meson_dw_hdmi_bind()
741 meson_dw_hdmi->hdmitx_apb = devm_reset_control_get_exclusive(dev, in meson_dw_hdmi_bind()
743 if (IS_ERR(meson_dw_hdmi->hdmitx_apb)) { in meson_dw_hdmi_bind()
745 return PTR_ERR(meson_dw_hdmi->hdmitx_apb); in meson_dw_hdmi_bind()
748 meson_dw_hdmi->hdmitx_ctrl = devm_reset_control_get_exclusive(dev, in meson_dw_hdmi_bind()
750 if (IS_ERR(meson_dw_hdmi->hdmitx_ctrl)) { in meson_dw_hdmi_bind()
752 return PTR_ERR(meson_dw_hdmi->hdmitx_ctrl); in meson_dw_hdmi_bind()
755 meson_dw_hdmi->hdmitx_phy = devm_reset_control_get_exclusive(dev, in meson_dw_hdmi_bind()
757 if (IS_ERR(meson_dw_hdmi->hdmitx_phy)) { in meson_dw_hdmi_bind()
759 return PTR_ERR(meson_dw_hdmi->hdmitx_phy); in meson_dw_hdmi_bind()
762 meson_dw_hdmi->hdmitx = devm_platform_ioremap_resource(pdev, 0); in meson_dw_hdmi_bind()
763 if (IS_ERR(meson_dw_hdmi->hdmitx)) in meson_dw_hdmi_bind()
764 return PTR_ERR(meson_dw_hdmi->hdmitx); in meson_dw_hdmi_bind()
778 dw_plat_data->regm = devm_regmap_init(dev, NULL, meson_dw_hdmi, in meson_dw_hdmi_bind()
780 if (IS_ERR(dw_plat_data->regm)) in meson_dw_hdmi_bind()
781 return PTR_ERR(dw_plat_data->regm); in meson_dw_hdmi_bind()
791 dev_err(dev, "Failed to request hdmi top irq\n"); in meson_dw_hdmi_bind()
799 dw_plat_data->priv_data = meson_dw_hdmi; in meson_dw_hdmi_bind()
800 dw_plat_data->phy_ops = &meson_dw_hdmi_phy_ops; in meson_dw_hdmi_bind()
801 dw_plat_data->phy_name = "meson_dw_hdmi_phy"; in meson_dw_hdmi_bind()
802 dw_plat_data->phy_data = meson_dw_hdmi; in meson_dw_hdmi_bind()
803 dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709; in meson_dw_hdmi_bind()
804 dw_plat_data->ycbcr_420_allowed = true; in meson_dw_hdmi_bind()
805 dw_plat_data->disable_cec = true; in meson_dw_hdmi_bind()
806 dw_plat_data->output_port = 1; in meson_dw_hdmi_bind()
808 if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") || in meson_dw_hdmi_bind()
809 dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") || in meson_dw_hdmi_bind()
810 dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-g12a-dw-hdmi")) in meson_dw_hdmi_bind()
811 dw_plat_data->use_drm_infoframe = true; in meson_dw_hdmi_bind()
815 meson_dw_hdmi->hdmi = dw_hdmi_probe(pdev, &meson_dw_hdmi->dw_plat_data); in meson_dw_hdmi_bind()
816 if (IS_ERR(meson_dw_hdmi->hdmi)) in meson_dw_hdmi_bind()
817 return PTR_ERR(meson_dw_hdmi->hdmi); in meson_dw_hdmi_bind()
819 meson_dw_hdmi->bridge = of_drm_find_bridge(pdev->dev.of_node); in meson_dw_hdmi_bind()
821 DRM_DEBUG_DRIVER("HDMI controller initialized\n"); in meson_dw_hdmi_bind()
831 dw_hdmi_unbind(meson_dw_hdmi->hdmi); in meson_dw_hdmi_unbind()
847 meson_dw_hdmi->data->top_write(meson_dw_hdmi, in meson_dw_hdmi_pm_suspend()
862 dw_hdmi_resume(meson_dw_hdmi->hdmi); in meson_dw_hdmi_pm_resume()
869 return component_add(&pdev->dev, &meson_dw_hdmi_ops); in meson_dw_hdmi_probe()
874 component_del(&pdev->dev, &meson_dw_hdmi_ops); in meson_dw_hdmi_remove()
885 { .compatible = "amlogic,meson-gxbb-dw-hdmi",
887 { .compatible = "amlogic,meson-gxl-dw-hdmi",
889 { .compatible = "amlogic,meson-gxm-dw-hdmi",
891 { .compatible = "amlogic,meson-g12a-dw-hdmi",