xref: /openbmc/linux/drivers/gpu/drm/meson/meson_dw_hdmi.c (revision e0d77d0f38aa60ca61b3ce6e60d64fad2aa0853d)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23f68be7dSNeil Armstrong /*
33f68be7dSNeil Armstrong  * Copyright (C) 2016 BayLibre, SAS
43f68be7dSNeil Armstrong  * Author: Neil Armstrong <narmstrong@baylibre.com>
53f68be7dSNeil Armstrong  * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
63f68be7dSNeil Armstrong  */
73f68be7dSNeil Armstrong 
866620f48SSam Ravnborg #include <linux/clk.h>
966620f48SSam Ravnborg #include <linux/component.h>
103f68be7dSNeil Armstrong #include <linux/kernel.h>
113f68be7dSNeil Armstrong #include <linux/module.h>
12722d4f06SRob Herring #include <linux/of.h>
133f68be7dSNeil Armstrong #include <linux/of_graph.h>
14722d4f06SRob Herring #include <linux/platform_device.h>
15161a803fSNeil Armstrong #include <linux/regulator/consumer.h>
1666620f48SSam Ravnborg #include <linux/reset.h>
173f68be7dSNeil Armstrong 
1866620f48SSam Ravnborg #include <drm/bridge/dw_hdmi.h>
193f68be7dSNeil Armstrong #include <drm/drm_atomic_helper.h>
20f43aa584SNeil Armstrong #include <drm/drm_bridge.h>
2166620f48SSam Ravnborg #include <drm/drm_device.h>
22fcd70cd3SDaniel Vetter #include <drm/drm_edid.h>
23fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
2466620f48SSam Ravnborg #include <drm/drm_print.h>
253f68be7dSNeil Armstrong 
2666620f48SSam Ravnborg #include <linux/videodev2.h>
273f68be7dSNeil Armstrong 
283f68be7dSNeil Armstrong #include "meson_drv.h"
293f68be7dSNeil Armstrong #include "meson_dw_hdmi.h"
303f68be7dSNeil Armstrong #include "meson_registers.h"
313f68be7dSNeil Armstrong 
323f68be7dSNeil Armstrong #define DRIVER_NAME "meson-dw-hdmi"
333f68be7dSNeil Armstrong #define DRIVER_DESC "Amlogic Meson HDMI-TX DRM driver"
343f68be7dSNeil Armstrong 
352021d5b7SNeil Armstrong /**
362021d5b7SNeil Armstrong  * DOC: HDMI Output
372021d5b7SNeil Armstrong  *
383f68be7dSNeil Armstrong  * HDMI Output is composed of :
392021d5b7SNeil Armstrong  *
403f68be7dSNeil Armstrong  * - A Synopsys DesignWare HDMI Controller IP
413f68be7dSNeil Armstrong  * - A TOP control block controlling the Clocks and PHY
423f68be7dSNeil Armstrong  * - A custom HDMI PHY in order convert video to TMDS signal
432021d5b7SNeil Armstrong  *
442021d5b7SNeil Armstrong  * .. code::
452021d5b7SNeil Armstrong  *
463f68be7dSNeil Armstrong  *    ___________________________________
473f68be7dSNeil Armstrong  *   |            HDMI TOP               |<= HPD
483f68be7dSNeil Armstrong  *   |___________________________________|
493f68be7dSNeil Armstrong  *   |                  |                |
503f68be7dSNeil Armstrong  *   |  Synopsys HDMI   |   HDMI PHY     |=> TMDS
513f68be7dSNeil Armstrong  *   |    Controller    |________________|
523f68be7dSNeil Armstrong  *   |___________________________________|<=> DDC
533f68be7dSNeil Armstrong  *
542021d5b7SNeil Armstrong  *
553f68be7dSNeil Armstrong  * The HDMI TOP block only supports HPD sensing.
563f68be7dSNeil Armstrong  * The Synopsys HDMI Controller interrupt is routed
573f68be7dSNeil Armstrong  * through the TOP Block interrupt.
583f68be7dSNeil Armstrong  * Communication to the TOP Block and the Synopsys
593f68be7dSNeil Armstrong  * HDMI Controller is done a pair of addr+read/write
603f68be7dSNeil Armstrong  * registers.
613f68be7dSNeil Armstrong  * The HDMI PHY is configured by registers in the
623f68be7dSNeil Armstrong  * HHI register block.
633f68be7dSNeil Armstrong  *
643f68be7dSNeil Armstrong  * Pixel data arrives in 4:4:4 format from the VENC
653f68be7dSNeil Armstrong  * block and the VPU HDMI mux selects either the ENCI
663f68be7dSNeil Armstrong  * encoder for the 576i or 480i formats or the ENCP
673f68be7dSNeil Armstrong  * encoder for all the other formats including
683f68be7dSNeil Armstrong  * interlaced HD formats.
693f68be7dSNeil Armstrong  * The VENC uses a DVI encoder on top of the ENCI
703f68be7dSNeil Armstrong  * or ENCP encoders to generate DVI timings for the
713f68be7dSNeil Armstrong  * HDMI controller.
723f68be7dSNeil Armstrong  *
733f68be7dSNeil Armstrong  * GXBB, GXL and GXM embeds the Synopsys DesignWare
743f68be7dSNeil Armstrong  * HDMI TX IP version 2.01a with HDCP and I2C & S/PDIF
753f68be7dSNeil Armstrong  * audio source interfaces.
763f68be7dSNeil Armstrong  *
773f68be7dSNeil Armstrong  * We handle the following features :
782021d5b7SNeil Armstrong  *
793f68be7dSNeil Armstrong  * - HPD Rise & Fall interrupt
803f68be7dSNeil Armstrong  * - HDMI Controller Interrupt
813f68be7dSNeil Armstrong  * - HDMI PHY Init for 480i to 1080p60
823f68be7dSNeil Armstrong  * - VENC & HDMI Clock setup for 480i to 1080p60
833f68be7dSNeil Armstrong  * - VENC Mode setup for 480i to 1080p60
843f68be7dSNeil Armstrong  *
853f68be7dSNeil Armstrong  * What is missing :
862021d5b7SNeil Armstrong  *
873f68be7dSNeil Armstrong  * - PHY, Clock and Mode setup for 2k && 4k modes
883f68be7dSNeil Armstrong  * - SDDC Scrambling mode for HDMI 2.0a
893f68be7dSNeil Armstrong  * - HDCP Setup
903f68be7dSNeil Armstrong  * - CEC Management
913f68be7dSNeil Armstrong  */
923f68be7dSNeil Armstrong 
933f68be7dSNeil Armstrong /* TOP Block Communication Channel */
943f68be7dSNeil Armstrong #define HDMITX_TOP_ADDR_REG	0x0
953f68be7dSNeil Armstrong #define HDMITX_TOP_DATA_REG	0x4
963f68be7dSNeil Armstrong #define HDMITX_TOP_CTRL_REG	0x8
973b7c1237SNeil Armstrong #define HDMITX_TOP_G12A_OFFSET	0x8000
983f68be7dSNeil Armstrong 
993f68be7dSNeil Armstrong /* Controller Communication Channel */
1003f68be7dSNeil Armstrong #define HDMITX_DWC_ADDR_REG	0x10
1013f68be7dSNeil Armstrong #define HDMITX_DWC_DATA_REG	0x14
1023f68be7dSNeil Armstrong #define HDMITX_DWC_CTRL_REG	0x18
1033f68be7dSNeil Armstrong 
1043f68be7dSNeil Armstrong /* HHI Registers */
1053f68be7dSNeil Armstrong #define HHI_MEM_PD_REG0		0x100 /* 0x40 */
1063f68be7dSNeil Armstrong #define HHI_HDMI_CLK_CNTL	0x1cc /* 0x73 */
1073f68be7dSNeil Armstrong #define HHI_HDMI_PHY_CNTL0	0x3a0 /* 0xe8 */
1083f68be7dSNeil Armstrong #define HHI_HDMI_PHY_CNTL1	0x3a4 /* 0xe9 */
109*fa2d2e2dSJerome Brunet #define  PHY_CNTL1_INIT		0x03900000
110*fa2d2e2dSJerome Brunet #define  PHY_INVERT		BIT(17)
1113f68be7dSNeil Armstrong #define HHI_HDMI_PHY_CNTL2	0x3a8 /* 0xea */
1123f68be7dSNeil Armstrong #define HHI_HDMI_PHY_CNTL3	0x3ac /* 0xeb */
1133b7c1237SNeil Armstrong #define HHI_HDMI_PHY_CNTL4	0x3b0 /* 0xec */
1143b7c1237SNeil Armstrong #define HHI_HDMI_PHY_CNTL5	0x3b4 /* 0xed */
1153f68be7dSNeil Armstrong 
1163f68be7dSNeil Armstrong static DEFINE_SPINLOCK(reg_lock);
1173f68be7dSNeil Armstrong 
1183f68be7dSNeil Armstrong enum meson_venc_source {
1193f68be7dSNeil Armstrong 	MESON_VENC_SOURCE_NONE = 0,
1203f68be7dSNeil Armstrong 	MESON_VENC_SOURCE_ENCI = 1,
1213f68be7dSNeil Armstrong 	MESON_VENC_SOURCE_ENCP = 2,
1223f68be7dSNeil Armstrong };
1233f68be7dSNeil Armstrong 
1243b7c1237SNeil Armstrong struct meson_dw_hdmi;
1253b7c1237SNeil Armstrong 
1263b7c1237SNeil Armstrong struct meson_dw_hdmi_data {
1273b7c1237SNeil Armstrong 	unsigned int	(*top_read)(struct meson_dw_hdmi *dw_hdmi,
1283b7c1237SNeil Armstrong 				    unsigned int addr);
1293b7c1237SNeil Armstrong 	void		(*top_write)(struct meson_dw_hdmi *dw_hdmi,
1303b7c1237SNeil Armstrong 				     unsigned int addr, unsigned int data);
1313b7c1237SNeil Armstrong 	unsigned int	(*dwc_read)(struct meson_dw_hdmi *dw_hdmi,
1323b7c1237SNeil Armstrong 				    unsigned int addr);
1333b7c1237SNeil Armstrong 	void		(*dwc_write)(struct meson_dw_hdmi *dw_hdmi,
1343b7c1237SNeil Armstrong 				     unsigned int addr, unsigned int data);
135*fa2d2e2dSJerome Brunet 	u32 cntl0_init;
136*fa2d2e2dSJerome Brunet 	u32 cntl1_init;
1373b7c1237SNeil Armstrong };
1383b7c1237SNeil Armstrong 
1393f68be7dSNeil Armstrong struct meson_dw_hdmi {
1403f68be7dSNeil Armstrong 	struct dw_hdmi_plat_data dw_plat_data;
1413f68be7dSNeil Armstrong 	struct meson_drm *priv;
1423f68be7dSNeil Armstrong 	struct device *dev;
1433f68be7dSNeil Armstrong 	void __iomem *hdmitx;
1443b7c1237SNeil Armstrong 	const struct meson_dw_hdmi_data *data;
1453f68be7dSNeil Armstrong 	struct reset_control *hdmitx_apb;
1463f68be7dSNeil Armstrong 	struct reset_control *hdmitx_ctrl;
1473f68be7dSNeil Armstrong 	struct reset_control *hdmitx_phy;
1483f68be7dSNeil Armstrong 	u32 irq_stat;
149eea034afSJernej Skrabec 	struct dw_hdmi *hdmi;
150e67f6037SNeil Armstrong 	struct drm_bridge *bridge;
1513f68be7dSNeil Armstrong };
1523f68be7dSNeil Armstrong 
dw_hdmi_is_compatible(struct meson_dw_hdmi * dw_hdmi,const char * compat)1533f68be7dSNeil Armstrong static inline int dw_hdmi_is_compatible(struct meson_dw_hdmi *dw_hdmi,
1543f68be7dSNeil Armstrong 					const char *compat)
1553f68be7dSNeil Armstrong {
1563f68be7dSNeil Armstrong 	return of_device_is_compatible(dw_hdmi->dev->of_node, compat);
1573f68be7dSNeil Armstrong }
1583f68be7dSNeil Armstrong 
1593f68be7dSNeil Armstrong /* PHY (via TOP bridge) and Controller dedicated register interface */
1603f68be7dSNeil Armstrong 
dw_hdmi_top_read(struct meson_dw_hdmi * dw_hdmi,unsigned int addr)1613f68be7dSNeil Armstrong static unsigned int dw_hdmi_top_read(struct meson_dw_hdmi *dw_hdmi,
1623f68be7dSNeil Armstrong 				     unsigned int addr)
1633f68be7dSNeil Armstrong {
1643f68be7dSNeil Armstrong 	unsigned long flags;
1653f68be7dSNeil Armstrong 	unsigned int data;
1663f68be7dSNeil Armstrong 
1673f68be7dSNeil Armstrong 	spin_lock_irqsave(&reg_lock, flags);
1683f68be7dSNeil Armstrong 
1693f68be7dSNeil Armstrong 	/* ADDR must be written twice */
1703f68be7dSNeil Armstrong 	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
1713f68be7dSNeil Armstrong 	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
1723f68be7dSNeil Armstrong 
1733f68be7dSNeil Armstrong 	/* Read needs a second DATA read */
1743f68be7dSNeil Armstrong 	data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG);
1753f68be7dSNeil Armstrong 	data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG);
1763f68be7dSNeil Armstrong 
1773f68be7dSNeil Armstrong 	spin_unlock_irqrestore(&reg_lock, flags);
1783f68be7dSNeil Armstrong 
1793f68be7dSNeil Armstrong 	return data;
1803f68be7dSNeil Armstrong }
1813f68be7dSNeil Armstrong 
dw_hdmi_g12a_top_read(struct meson_dw_hdmi * dw_hdmi,unsigned int addr)1823b7c1237SNeil Armstrong static unsigned int dw_hdmi_g12a_top_read(struct meson_dw_hdmi *dw_hdmi,
1833b7c1237SNeil Armstrong 					  unsigned int addr)
1843b7c1237SNeil Armstrong {
1853b7c1237SNeil Armstrong 	return readl(dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2));
1863b7c1237SNeil Armstrong }
1873b7c1237SNeil Armstrong 
dw_hdmi_top_write(struct meson_dw_hdmi * dw_hdmi,unsigned int addr,unsigned int data)1883f68be7dSNeil Armstrong static inline void dw_hdmi_top_write(struct meson_dw_hdmi *dw_hdmi,
1893f68be7dSNeil Armstrong 				     unsigned int addr, unsigned int data)
1903f68be7dSNeil Armstrong {
1913f68be7dSNeil Armstrong 	unsigned long flags;
1923f68be7dSNeil Armstrong 
1933f68be7dSNeil Armstrong 	spin_lock_irqsave(&reg_lock, flags);
1943f68be7dSNeil Armstrong 
1953f68be7dSNeil Armstrong 	/* ADDR must be written twice */
1963f68be7dSNeil Armstrong 	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
1973f68be7dSNeil Armstrong 	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
1983f68be7dSNeil Armstrong 
1993f68be7dSNeil Armstrong 	/* Write needs single DATA write */
2003f68be7dSNeil Armstrong 	writel(data, dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG);
2013f68be7dSNeil Armstrong 
2023f68be7dSNeil Armstrong 	spin_unlock_irqrestore(&reg_lock, flags);
2033f68be7dSNeil Armstrong }
2043f68be7dSNeil Armstrong 
dw_hdmi_g12a_top_write(struct meson_dw_hdmi * dw_hdmi,unsigned int addr,unsigned int data)2053b7c1237SNeil Armstrong static inline void dw_hdmi_g12a_top_write(struct meson_dw_hdmi *dw_hdmi,
2063b7c1237SNeil Armstrong 					  unsigned int addr, unsigned int data)
2073b7c1237SNeil Armstrong {
2083b7c1237SNeil Armstrong 	writel(data, dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2));
2093b7c1237SNeil Armstrong }
2103b7c1237SNeil Armstrong 
2113f68be7dSNeil Armstrong /* Helper to change specific bits in PHY registers */
dw_hdmi_top_write_bits(struct meson_dw_hdmi * dw_hdmi,unsigned int addr,unsigned int mask,unsigned int val)2123f68be7dSNeil Armstrong static inline void dw_hdmi_top_write_bits(struct meson_dw_hdmi *dw_hdmi,
2133f68be7dSNeil Armstrong 					  unsigned int addr,
2143f68be7dSNeil Armstrong 					  unsigned int mask,
2153f68be7dSNeil Armstrong 					  unsigned int val)
2163f68be7dSNeil Armstrong {
2173b7c1237SNeil Armstrong 	unsigned int data = dw_hdmi->data->top_read(dw_hdmi, addr);
2183f68be7dSNeil Armstrong 
2193f68be7dSNeil Armstrong 	data &= ~mask;
2203f68be7dSNeil Armstrong 	data |= val;
2213f68be7dSNeil Armstrong 
2223b7c1237SNeil Armstrong 	dw_hdmi->data->top_write(dw_hdmi, addr, data);
2233f68be7dSNeil Armstrong }
2243f68be7dSNeil Armstrong 
dw_hdmi_dwc_read(struct meson_dw_hdmi * dw_hdmi,unsigned int addr)2253f68be7dSNeil Armstrong static unsigned int dw_hdmi_dwc_read(struct meson_dw_hdmi *dw_hdmi,
2263f68be7dSNeil Armstrong 				     unsigned int addr)
2273f68be7dSNeil Armstrong {
2283f68be7dSNeil Armstrong 	unsigned long flags;
2293f68be7dSNeil Armstrong 	unsigned int data;
2303f68be7dSNeil Armstrong 
2313f68be7dSNeil Armstrong 	spin_lock_irqsave(&reg_lock, flags);
2323f68be7dSNeil Armstrong 
2333f68be7dSNeil Armstrong 	/* ADDR must be written twice */
2343f68be7dSNeil Armstrong 	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
2353f68be7dSNeil Armstrong 	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
2363f68be7dSNeil Armstrong 
2373f68be7dSNeil Armstrong 	/* Read needs a second DATA read */
2383f68be7dSNeil Armstrong 	data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG);
2393f68be7dSNeil Armstrong 	data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG);
2403f68be7dSNeil Armstrong 
2413f68be7dSNeil Armstrong 	spin_unlock_irqrestore(&reg_lock, flags);
2423f68be7dSNeil Armstrong 
2433f68be7dSNeil Armstrong 	return data;
2443f68be7dSNeil Armstrong }
2453f68be7dSNeil Armstrong 
dw_hdmi_g12a_dwc_read(struct meson_dw_hdmi * dw_hdmi,unsigned int addr)2463b7c1237SNeil Armstrong static unsigned int dw_hdmi_g12a_dwc_read(struct meson_dw_hdmi *dw_hdmi,
2473b7c1237SNeil Armstrong 					  unsigned int addr)
2483b7c1237SNeil Armstrong {
2493b7c1237SNeil Armstrong 	return readb(dw_hdmi->hdmitx + addr);
2503b7c1237SNeil Armstrong }
2513b7c1237SNeil Armstrong 
dw_hdmi_dwc_write(struct meson_dw_hdmi * dw_hdmi,unsigned int addr,unsigned int data)2523f68be7dSNeil Armstrong static inline void dw_hdmi_dwc_write(struct meson_dw_hdmi *dw_hdmi,
2533f68be7dSNeil Armstrong 				     unsigned int addr, unsigned int data)
2543f68be7dSNeil Armstrong {
2553f68be7dSNeil Armstrong 	unsigned long flags;
2563f68be7dSNeil Armstrong 
2573f68be7dSNeil Armstrong 	spin_lock_irqsave(&reg_lock, flags);
2583f68be7dSNeil Armstrong 
2593f68be7dSNeil Armstrong 	/* ADDR must be written twice */
2603f68be7dSNeil Armstrong 	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
2613f68be7dSNeil Armstrong 	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
2623f68be7dSNeil Armstrong 
2633f68be7dSNeil Armstrong 	/* Write needs single DATA write */
2643f68be7dSNeil Armstrong 	writel(data, dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG);
2653f68be7dSNeil Armstrong 
2663f68be7dSNeil Armstrong 	spin_unlock_irqrestore(&reg_lock, flags);
2673f68be7dSNeil Armstrong }
2683f68be7dSNeil Armstrong 
dw_hdmi_g12a_dwc_write(struct meson_dw_hdmi * dw_hdmi,unsigned int addr,unsigned int data)2693b7c1237SNeil Armstrong static inline void dw_hdmi_g12a_dwc_write(struct meson_dw_hdmi *dw_hdmi,
2703b7c1237SNeil Armstrong 					  unsigned int addr, unsigned int data)
2713b7c1237SNeil Armstrong {
2723b7c1237SNeil Armstrong 	writeb(data, dw_hdmi->hdmitx + addr);
2733b7c1237SNeil Armstrong }
2743b7c1237SNeil Armstrong 
2753f68be7dSNeil Armstrong /* Helper to change specific bits in controller registers */
dw_hdmi_dwc_write_bits(struct meson_dw_hdmi * dw_hdmi,unsigned int addr,unsigned int mask,unsigned int val)2763f68be7dSNeil Armstrong static inline void dw_hdmi_dwc_write_bits(struct meson_dw_hdmi *dw_hdmi,
2773f68be7dSNeil Armstrong 					  unsigned int addr,
2783f68be7dSNeil Armstrong 					  unsigned int mask,
2793f68be7dSNeil Armstrong 					  unsigned int val)
2803f68be7dSNeil Armstrong {
2813b7c1237SNeil Armstrong 	unsigned int data = dw_hdmi->data->dwc_read(dw_hdmi, addr);
2823f68be7dSNeil Armstrong 
2833f68be7dSNeil Armstrong 	data &= ~mask;
2843f68be7dSNeil Armstrong 	data |= val;
2853f68be7dSNeil Armstrong 
2863b7c1237SNeil Armstrong 	dw_hdmi->data->dwc_write(dw_hdmi, addr, data);
2873f68be7dSNeil Armstrong }
2883f68be7dSNeil Armstrong 
2893f68be7dSNeil Armstrong /* Bridge */
2903f68be7dSNeil Armstrong 
2913f68be7dSNeil Armstrong /* Setup PHY bandwidth modes */
meson_hdmi_phy_setup_mode(struct meson_dw_hdmi * dw_hdmi,const struct drm_display_mode * mode,bool mode_is_420)2923f68be7dSNeil Armstrong static void meson_hdmi_phy_setup_mode(struct meson_dw_hdmi *dw_hdmi,
293e67f6037SNeil Armstrong 				      const struct drm_display_mode *mode,
294e67f6037SNeil Armstrong 				      bool mode_is_420)
2953f68be7dSNeil Armstrong {
2963f68be7dSNeil Armstrong 	struct meson_drm *priv = dw_hdmi->priv;
2973f68be7dSNeil Armstrong 	unsigned int pixel_clock = mode->clock;
2983f68be7dSNeil Armstrong 
2998496a217SNeil Armstrong 	/* For 420, pixel clock is half unlike venc clock */
300e67f6037SNeil Armstrong 	if (mode_is_420) pixel_clock /= 2;
3018496a217SNeil Armstrong 
3023f68be7dSNeil Armstrong 	if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
3033f68be7dSNeil Armstrong 	    dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi")) {
3043f68be7dSNeil Armstrong 		if (pixel_clock >= 371250) {
3053f68be7dSNeil Armstrong 			/* 5.94Gbps, 3.7125Gbps */
3063f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x333d3282);
3073f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2136315b);
3083f68be7dSNeil Armstrong 		} else if (pixel_clock >= 297000) {
3093f68be7dSNeil Armstrong 			/* 2.97Gbps */
3103f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303382);
3113f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2036315b);
3123f68be7dSNeil Armstrong 		} else if (pixel_clock >= 148500) {
3133f68be7dSNeil Armstrong 			/* 1.485Gbps */
3143f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303362);
3153f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2016315b);
3163f68be7dSNeil Armstrong 		} else {
3173f68be7dSNeil Armstrong 			/* 742.5Mbps, and below */
3183f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33604142);
3193f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x0016315b);
3203f68be7dSNeil Armstrong 		}
3213f68be7dSNeil Armstrong 	} else if (dw_hdmi_is_compatible(dw_hdmi,
3223f68be7dSNeil Armstrong 					 "amlogic,meson-gxbb-dw-hdmi")) {
3233f68be7dSNeil Armstrong 		if (pixel_clock >= 371250) {
3243f68be7dSNeil Armstrong 			/* 5.94Gbps, 3.7125Gbps */
3253f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33353245);
3263f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2100115b);
3273f68be7dSNeil Armstrong 		} else if (pixel_clock >= 297000) {
3283f68be7dSNeil Armstrong 			/* 2.97Gbps */
3293f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33634283);
3303f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0xb000115b);
3313f68be7dSNeil Armstrong 		} else {
3323f68be7dSNeil Armstrong 			/* 1.485Gbps, and below */
3333f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33632122);
3343f68be7dSNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2000115b);
3353f68be7dSNeil Armstrong 		}
3363b7c1237SNeil Armstrong 	} else if (dw_hdmi_is_compatible(dw_hdmi,
3373b7c1237SNeil Armstrong 					 "amlogic,meson-g12a-dw-hdmi")) {
3383b7c1237SNeil Armstrong 		if (pixel_clock >= 371250) {
3393b7c1237SNeil Armstrong 			/* 5.94Gbps, 3.7125Gbps */
3403b7c1237SNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x37eb65c4);
3413b7c1237SNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
3423b7c1237SNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x0000080b);
3433b7c1237SNeil Armstrong 		} else if (pixel_clock >= 297000) {
3443b7c1237SNeil Armstrong 			/* 2.97Gbps */
3453b7c1237SNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb6262);
3463b7c1237SNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
3473b7c1237SNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003);
3483b7c1237SNeil Armstrong 		} else {
3493b7c1237SNeil Armstrong 			/* 1.485Gbps, and below */
3503b7c1237SNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb4242);
3513b7c1237SNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
3523b7c1237SNeil Armstrong 			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003);
3533b7c1237SNeil Armstrong 		}
3543f68be7dSNeil Armstrong 	}
3553f68be7dSNeil Armstrong }
3563f68be7dSNeil Armstrong 
meson_dw_hdmi_phy_reset(struct meson_dw_hdmi * dw_hdmi)3575765916aSJernej Skrabec static inline void meson_dw_hdmi_phy_reset(struct meson_dw_hdmi *dw_hdmi)
3583f68be7dSNeil Armstrong {
3593f68be7dSNeil Armstrong 	struct meson_drm *priv = dw_hdmi->priv;
3603f68be7dSNeil Armstrong 
3613f68be7dSNeil Armstrong 	/* Enable and software reset */
3623f68be7dSNeil Armstrong 	regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xf);
3633f68be7dSNeil Armstrong 
3643f68be7dSNeil Armstrong 	mdelay(2);
3653f68be7dSNeil Armstrong 
3663f68be7dSNeil Armstrong 	/* Enable and unreset */
3673f68be7dSNeil Armstrong 	regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xe);
3683f68be7dSNeil Armstrong 
3693f68be7dSNeil Armstrong 	mdelay(2);
3703f68be7dSNeil Armstrong }
3713f68be7dSNeil Armstrong 
dw_hdmi_phy_init(struct dw_hdmi * hdmi,void * data,const struct drm_display_info * display,const struct drm_display_mode * mode)3723f68be7dSNeil Armstrong static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
3737be390d4SLaurent Pinchart 			    const struct drm_display_info *display,
37435a395f1SLaurent Pinchart 			    const struct drm_display_mode *mode)
3753f68be7dSNeil Armstrong {
3763f68be7dSNeil Armstrong 	struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
377e67f6037SNeil Armstrong 	bool is_hdmi2_sink = display->hdmi.scdc.supported;
3783f68be7dSNeil Armstrong 	struct meson_drm *priv = dw_hdmi->priv;
3793f68be7dSNeil Armstrong 	unsigned int wr_clk =
3803f68be7dSNeil Armstrong 		readl_relaxed(priv->io_base + _REG(VPU_HDMI_SETTING));
381e67f6037SNeil Armstrong 	bool mode_is_420 = false;
3823f68be7dSNeil Armstrong 
383d7d8fb70SNeil Armstrong 	DRM_DEBUG_DRIVER("\"%s\" div%d\n", mode->name,
384d7d8fb70SNeil Armstrong 			 mode->clock > 340000 ? 40 : 10);
3853f68be7dSNeil Armstrong 
386e67f6037SNeil Armstrong 	if (drm_mode_is_420_only(display, mode) ||
3877ed40ff1SAdrián Larumbe 	    (!is_hdmi2_sink && drm_mode_is_420_also(display, mode)) ||
3887ed40ff1SAdrián Larumbe 	    dw_hdmi_bus_fmt_is_420(hdmi))
389e67f6037SNeil Armstrong 		mode_is_420 = true;
390e67f6037SNeil Armstrong 
3918496a217SNeil Armstrong 	/* TMDS pattern setup */
392e67f6037SNeil Armstrong 	if (mode->clock > 340000 && !mode_is_420) {
3933b7c1237SNeil Armstrong 		dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
3943b7c1237SNeil Armstrong 				  0);
3953b7c1237SNeil Armstrong 		dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
396d7d8fb70SNeil Armstrong 				  0x03ff03ff);
397d7d8fb70SNeil Armstrong 	} else {
3983b7c1237SNeil Armstrong 		dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
399d7d8fb70SNeil Armstrong 				  0x001f001f);
4003b7c1237SNeil Armstrong 		dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
401d7d8fb70SNeil Armstrong 				  0x001f001f);
402d7d8fb70SNeil Armstrong 	}
4033f68be7dSNeil Armstrong 
4043f68be7dSNeil Armstrong 	/* Load TMDS pattern */
4053b7c1237SNeil Armstrong 	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1);
4063f68be7dSNeil Armstrong 	msleep(20);
4073b7c1237SNeil Armstrong 	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2);
4083f68be7dSNeil Armstrong 
4093f68be7dSNeil Armstrong 	/* Setup PHY parameters */
410e67f6037SNeil Armstrong 	meson_hdmi_phy_setup_mode(dw_hdmi, mode, mode_is_420);
4113f68be7dSNeil Armstrong 
4123f68be7dSNeil Armstrong 	/* Disable clock, fifo, fifo_wr */
4133f68be7dSNeil Armstrong 	regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0);
4143f68be7dSNeil Armstrong 
4157be390d4SLaurent Pinchart 	dw_hdmi_set_high_tmds_clock_ratio(hdmi, display);
416d7d8fb70SNeil Armstrong 
4173f68be7dSNeil Armstrong 	msleep(100);
4183f68be7dSNeil Armstrong 
4193f68be7dSNeil Armstrong 	/* Reset PHY 3 times in a row */
4205765916aSJernej Skrabec 	meson_dw_hdmi_phy_reset(dw_hdmi);
4215765916aSJernej Skrabec 	meson_dw_hdmi_phy_reset(dw_hdmi);
4225765916aSJernej Skrabec 	meson_dw_hdmi_phy_reset(dw_hdmi);
4233f68be7dSNeil Armstrong 
4243f68be7dSNeil Armstrong 	/* Temporary Disable VENC video stream */
4253f68be7dSNeil Armstrong 	if (priv->venc.hdmi_use_enci)
4263f68be7dSNeil Armstrong 		writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN));
4273f68be7dSNeil Armstrong 	else
4283f68be7dSNeil Armstrong 		writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
4293f68be7dSNeil Armstrong 
4303f68be7dSNeil Armstrong 	/* Temporary Disable HDMI video stream to HDMI-TX */
4313f68be7dSNeil Armstrong 	writel_bits_relaxed(0x3, 0,
4323f68be7dSNeil Armstrong 			    priv->io_base + _REG(VPU_HDMI_SETTING));
4333f68be7dSNeil Armstrong 	writel_bits_relaxed(0xf << 8, 0,
4343f68be7dSNeil Armstrong 			    priv->io_base + _REG(VPU_HDMI_SETTING));
4353f68be7dSNeil Armstrong 
4363f68be7dSNeil Armstrong 	/* Re-Enable VENC video stream */
4373f68be7dSNeil Armstrong 	if (priv->venc.hdmi_use_enci)
4383f68be7dSNeil Armstrong 		writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN));
4393f68be7dSNeil Armstrong 	else
4403f68be7dSNeil Armstrong 		writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN));
4413f68be7dSNeil Armstrong 
4423f68be7dSNeil Armstrong 	/* Push back HDMI clock settings */
4433f68be7dSNeil Armstrong 	writel_bits_relaxed(0xf << 8, wr_clk & (0xf << 8),
4443f68be7dSNeil Armstrong 			    priv->io_base + _REG(VPU_HDMI_SETTING));
4453f68be7dSNeil Armstrong 
4463f68be7dSNeil Armstrong 	/* Enable and Select HDMI video source for HDMI-TX */
4473f68be7dSNeil Armstrong 	if (priv->venc.hdmi_use_enci)
4483f68be7dSNeil Armstrong 		writel_bits_relaxed(0x3, MESON_VENC_SOURCE_ENCI,
4493f68be7dSNeil Armstrong 				    priv->io_base + _REG(VPU_HDMI_SETTING));
4503f68be7dSNeil Armstrong 	else
4513f68be7dSNeil Armstrong 		writel_bits_relaxed(0x3, MESON_VENC_SOURCE_ENCP,
4523f68be7dSNeil Armstrong 				    priv->io_base + _REG(VPU_HDMI_SETTING));
4533f68be7dSNeil Armstrong 
4543f68be7dSNeil Armstrong 	return 0;
4553f68be7dSNeil Armstrong }
4563f68be7dSNeil Armstrong 
dw_hdmi_phy_disable(struct dw_hdmi * hdmi,void * data)4573f68be7dSNeil Armstrong static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi,
4583f68be7dSNeil Armstrong 				void *data)
4593f68be7dSNeil Armstrong {
4603f68be7dSNeil Armstrong 	struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
4613f68be7dSNeil Armstrong 	struct meson_drm *priv = dw_hdmi->priv;
4623f68be7dSNeil Armstrong 
4633f68be7dSNeil Armstrong 	DRM_DEBUG_DRIVER("\n");
4643f68be7dSNeil Armstrong 
465*fa2d2e2dSJerome Brunet 	/* Fallback to init mode */
466*fa2d2e2dSJerome Brunet 	regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, dw_hdmi->data->cntl1_init);
467*fa2d2e2dSJerome Brunet 	regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, dw_hdmi->data->cntl0_init);
4683f68be7dSNeil Armstrong }
4693f68be7dSNeil Armstrong 
dw_hdmi_read_hpd(struct dw_hdmi * hdmi,void * data)4703f68be7dSNeil Armstrong static enum drm_connector_status dw_hdmi_read_hpd(struct dw_hdmi *hdmi,
4713f68be7dSNeil Armstrong 			     void *data)
4723f68be7dSNeil Armstrong {
4733f68be7dSNeil Armstrong 	struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
4743f68be7dSNeil Armstrong 
4753b7c1237SNeil Armstrong 	return !!dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_STAT0) ?
4763f68be7dSNeil Armstrong 		connector_status_connected : connector_status_disconnected;
4773f68be7dSNeil Armstrong }
4783f68be7dSNeil Armstrong 
dw_hdmi_setup_hpd(struct dw_hdmi * hdmi,void * data)4793f68be7dSNeil Armstrong static void dw_hdmi_setup_hpd(struct dw_hdmi *hdmi,
4803f68be7dSNeil Armstrong 			      void *data)
4813f68be7dSNeil Armstrong {
4823f68be7dSNeil Armstrong 	struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
4833f68be7dSNeil Armstrong 
4843f68be7dSNeil Armstrong 	/* Setup HPD Filter */
4853b7c1237SNeil Armstrong 	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_HPD_FILTER,
4863f68be7dSNeil Armstrong 			  (0xa << 12) | 0xa0);
4873f68be7dSNeil Armstrong 
4883f68be7dSNeil Armstrong 	/* Clear interrupts */
4893b7c1237SNeil Armstrong 	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
4903f68be7dSNeil Armstrong 			  HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL);
4913f68be7dSNeil Armstrong 
4923f68be7dSNeil Armstrong 	/* Unmask interrupts */
4933f68be7dSNeil Armstrong 	dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_INTR_MASKN,
4943f68be7dSNeil Armstrong 			HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL,
4953f68be7dSNeil Armstrong 			HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL);
4963f68be7dSNeil Armstrong }
4973f68be7dSNeil Armstrong 
4983f68be7dSNeil Armstrong static const struct dw_hdmi_phy_ops meson_dw_hdmi_phy_ops = {
4993f68be7dSNeil Armstrong 	.init = dw_hdmi_phy_init,
5003f68be7dSNeil Armstrong 	.disable = dw_hdmi_phy_disable,
5013f68be7dSNeil Armstrong 	.read_hpd = dw_hdmi_read_hpd,
5023f68be7dSNeil Armstrong 	.setup_hpd = dw_hdmi_setup_hpd,
5033f68be7dSNeil Armstrong };
5043f68be7dSNeil Armstrong 
dw_hdmi_top_irq(int irq,void * dev_id)5053f68be7dSNeil Armstrong static irqreturn_t dw_hdmi_top_irq(int irq, void *dev_id)
5063f68be7dSNeil Armstrong {
5073f68be7dSNeil Armstrong 	struct meson_dw_hdmi *dw_hdmi = dev_id;
5083f68be7dSNeil Armstrong 	u32 stat;
5093f68be7dSNeil Armstrong 
5103b7c1237SNeil Armstrong 	stat = dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_INTR_STAT);
5113b7c1237SNeil Armstrong 	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, stat);
5123f68be7dSNeil Armstrong 
5133f68be7dSNeil Armstrong 	/* HPD Events, handle in the threaded interrupt handler */
5143f68be7dSNeil Armstrong 	if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) {
5153f68be7dSNeil Armstrong 		dw_hdmi->irq_stat = stat;
5163f68be7dSNeil Armstrong 		return IRQ_WAKE_THREAD;
5173f68be7dSNeil Armstrong 	}
5183f68be7dSNeil Armstrong 
5193f68be7dSNeil Armstrong 	/* HDMI Controller Interrupt */
5203f68be7dSNeil Armstrong 	if (stat & 1)
5213f68be7dSNeil Armstrong 		return IRQ_NONE;
5223f68be7dSNeil Armstrong 
5233f68be7dSNeil Armstrong 	/* TOFIX Handle HDCP Interrupts */
5243f68be7dSNeil Armstrong 
5253f68be7dSNeil Armstrong 	return IRQ_HANDLED;
5263f68be7dSNeil Armstrong }
5273f68be7dSNeil Armstrong 
5283f68be7dSNeil Armstrong /* Threaded interrupt handler to manage HPD events */
dw_hdmi_top_thread_irq(int irq,void * dev_id)5293f68be7dSNeil Armstrong static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id)
5303f68be7dSNeil Armstrong {
5313f68be7dSNeil Armstrong 	struct meson_dw_hdmi *dw_hdmi = dev_id;
5323f68be7dSNeil Armstrong 	u32 stat = dw_hdmi->irq_stat;
5333f68be7dSNeil Armstrong 
5343f68be7dSNeil Armstrong 	/* HPD Events */
5353f68be7dSNeil Armstrong 	if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) {
5363f68be7dSNeil Armstrong 		bool hpd_connected = false;
5373f68be7dSNeil Armstrong 
5383f68be7dSNeil Armstrong 		if (stat & HDMITX_TOP_INTR_HPD_RISE)
5393f68be7dSNeil Armstrong 			hpd_connected = true;
5403f68be7dSNeil Armstrong 
541c32048d9SNeil Armstrong 		dw_hdmi_setup_rx_sense(dw_hdmi->hdmi, hpd_connected,
5423f68be7dSNeil Armstrong 				       hpd_connected);
5433f68be7dSNeil Armstrong 
544e67f6037SNeil Armstrong 		drm_helper_hpd_irq_event(dw_hdmi->bridge->dev);
545e67f6037SNeil Armstrong 		drm_bridge_hpd_notify(dw_hdmi->bridge,
546e67f6037SNeil Armstrong 				      hpd_connected ? connector_status_connected
547e67f6037SNeil Armstrong 						    : connector_status_disconnected);
5483f68be7dSNeil Armstrong 	}
5493f68be7dSNeil Armstrong 
5503f68be7dSNeil Armstrong 	return IRQ_HANDLED;
5513f68be7dSNeil Armstrong }
5523f68be7dSNeil Armstrong 
5533f68be7dSNeil Armstrong /* DW HDMI Regmap */
5543f68be7dSNeil Armstrong 
meson_dw_hdmi_reg_read(void * context,unsigned int reg,unsigned int * result)5553f68be7dSNeil Armstrong static int meson_dw_hdmi_reg_read(void *context, unsigned int reg,
5563f68be7dSNeil Armstrong 				  unsigned int *result)
5573f68be7dSNeil Armstrong {
5583b7c1237SNeil Armstrong 	struct meson_dw_hdmi *dw_hdmi = context;
5593b7c1237SNeil Armstrong 
5603b7c1237SNeil Armstrong 	*result = dw_hdmi->data->dwc_read(dw_hdmi, reg);
5613f68be7dSNeil Armstrong 
5623f68be7dSNeil Armstrong 	return 0;
5633f68be7dSNeil Armstrong 
5643f68be7dSNeil Armstrong }
5653f68be7dSNeil Armstrong 
meson_dw_hdmi_reg_write(void * context,unsigned int reg,unsigned int val)5663f68be7dSNeil Armstrong static int meson_dw_hdmi_reg_write(void *context, unsigned int reg,
5673f68be7dSNeil Armstrong 				   unsigned int val)
5683f68be7dSNeil Armstrong {
5693b7c1237SNeil Armstrong 	struct meson_dw_hdmi *dw_hdmi = context;
5703b7c1237SNeil Armstrong 
5713b7c1237SNeil Armstrong 	dw_hdmi->data->dwc_write(dw_hdmi, reg, val);
5723f68be7dSNeil Armstrong 
5733f68be7dSNeil Armstrong 	return 0;
5743f68be7dSNeil Armstrong }
5753f68be7dSNeil Armstrong 
5763f68be7dSNeil Armstrong static const struct regmap_config meson_dw_hdmi_regmap_config = {
5773f68be7dSNeil Armstrong 	.reg_bits = 32,
5783f68be7dSNeil Armstrong 	.val_bits = 8,
5793f68be7dSNeil Armstrong 	.reg_read = meson_dw_hdmi_reg_read,
5803f68be7dSNeil Armstrong 	.reg_write = meson_dw_hdmi_reg_write,
5813f68be7dSNeil Armstrong 	.max_register = 0x10000,
582995b278eSLyude Paul 	.fast_io = true,
5833f68be7dSNeil Armstrong };
5843f68be7dSNeil Armstrong 
585*fa2d2e2dSJerome Brunet static const struct meson_dw_hdmi_data meson_dw_hdmi_gxbb_data = {
5863b7c1237SNeil Armstrong 	.top_read = dw_hdmi_top_read,
5873b7c1237SNeil Armstrong 	.top_write = dw_hdmi_top_write,
5883b7c1237SNeil Armstrong 	.dwc_read = dw_hdmi_dwc_read,
5893b7c1237SNeil Armstrong 	.dwc_write = dw_hdmi_dwc_write,
590*fa2d2e2dSJerome Brunet 	.cntl0_init = 0x0,
591*fa2d2e2dSJerome Brunet 	.cntl1_init = PHY_CNTL1_INIT | PHY_INVERT,
592*fa2d2e2dSJerome Brunet };
593*fa2d2e2dSJerome Brunet 
594*fa2d2e2dSJerome Brunet static const struct meson_dw_hdmi_data meson_dw_hdmi_gxl_data = {
595*fa2d2e2dSJerome Brunet 	.top_read = dw_hdmi_top_read,
596*fa2d2e2dSJerome Brunet 	.top_write = dw_hdmi_top_write,
597*fa2d2e2dSJerome Brunet 	.dwc_read = dw_hdmi_dwc_read,
598*fa2d2e2dSJerome Brunet 	.dwc_write = dw_hdmi_dwc_write,
599*fa2d2e2dSJerome Brunet 	.cntl0_init = 0x0,
600*fa2d2e2dSJerome Brunet 	.cntl1_init = PHY_CNTL1_INIT,
6013b7c1237SNeil Armstrong };
6023b7c1237SNeil Armstrong 
6033b7c1237SNeil Armstrong static const struct meson_dw_hdmi_data meson_dw_hdmi_g12a_data = {
6043b7c1237SNeil Armstrong 	.top_read = dw_hdmi_g12a_top_read,
6053b7c1237SNeil Armstrong 	.top_write = dw_hdmi_g12a_top_write,
6063b7c1237SNeil Armstrong 	.dwc_read = dw_hdmi_g12a_dwc_read,
6073b7c1237SNeil Armstrong 	.dwc_write = dw_hdmi_g12a_dwc_write,
608*fa2d2e2dSJerome Brunet 	.cntl0_init = 0x000b4242, /* Bandgap */
609*fa2d2e2dSJerome Brunet 	.cntl1_init = PHY_CNTL1_INIT,
6103b7c1237SNeil Armstrong };
6113b7c1237SNeil Armstrong 
meson_dw_hdmi_init(struct meson_dw_hdmi * meson_dw_hdmi)6121374b837SNeil Armstrong static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi)
6131374b837SNeil Armstrong {
6141374b837SNeil Armstrong 	struct meson_drm *priv = meson_dw_hdmi->priv;
6151374b837SNeil Armstrong 
6161374b837SNeil Armstrong 	/* Enable clocks */
6171374b837SNeil Armstrong 	regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100);
6181374b837SNeil Armstrong 
6191374b837SNeil Armstrong 	/* Bring HDMITX MEM output of power down */
6201374b837SNeil Armstrong 	regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0);
6211374b837SNeil Armstrong 
6221374b837SNeil Armstrong 	/* Reset HDMITX APB & TX & PHY */
6231374b837SNeil Armstrong 	reset_control_reset(meson_dw_hdmi->hdmitx_apb);
6241374b837SNeil Armstrong 	reset_control_reset(meson_dw_hdmi->hdmitx_ctrl);
6251374b837SNeil Armstrong 	reset_control_reset(meson_dw_hdmi->hdmitx_phy);
6261374b837SNeil Armstrong 
6271374b837SNeil Armstrong 	/* Enable APB3 fail on error */
6281374b837SNeil Armstrong 	if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
6291374b837SNeil Armstrong 		writel_bits_relaxed(BIT(15), BIT(15),
6301374b837SNeil Armstrong 				    meson_dw_hdmi->hdmitx + HDMITX_TOP_CTRL_REG);
6311374b837SNeil Armstrong 		writel_bits_relaxed(BIT(15), BIT(15),
6321374b837SNeil Armstrong 				    meson_dw_hdmi->hdmitx + HDMITX_DWC_CTRL_REG);
6331374b837SNeil Armstrong 	}
6341374b837SNeil Armstrong 
6351374b837SNeil Armstrong 	/* Bring out of reset */
6361374b837SNeil Armstrong 	meson_dw_hdmi->data->top_write(meson_dw_hdmi,
6371374b837SNeil Armstrong 				       HDMITX_TOP_SW_RESET,  0);
6381374b837SNeil Armstrong 
6391374b837SNeil Armstrong 	msleep(20);
6401374b837SNeil Armstrong 
6411374b837SNeil Armstrong 	meson_dw_hdmi->data->top_write(meson_dw_hdmi,
6421374b837SNeil Armstrong 				       HDMITX_TOP_CLK_CNTL, 0xff);
6431374b837SNeil Armstrong 
644dbe2cf9fSJerome Brunet 	/* Enable normal output to PHY */
645dbe2cf9fSJerome Brunet 	meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
646dbe2cf9fSJerome Brunet 
647dbe2cf9fSJerome Brunet 	/* Setup PHY */
648*fa2d2e2dSJerome Brunet 	regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, meson_dw_hdmi->data->cntl1_init);
649*fa2d2e2dSJerome Brunet 	regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, meson_dw_hdmi->data->cntl0_init);
650dbe2cf9fSJerome Brunet 
6511374b837SNeil Armstrong 	/* Enable HDMI-TX Interrupt */
6521374b837SNeil Armstrong 	meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
6531374b837SNeil Armstrong 				       HDMITX_TOP_INTR_CORE);
6541374b837SNeil Armstrong 
6551374b837SNeil Armstrong 	meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_MASKN,
6561374b837SNeil Armstrong 				       HDMITX_TOP_INTR_CORE);
6571374b837SNeil Armstrong 
6581374b837SNeil Armstrong }
6591374b837SNeil Armstrong 
meson_disable_clk(void * data)6601dfeea90SMarc Zyngier static void meson_disable_clk(void *data)
6611dfeea90SMarc Zyngier {
6621dfeea90SMarc Zyngier 	clk_disable_unprepare(data);
6631dfeea90SMarc Zyngier }
6641dfeea90SMarc Zyngier 
meson_enable_clk(struct device * dev,char * name)6651dfeea90SMarc Zyngier static int meson_enable_clk(struct device *dev, char *name)
6661dfeea90SMarc Zyngier {
6671dfeea90SMarc Zyngier 	struct clk *clk;
6681dfeea90SMarc Zyngier 	int ret;
6691dfeea90SMarc Zyngier 
6701dfeea90SMarc Zyngier 	clk = devm_clk_get(dev, name);
6711dfeea90SMarc Zyngier 	if (IS_ERR(clk)) {
6721dfeea90SMarc Zyngier 		dev_err(dev, "Unable to get %s pclk\n", name);
6731dfeea90SMarc Zyngier 		return PTR_ERR(clk);
6741dfeea90SMarc Zyngier 	}
6751dfeea90SMarc Zyngier 
6761dfeea90SMarc Zyngier 	ret = clk_prepare_enable(clk);
6771dfeea90SMarc Zyngier 	if (!ret)
6781dfeea90SMarc Zyngier 		ret = devm_add_action_or_reset(dev, meson_disable_clk, clk);
6791dfeea90SMarc Zyngier 
6801dfeea90SMarc Zyngier 	return ret;
6811dfeea90SMarc Zyngier }
6821dfeea90SMarc Zyngier 
meson_dw_hdmi_bind(struct device * dev,struct device * master,void * data)6833f68be7dSNeil Armstrong static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
6843f68be7dSNeil Armstrong 				void *data)
6853f68be7dSNeil Armstrong {
6863f68be7dSNeil Armstrong 	struct platform_device *pdev = to_platform_device(dev);
6873b7c1237SNeil Armstrong 	const struct meson_dw_hdmi_data *match;
6883f68be7dSNeil Armstrong 	struct meson_dw_hdmi *meson_dw_hdmi;
6893f68be7dSNeil Armstrong 	struct drm_device *drm = data;
6903f68be7dSNeil Armstrong 	struct meson_drm *priv = drm->dev_private;
6913f68be7dSNeil Armstrong 	struct dw_hdmi_plat_data *dw_plat_data;
6923f68be7dSNeil Armstrong 	int irq;
6933f68be7dSNeil Armstrong 	int ret;
6943f68be7dSNeil Armstrong 
6953f68be7dSNeil Armstrong 	DRM_DEBUG_DRIVER("\n");
6963f68be7dSNeil Armstrong 
6973b7c1237SNeil Armstrong 	match = of_device_get_match_data(&pdev->dev);
6983b7c1237SNeil Armstrong 	if (!match) {
6993b7c1237SNeil Armstrong 		dev_err(&pdev->dev, "failed to get match data\n");
7003b7c1237SNeil Armstrong 		return -ENODEV;
7013b7c1237SNeil Armstrong 	}
7023b7c1237SNeil Armstrong 
7033f68be7dSNeil Armstrong 	meson_dw_hdmi = devm_kzalloc(dev, sizeof(*meson_dw_hdmi),
7043f68be7dSNeil Armstrong 				     GFP_KERNEL);
7053f68be7dSNeil Armstrong 	if (!meson_dw_hdmi)
7063f68be7dSNeil Armstrong 		return -ENOMEM;
7073f68be7dSNeil Armstrong 
7083f68be7dSNeil Armstrong 	meson_dw_hdmi->priv = priv;
7093f68be7dSNeil Armstrong 	meson_dw_hdmi->dev = dev;
7103b7c1237SNeil Armstrong 	meson_dw_hdmi->data = match;
7113f68be7dSNeil Armstrong 	dw_plat_data = &meson_dw_hdmi->dw_plat_data;
7123f68be7dSNeil Armstrong 
713429e8706SMatti Vaittinen 	ret = devm_regulator_get_enable_optional(dev, "hdmi");
7144028cbf8SMarek Szyprowski 	if (ret < 0 && ret != -ENODEV)
715161a803fSNeil Armstrong 		return ret;
716161a803fSNeil Armstrong 
7173f68be7dSNeil Armstrong 	meson_dw_hdmi->hdmitx_apb = devm_reset_control_get_exclusive(dev,
7183f68be7dSNeil Armstrong 						"hdmitx_apb");
7193f68be7dSNeil Armstrong 	if (IS_ERR(meson_dw_hdmi->hdmitx_apb)) {
7203f68be7dSNeil Armstrong 		dev_err(dev, "Failed to get hdmitx_apb reset\n");
7213f68be7dSNeil Armstrong 		return PTR_ERR(meson_dw_hdmi->hdmitx_apb);
7223f68be7dSNeil Armstrong 	}
7233f68be7dSNeil Armstrong 
7243f68be7dSNeil Armstrong 	meson_dw_hdmi->hdmitx_ctrl = devm_reset_control_get_exclusive(dev,
7253f68be7dSNeil Armstrong 						"hdmitx");
7263f68be7dSNeil Armstrong 	if (IS_ERR(meson_dw_hdmi->hdmitx_ctrl)) {
7273f68be7dSNeil Armstrong 		dev_err(dev, "Failed to get hdmitx reset\n");
7283f68be7dSNeil Armstrong 		return PTR_ERR(meson_dw_hdmi->hdmitx_ctrl);
7293f68be7dSNeil Armstrong 	}
7303f68be7dSNeil Armstrong 
7313f68be7dSNeil Armstrong 	meson_dw_hdmi->hdmitx_phy = devm_reset_control_get_exclusive(dev,
7323f68be7dSNeil Armstrong 						"hdmitx_phy");
7333f68be7dSNeil Armstrong 	if (IS_ERR(meson_dw_hdmi->hdmitx_phy)) {
7343f68be7dSNeil Armstrong 		dev_err(dev, "Failed to get hdmitx_phy reset\n");
7353f68be7dSNeil Armstrong 		return PTR_ERR(meson_dw_hdmi->hdmitx_phy);
7363f68be7dSNeil Armstrong 	}
7373f68be7dSNeil Armstrong 
738d4cb82aaSCai Huoqing 	meson_dw_hdmi->hdmitx = devm_platform_ioremap_resource(pdev, 0);
7393f68be7dSNeil Armstrong 	if (IS_ERR(meson_dw_hdmi->hdmitx))
7403f68be7dSNeil Armstrong 		return PTR_ERR(meson_dw_hdmi->hdmitx);
7413f68be7dSNeil Armstrong 
7421dfeea90SMarc Zyngier 	ret = meson_enable_clk(dev, "isfr");
7431dfeea90SMarc Zyngier 	if (ret)
7441dfeea90SMarc Zyngier 		return ret;
7453f68be7dSNeil Armstrong 
7462b6cb81bSMarc Zyngier 	ret = meson_enable_clk(dev, "iahb");
7472b6cb81bSMarc Zyngier 	if (ret)
7482b6cb81bSMarc Zyngier 		return ret;
7492b6cb81bSMarc Zyngier 
7501dfeea90SMarc Zyngier 	ret = meson_enable_clk(dev, "venci");
7511dfeea90SMarc Zyngier 	if (ret)
7521dfeea90SMarc Zyngier 		return ret;
7533f68be7dSNeil Armstrong 
7543f68be7dSNeil Armstrong 	dw_plat_data->regm = devm_regmap_init(dev, NULL, meson_dw_hdmi,
7553f68be7dSNeil Armstrong 					      &meson_dw_hdmi_regmap_config);
7563f68be7dSNeil Armstrong 	if (IS_ERR(dw_plat_data->regm))
7573f68be7dSNeil Armstrong 		return PTR_ERR(dw_plat_data->regm);
7583f68be7dSNeil Armstrong 
7593f68be7dSNeil Armstrong 	irq = platform_get_irq(pdev, 0);
760d918fe42SMarkus Elfring 	if (irq < 0)
7613f68be7dSNeil Armstrong 		return irq;
7623f68be7dSNeil Armstrong 
7633f68be7dSNeil Armstrong 	ret = devm_request_threaded_irq(dev, irq, dw_hdmi_top_irq,
7643f68be7dSNeil Armstrong 					dw_hdmi_top_thread_irq, IRQF_SHARED,
7653f68be7dSNeil Armstrong 					"dw_hdmi_top_irq", meson_dw_hdmi);
7663f68be7dSNeil Armstrong 	if (ret) {
7673f68be7dSNeil Armstrong 		dev_err(dev, "Failed to request hdmi top irq\n");
7683f68be7dSNeil Armstrong 		return ret;
7693f68be7dSNeil Armstrong 	}
7703f68be7dSNeil Armstrong 
7712b6cb81bSMarc Zyngier 	meson_dw_hdmi_init(meson_dw_hdmi);
7722b6cb81bSMarc Zyngier 
7733f68be7dSNeil Armstrong 	/* Bridge / Connector */
7743f68be7dSNeil Armstrong 
7759bc78d6dSLaurent Pinchart 	dw_plat_data->priv_data = meson_dw_hdmi;
7763f68be7dSNeil Armstrong 	dw_plat_data->phy_ops = &meson_dw_hdmi_phy_ops;
7773f68be7dSNeil Armstrong 	dw_plat_data->phy_name = "meson_dw_hdmi_phy";
7783f68be7dSNeil Armstrong 	dw_plat_data->phy_data = meson_dw_hdmi;
7793f68be7dSNeil Armstrong 	dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;
7808496a217SNeil Armstrong 	dw_plat_data->ycbcr_420_allowed = true;
781a584e2c3SNeil Armstrong 	dw_plat_data->disable_cec = true;
7820af5e0b4SNeil Armstrong 	dw_plat_data->output_port = 1;
7833f68be7dSNeil Armstrong 
784e2229fb7SJonas Karlman 	if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
785e2229fb7SJonas Karlman 	    dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
786e2229fb7SJonas Karlman 	    dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-g12a-dw-hdmi"))
787e2229fb7SJonas Karlman 		dw_plat_data->use_drm_infoframe = true;
788e2229fb7SJonas Karlman 
789eea034afSJernej Skrabec 	platform_set_drvdata(pdev, meson_dw_hdmi);
790eea034afSJernej Skrabec 
791e67f6037SNeil Armstrong 	meson_dw_hdmi->hdmi = dw_hdmi_probe(pdev, &meson_dw_hdmi->dw_plat_data);
792eea034afSJernej Skrabec 	if (IS_ERR(meson_dw_hdmi->hdmi))
793eea034afSJernej Skrabec 		return PTR_ERR(meson_dw_hdmi->hdmi);
7943f68be7dSNeil Armstrong 
795e67f6037SNeil Armstrong 	meson_dw_hdmi->bridge = of_drm_find_bridge(pdev->dev.of_node);
796f43aa584SNeil Armstrong 
7973f68be7dSNeil Armstrong 	DRM_DEBUG_DRIVER("HDMI controller initialized\n");
7983f68be7dSNeil Armstrong 
7993f68be7dSNeil Armstrong 	return 0;
8003f68be7dSNeil Armstrong }
8013f68be7dSNeil Armstrong 
meson_dw_hdmi_unbind(struct device * dev,struct device * master,void * data)8023f68be7dSNeil Armstrong static void meson_dw_hdmi_unbind(struct device *dev, struct device *master,
8033f68be7dSNeil Armstrong 				   void *data)
8043f68be7dSNeil Armstrong {
805eea034afSJernej Skrabec 	struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev);
806eea034afSJernej Skrabec 
807eea034afSJernej Skrabec 	dw_hdmi_unbind(meson_dw_hdmi->hdmi);
8083f68be7dSNeil Armstrong }
8093f68be7dSNeil Armstrong 
8103f68be7dSNeil Armstrong static const struct component_ops meson_dw_hdmi_ops = {
8113f68be7dSNeil Armstrong 	.bind	= meson_dw_hdmi_bind,
8123f68be7dSNeil Armstrong 	.unbind	= meson_dw_hdmi_unbind,
8133f68be7dSNeil Armstrong };
8143f68be7dSNeil Armstrong 
meson_dw_hdmi_pm_suspend(struct device * dev)8151374b837SNeil Armstrong static int __maybe_unused meson_dw_hdmi_pm_suspend(struct device *dev)
8161374b837SNeil Armstrong {
8171374b837SNeil Armstrong 	struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev);
8181374b837SNeil Armstrong 
8191374b837SNeil Armstrong 	if (!meson_dw_hdmi)
8201374b837SNeil Armstrong 		return 0;
8211374b837SNeil Armstrong 
8221374b837SNeil Armstrong 	/* Reset TOP */
8231374b837SNeil Armstrong 	meson_dw_hdmi->data->top_write(meson_dw_hdmi,
8241374b837SNeil Armstrong 				       HDMITX_TOP_SW_RESET, 0);
8251374b837SNeil Armstrong 
8261374b837SNeil Armstrong 	return 0;
8271374b837SNeil Armstrong }
8281374b837SNeil Armstrong 
meson_dw_hdmi_pm_resume(struct device * dev)8291374b837SNeil Armstrong static int __maybe_unused meson_dw_hdmi_pm_resume(struct device *dev)
8301374b837SNeil Armstrong {
8311374b837SNeil Armstrong 	struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev);
8321374b837SNeil Armstrong 
8331374b837SNeil Armstrong 	if (!meson_dw_hdmi)
8341374b837SNeil Armstrong 		return 0;
8351374b837SNeil Armstrong 
8361374b837SNeil Armstrong 	meson_dw_hdmi_init(meson_dw_hdmi);
8371374b837SNeil Armstrong 
8381374b837SNeil Armstrong 	dw_hdmi_resume(meson_dw_hdmi->hdmi);
8391374b837SNeil Armstrong 
8401374b837SNeil Armstrong 	return 0;
8411374b837SNeil Armstrong }
8421374b837SNeil Armstrong 
meson_dw_hdmi_probe(struct platform_device * pdev)8433f68be7dSNeil Armstrong static int meson_dw_hdmi_probe(struct platform_device *pdev)
8443f68be7dSNeil Armstrong {
8453f68be7dSNeil Armstrong 	return component_add(&pdev->dev, &meson_dw_hdmi_ops);
8463f68be7dSNeil Armstrong }
8473f68be7dSNeil Armstrong 
meson_dw_hdmi_remove(struct platform_device * pdev)84838ca2d93SUwe Kleine-König static void meson_dw_hdmi_remove(struct platform_device *pdev)
8493f68be7dSNeil Armstrong {
8503f68be7dSNeil Armstrong 	component_del(&pdev->dev, &meson_dw_hdmi_ops);
8513f68be7dSNeil Armstrong }
8523f68be7dSNeil Armstrong 
8531374b837SNeil Armstrong static const struct dev_pm_ops meson_dw_hdmi_pm_ops = {
8541374b837SNeil Armstrong 	SET_SYSTEM_SLEEP_PM_OPS(meson_dw_hdmi_pm_suspend,
8551374b837SNeil Armstrong 				meson_dw_hdmi_pm_resume)
8561374b837SNeil Armstrong };
8571374b837SNeil Armstrong 
8583f68be7dSNeil Armstrong static const struct of_device_id meson_dw_hdmi_of_table[] = {
8593b7c1237SNeil Armstrong 	{ .compatible = "amlogic,meson-gxbb-dw-hdmi",
860*fa2d2e2dSJerome Brunet 	  .data = &meson_dw_hdmi_gxbb_data },
8613b7c1237SNeil Armstrong 	{ .compatible = "amlogic,meson-gxl-dw-hdmi",
862*fa2d2e2dSJerome Brunet 	  .data = &meson_dw_hdmi_gxl_data },
8633b7c1237SNeil Armstrong 	{ .compatible = "amlogic,meson-gxm-dw-hdmi",
864*fa2d2e2dSJerome Brunet 	  .data = &meson_dw_hdmi_gxl_data },
8653b7c1237SNeil Armstrong 	{ .compatible = "amlogic,meson-g12a-dw-hdmi",
8663b7c1237SNeil Armstrong 	  .data = &meson_dw_hdmi_g12a_data },
8673f68be7dSNeil Armstrong 	{ }
8683f68be7dSNeil Armstrong };
8693f68be7dSNeil Armstrong MODULE_DEVICE_TABLE(of, meson_dw_hdmi_of_table);
8703f68be7dSNeil Armstrong 
8713f68be7dSNeil Armstrong static struct platform_driver meson_dw_hdmi_platform_driver = {
8723f68be7dSNeil Armstrong 	.probe		= meson_dw_hdmi_probe,
87338ca2d93SUwe Kleine-König 	.remove_new	= meson_dw_hdmi_remove,
8743f68be7dSNeil Armstrong 	.driver		= {
8753f68be7dSNeil Armstrong 		.name		= DRIVER_NAME,
8763f68be7dSNeil Armstrong 		.of_match_table	= meson_dw_hdmi_of_table,
8771374b837SNeil Armstrong 		.pm = &meson_dw_hdmi_pm_ops,
8783f68be7dSNeil Armstrong 	},
8793f68be7dSNeil Armstrong };
8803f68be7dSNeil Armstrong module_platform_driver(meson_dw_hdmi_platform_driver);
8813f68be7dSNeil Armstrong 
8823f68be7dSNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
8833f68be7dSNeil Armstrong MODULE_DESCRIPTION(DRIVER_DESC);
8843f68be7dSNeil Armstrong MODULE_LICENSE("GPL");
885