1*3bed4220SNeil Armstrong // SPDX-License-Identifier: GPL-2.0
2*3bed4220SNeil Armstrong /*
3*3bed4220SNeil Armstrong * Copyright (C) 2018 BayLibre, SAS
4*3bed4220SNeil Armstrong * Author: Jorge Ramirez-Ortiz <jramirez@baylibre.com>
5*3bed4220SNeil Armstrong */
6*3bed4220SNeil Armstrong
7*3bed4220SNeil Armstrong #include <common.h>
8*3bed4220SNeil Armstrong #include <display.h>
9*3bed4220SNeil Armstrong #include <dm.h>
10*3bed4220SNeil Armstrong #include <edid.h>
11*3bed4220SNeil Armstrong #include <asm/io.h>
12*3bed4220SNeil Armstrong #include <dw_hdmi.h>
13*3bed4220SNeil Armstrong #include <dm/device-internal.h>
14*3bed4220SNeil Armstrong #include <dm/uclass-internal.h>
15*3bed4220SNeil Armstrong #include <power/regulator.h>
16*3bed4220SNeil Armstrong #include <clk.h>
17*3bed4220SNeil Armstrong #include <linux/delay.h>
18*3bed4220SNeil Armstrong #include <reset.h>
19*3bed4220SNeil Armstrong #include <media_bus_format.h>
20*3bed4220SNeil Armstrong #include "meson_dw_hdmi.h"
21*3bed4220SNeil Armstrong #include "meson_vpu.h"
22*3bed4220SNeil Armstrong
23*3bed4220SNeil Armstrong /* TOP Block Communication Channel */
24*3bed4220SNeil Armstrong #define HDMITX_TOP_ADDR_REG 0x0
25*3bed4220SNeil Armstrong #define HDMITX_TOP_DATA_REG 0x4
26*3bed4220SNeil Armstrong #define HDMITX_TOP_CTRL_REG 0x8
27*3bed4220SNeil Armstrong
28*3bed4220SNeil Armstrong /* Controller Communication Channel */
29*3bed4220SNeil Armstrong #define HDMITX_DWC_ADDR_REG 0x10
30*3bed4220SNeil Armstrong #define HDMITX_DWC_DATA_REG 0x14
31*3bed4220SNeil Armstrong #define HDMITX_DWC_CTRL_REG 0x18
32*3bed4220SNeil Armstrong
33*3bed4220SNeil Armstrong /* HHI Registers */
34*3bed4220SNeil Armstrong #define HHI_MEM_PD_REG0 0x100 /* 0x40 */
35*3bed4220SNeil Armstrong #define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 */
36*3bed4220SNeil Armstrong #define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 */
37*3bed4220SNeil Armstrong #define HHI_HDMI_PHY_CNTL1 0x3a4 /* 0xe9 */
38*3bed4220SNeil Armstrong #define HHI_HDMI_PHY_CNTL2 0x3a8 /* 0xea */
39*3bed4220SNeil Armstrong #define HHI_HDMI_PHY_CNTL3 0x3ac /* 0xeb */
40*3bed4220SNeil Armstrong
41*3bed4220SNeil Armstrong struct meson_dw_hdmi {
42*3bed4220SNeil Armstrong struct udevice *dev;
43*3bed4220SNeil Armstrong struct dw_hdmi hdmi;
44*3bed4220SNeil Armstrong void __iomem *hhi_base;
45*3bed4220SNeil Armstrong };
46*3bed4220SNeil Armstrong
47*3bed4220SNeil Armstrong enum hdmi_compatible {
48*3bed4220SNeil Armstrong HDMI_COMPATIBLE_GXBB = 0,
49*3bed4220SNeil Armstrong HDMI_COMPATIBLE_GXL = 1,
50*3bed4220SNeil Armstrong HDMI_COMPATIBLE_GXM = 2,
51*3bed4220SNeil Armstrong };
52*3bed4220SNeil Armstrong
meson_hdmi_is_compatible(struct meson_dw_hdmi * priv,enum hdmi_compatible family)53*3bed4220SNeil Armstrong static inline bool meson_hdmi_is_compatible(struct meson_dw_hdmi *priv,
54*3bed4220SNeil Armstrong enum hdmi_compatible family)
55*3bed4220SNeil Armstrong {
56*3bed4220SNeil Armstrong enum hdmi_compatible compat = dev_get_driver_data(priv->dev);
57*3bed4220SNeil Armstrong
58*3bed4220SNeil Armstrong return compat == family;
59*3bed4220SNeil Armstrong }
60*3bed4220SNeil Armstrong
dw_hdmi_top_read(struct dw_hdmi * hdmi,unsigned int addr)61*3bed4220SNeil Armstrong static unsigned int dw_hdmi_top_read(struct dw_hdmi *hdmi, unsigned int addr)
62*3bed4220SNeil Armstrong {
63*3bed4220SNeil Armstrong unsigned int data;
64*3bed4220SNeil Armstrong
65*3bed4220SNeil Armstrong /* ADDR must be written twice */
66*3bed4220SNeil Armstrong writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
67*3bed4220SNeil Armstrong writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
68*3bed4220SNeil Armstrong
69*3bed4220SNeil Armstrong /* Read needs a second DATA read */
70*3bed4220SNeil Armstrong data = readl(hdmi->ioaddr + HDMITX_TOP_DATA_REG);
71*3bed4220SNeil Armstrong data = readl(hdmi->ioaddr + HDMITX_TOP_DATA_REG);
72*3bed4220SNeil Armstrong
73*3bed4220SNeil Armstrong return data;
74*3bed4220SNeil Armstrong }
75*3bed4220SNeil Armstrong
dw_hdmi_top_write(struct dw_hdmi * hdmi,unsigned int addr,unsigned int data)76*3bed4220SNeil Armstrong static inline void dw_hdmi_top_write(struct dw_hdmi *hdmi,
77*3bed4220SNeil Armstrong unsigned int addr, unsigned int data)
78*3bed4220SNeil Armstrong {
79*3bed4220SNeil Armstrong /* ADDR must be written twice */
80*3bed4220SNeil Armstrong writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
81*3bed4220SNeil Armstrong writel(addr & 0xffff, hdmi->ioaddr + HDMITX_TOP_ADDR_REG);
82*3bed4220SNeil Armstrong
83*3bed4220SNeil Armstrong /* Write needs single DATA write */
84*3bed4220SNeil Armstrong writel(data, hdmi->ioaddr + HDMITX_TOP_DATA_REG);
85*3bed4220SNeil Armstrong }
86*3bed4220SNeil Armstrong
dw_hdmi_top_write_bits(struct dw_hdmi * hdmi,unsigned int addr,unsigned int mask,unsigned int val)87*3bed4220SNeil Armstrong static inline void dw_hdmi_top_write_bits(struct dw_hdmi *hdmi,
88*3bed4220SNeil Armstrong unsigned int addr,
89*3bed4220SNeil Armstrong unsigned int mask,
90*3bed4220SNeil Armstrong unsigned int val)
91*3bed4220SNeil Armstrong {
92*3bed4220SNeil Armstrong unsigned int data = dw_hdmi_top_read(hdmi, addr);
93*3bed4220SNeil Armstrong
94*3bed4220SNeil Armstrong data &= ~mask;
95*3bed4220SNeil Armstrong data |= val;
96*3bed4220SNeil Armstrong dw_hdmi_top_write(hdmi, addr, data);
97*3bed4220SNeil Armstrong }
98*3bed4220SNeil Armstrong
dw_hdmi_dwc_read(struct dw_hdmi * hdmi,int addr)99*3bed4220SNeil Armstrong static u8 dw_hdmi_dwc_read(struct dw_hdmi *hdmi, int addr)
100*3bed4220SNeil Armstrong {
101*3bed4220SNeil Armstrong unsigned int data;
102*3bed4220SNeil Armstrong
103*3bed4220SNeil Armstrong /* ADDR must be written twice */
104*3bed4220SNeil Armstrong writel(addr & 0xffff, hdmi->ioaddr + HDMITX_DWC_ADDR_REG);
105*3bed4220SNeil Armstrong writel(addr & 0xffff, hdmi->ioaddr + HDMITX_DWC_ADDR_REG);
106*3bed4220SNeil Armstrong
107*3bed4220SNeil Armstrong /* Read needs a second DATA read */
108*3bed4220SNeil Armstrong data = readl(hdmi->ioaddr + HDMITX_DWC_DATA_REG);
109*3bed4220SNeil Armstrong data = readl(hdmi->ioaddr + HDMITX_DWC_DATA_REG);
110*3bed4220SNeil Armstrong
111*3bed4220SNeil Armstrong return data;
112*3bed4220SNeil Armstrong }
113*3bed4220SNeil Armstrong
dw_hdmi_dwc_write(struct dw_hdmi * hdmi,u8 data,int addr)114*3bed4220SNeil Armstrong static inline void dw_hdmi_dwc_write(struct dw_hdmi *hdmi, u8 data, int addr)
115*3bed4220SNeil Armstrong {
116*3bed4220SNeil Armstrong /* ADDR must be written twice */
117*3bed4220SNeil Armstrong writel(addr & 0xffff, hdmi->ioaddr + HDMITX_DWC_ADDR_REG);
118*3bed4220SNeil Armstrong writel(addr & 0xffff, hdmi->ioaddr + HDMITX_DWC_ADDR_REG);
119*3bed4220SNeil Armstrong
120*3bed4220SNeil Armstrong /* Write needs single DATA write */
121*3bed4220SNeil Armstrong writel(data, hdmi->ioaddr + HDMITX_DWC_DATA_REG);
122*3bed4220SNeil Armstrong }
123*3bed4220SNeil Armstrong
dw_hdmi_dwc_write_bits(struct dw_hdmi * hdmi,unsigned int addr,unsigned int mask,unsigned int val)124*3bed4220SNeil Armstrong static inline void dw_hdmi_dwc_write_bits(struct dw_hdmi *hdmi,
125*3bed4220SNeil Armstrong unsigned int addr,
126*3bed4220SNeil Armstrong unsigned int mask,
127*3bed4220SNeil Armstrong unsigned int val)
128*3bed4220SNeil Armstrong {
129*3bed4220SNeil Armstrong u8 data = dw_hdmi_dwc_read(hdmi, addr);
130*3bed4220SNeil Armstrong
131*3bed4220SNeil Armstrong data &= ~mask;
132*3bed4220SNeil Armstrong data |= val;
133*3bed4220SNeil Armstrong
134*3bed4220SNeil Armstrong dw_hdmi_dwc_write(hdmi, data, addr);
135*3bed4220SNeil Armstrong }
136*3bed4220SNeil Armstrong
dw_hdmi_hhi_write(struct meson_dw_hdmi * priv,unsigned int addr,unsigned int data)137*3bed4220SNeil Armstrong static inline void dw_hdmi_hhi_write(struct meson_dw_hdmi *priv,
138*3bed4220SNeil Armstrong unsigned int addr, unsigned int data)
139*3bed4220SNeil Armstrong {
140*3bed4220SNeil Armstrong hhi_write(addr, data);
141*3bed4220SNeil Armstrong }
142*3bed4220SNeil Armstrong
143*3bed4220SNeil Armstrong __attribute__((unused))
dw_hdmi_hhi_read(struct meson_dw_hdmi * priv,unsigned int addr)144*3bed4220SNeil Armstrong static unsigned int dw_hdmi_hhi_read(struct meson_dw_hdmi *priv,
145*3bed4220SNeil Armstrong unsigned int addr)
146*3bed4220SNeil Armstrong {
147*3bed4220SNeil Armstrong return hhi_read(addr);
148*3bed4220SNeil Armstrong }
149*3bed4220SNeil Armstrong
dw_hdmi_hhi_update_bits(struct meson_dw_hdmi * priv,unsigned int addr,unsigned int mask,unsigned int val)150*3bed4220SNeil Armstrong static inline void dw_hdmi_hhi_update_bits(struct meson_dw_hdmi *priv,
151*3bed4220SNeil Armstrong unsigned int addr,
152*3bed4220SNeil Armstrong unsigned int mask,
153*3bed4220SNeil Armstrong unsigned int val)
154*3bed4220SNeil Armstrong {
155*3bed4220SNeil Armstrong hhi_update_bits(addr, mask, val);
156*3bed4220SNeil Armstrong }
157*3bed4220SNeil Armstrong
meson_dw_hdmi_read_edid(struct udevice * dev,u8 * buf,int buf_size)158*3bed4220SNeil Armstrong static int meson_dw_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size)
159*3bed4220SNeil Armstrong {
160*3bed4220SNeil Armstrong #if defined DEBUG
161*3bed4220SNeil Armstrong struct display_timing timing;
162*3bed4220SNeil Armstrong int panel_bits_per_colour;
163*3bed4220SNeil Armstrong #endif
164*3bed4220SNeil Armstrong struct meson_dw_hdmi *priv = dev_get_priv(dev);
165*3bed4220SNeil Armstrong int ret;
166*3bed4220SNeil Armstrong
167*3bed4220SNeil Armstrong ret = dw_hdmi_read_edid(&priv->hdmi, buf, buf_size);
168*3bed4220SNeil Armstrong
169*3bed4220SNeil Armstrong #if defined DEBUG
170*3bed4220SNeil Armstrong if (!ret)
171*3bed4220SNeil Armstrong return ret;
172*3bed4220SNeil Armstrong
173*3bed4220SNeil Armstrong edid_print_info((struct edid1_info *)buf);
174*3bed4220SNeil Armstrong edid_get_timing(buf, ret, &timing, &panel_bits_per_colour);
175*3bed4220SNeil Armstrong debug("Display timing:\n");
176*3bed4220SNeil Armstrong debug(" hactive %04d, hfrontp %04d, hbackp %04d hsync %04d\n"
177*3bed4220SNeil Armstrong " vactive %04d, vfrontp %04d, vbackp %04d vsync %04d\n",
178*3bed4220SNeil Armstrong timing.hactive.typ, timing.hfront_porch.typ,
179*3bed4220SNeil Armstrong timing.hback_porch.typ, timing.hsync_len.typ,
180*3bed4220SNeil Armstrong timing.vactive.typ, timing.vfront_porch.typ,
181*3bed4220SNeil Armstrong timing.vback_porch.typ, timing.vsync_len.typ);
182*3bed4220SNeil Armstrong debug(" flags: ");
183*3bed4220SNeil Armstrong if (timing.flags & DISPLAY_FLAGS_INTERLACED)
184*3bed4220SNeil Armstrong debug("interlaced ");
185*3bed4220SNeil Armstrong if (timing.flags & DISPLAY_FLAGS_DOUBLESCAN)
186*3bed4220SNeil Armstrong debug("doublescan ");
187*3bed4220SNeil Armstrong if (timing.flags & DISPLAY_FLAGS_DOUBLECLK)
188*3bed4220SNeil Armstrong debug("doubleclk ");
189*3bed4220SNeil Armstrong if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW)
190*3bed4220SNeil Armstrong debug("hsync_low ");
191*3bed4220SNeil Armstrong if (timing.flags & DISPLAY_FLAGS_HSYNC_HIGH)
192*3bed4220SNeil Armstrong debug("hsync_high ");
193*3bed4220SNeil Armstrong if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW)
194*3bed4220SNeil Armstrong debug("vsync_low ");
195*3bed4220SNeil Armstrong if (timing.flags & DISPLAY_FLAGS_VSYNC_HIGH)
196*3bed4220SNeil Armstrong debug("vsync_high ");
197*3bed4220SNeil Armstrong debug("\n");
198*3bed4220SNeil Armstrong #endif
199*3bed4220SNeil Armstrong
200*3bed4220SNeil Armstrong return ret;
201*3bed4220SNeil Armstrong }
202*3bed4220SNeil Armstrong
meson_dw_hdmi_phy_reset(struct meson_dw_hdmi * priv)203*3bed4220SNeil Armstrong static inline void meson_dw_hdmi_phy_reset(struct meson_dw_hdmi *priv)
204*3bed4220SNeil Armstrong {
205*3bed4220SNeil Armstrong /* Enable and software reset */
206*3bed4220SNeil Armstrong dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1, 0xf, 0xf);
207*3bed4220SNeil Armstrong
208*3bed4220SNeil Armstrong mdelay(2);
209*3bed4220SNeil Armstrong
210*3bed4220SNeil Armstrong /* Enable and unreset */
211*3bed4220SNeil Armstrong dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1, 0xf, 0xe);
212*3bed4220SNeil Armstrong
213*3bed4220SNeil Armstrong mdelay(2);
214*3bed4220SNeil Armstrong }
215*3bed4220SNeil Armstrong
meson_dw_hdmi_phy_setup_mode(struct meson_dw_hdmi * priv,uint pixel_clock)216*3bed4220SNeil Armstrong static void meson_dw_hdmi_phy_setup_mode(struct meson_dw_hdmi *priv,
217*3bed4220SNeil Armstrong uint pixel_clock)
218*3bed4220SNeil Armstrong {
219*3bed4220SNeil Armstrong pixel_clock = pixel_clock / 1000;
220*3bed4220SNeil Armstrong
221*3bed4220SNeil Armstrong if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXL) ||
222*3bed4220SNeil Armstrong meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXM)) {
223*3bed4220SNeil Armstrong if (pixel_clock >= 371250) {
224*3bed4220SNeil Armstrong /* 5.94Gbps, 3.7125Gbps */
225*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL0, 0x333d3282);
226*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL3, 0x2136315b);
227*3bed4220SNeil Armstrong } else if (pixel_clock >= 297000) {
228*3bed4220SNeil Armstrong /* 2.97Gbps */
229*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL0, 0x33303382);
230*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL3, 0x2036315b);
231*3bed4220SNeil Armstrong } else if (pixel_clock >= 148500) {
232*3bed4220SNeil Armstrong /* 1.485Gbps */
233*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL0, 0x33303362);
234*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL3, 0x2016315b);
235*3bed4220SNeil Armstrong } else {
236*3bed4220SNeil Armstrong /* 742.5Mbps, and below */
237*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL0, 0x33604142);
238*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL3, 0x0016315b);
239*3bed4220SNeil Armstrong }
240*3bed4220SNeil Armstrong } else {
241*3bed4220SNeil Armstrong if (pixel_clock >= 371250) {
242*3bed4220SNeil Armstrong /* 5.94Gbps, 3.7125Gbps */
243*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL0, 0x33353245);
244*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL3, 0x2100115b);
245*3bed4220SNeil Armstrong } else if (pixel_clock >= 297000) {
246*3bed4220SNeil Armstrong /* 2.97Gbps */
247*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL0, 0x33634283);
248*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL3, 0xb000115b);
249*3bed4220SNeil Armstrong } else {
250*3bed4220SNeil Armstrong /* 1.485Gbps, and below */
251*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL0, 0x33632122);
252*3bed4220SNeil Armstrong hhi_write(HHI_HDMI_PHY_CNTL3, 0x2000115b);
253*3bed4220SNeil Armstrong }
254*3bed4220SNeil Armstrong }
255*3bed4220SNeil Armstrong }
256*3bed4220SNeil Armstrong
meson_dw_hdmi_phy_init(struct dw_hdmi * hdmi,uint pixel_clock)257*3bed4220SNeil Armstrong static int meson_dw_hdmi_phy_init(struct dw_hdmi *hdmi, uint pixel_clock)
258*3bed4220SNeil Armstrong {
259*3bed4220SNeil Armstrong struct meson_dw_hdmi *priv = container_of(hdmi, struct meson_dw_hdmi,
260*3bed4220SNeil Armstrong hdmi);
261*3bed4220SNeil Armstrong /* Enable clocks */
262*3bed4220SNeil Armstrong dw_hdmi_hhi_update_bits(priv, HHI_HDMI_CLK_CNTL, 0xffff, 0x100);
263*3bed4220SNeil Armstrong
264*3bed4220SNeil Armstrong /* Bring HDMITX MEM output of power down */
265*3bed4220SNeil Armstrong dw_hdmi_hhi_update_bits(priv, HHI_MEM_PD_REG0, 0xff << 8, 0);
266*3bed4220SNeil Armstrong
267*3bed4220SNeil Armstrong /* Bring out of reset */
268*3bed4220SNeil Armstrong dw_hdmi_top_write(hdmi, HDMITX_TOP_SW_RESET, 0);
269*3bed4220SNeil Armstrong
270*3bed4220SNeil Armstrong /* Enable internal pixclk, tmds_clk, spdif_clk, i2s_clk, cecclk */
271*3bed4220SNeil Armstrong dw_hdmi_top_write_bits(hdmi, HDMITX_TOP_CLK_CNTL, 0x3, 0x3);
272*3bed4220SNeil Armstrong dw_hdmi_top_write_bits(hdmi, HDMITX_TOP_CLK_CNTL, 0x3 << 4, 0x3 << 4);
273*3bed4220SNeil Armstrong
274*3bed4220SNeil Armstrong /* Enable normal output to PHY */
275*3bed4220SNeil Armstrong dw_hdmi_top_write(hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
276*3bed4220SNeil Armstrong
277*3bed4220SNeil Armstrong /* TMDS pattern setup (TOFIX pattern for 4k2k scrambling) */
278*3bed4220SNeil Armstrong dw_hdmi_top_write(hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, 0x001f001f);
279*3bed4220SNeil Armstrong dw_hdmi_top_write(hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, 0x001f001f);
280*3bed4220SNeil Armstrong
281*3bed4220SNeil Armstrong /* Load TMDS pattern */
282*3bed4220SNeil Armstrong dw_hdmi_top_write(hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1);
283*3bed4220SNeil Armstrong mdelay(20);
284*3bed4220SNeil Armstrong dw_hdmi_top_write(hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2);
285*3bed4220SNeil Armstrong
286*3bed4220SNeil Armstrong /* Setup PHY parameters */
287*3bed4220SNeil Armstrong meson_dw_hdmi_phy_setup_mode(priv, pixel_clock);
288*3bed4220SNeil Armstrong
289*3bed4220SNeil Armstrong /* Setup PHY */
290*3bed4220SNeil Armstrong dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1,
291*3bed4220SNeil Armstrong 0xffff << 16, 0x0390 << 16);
292*3bed4220SNeil Armstrong
293*3bed4220SNeil Armstrong /* BIT_INVERT */
294*3bed4220SNeil Armstrong if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXL) ||
295*3bed4220SNeil Armstrong meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_GXM))
296*3bed4220SNeil Armstrong dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1, BIT(17), 0);
297*3bed4220SNeil Armstrong else
298*3bed4220SNeil Armstrong dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1,
299*3bed4220SNeil Armstrong BIT(17), BIT(17));
300*3bed4220SNeil Armstrong
301*3bed4220SNeil Armstrong /* Disable clock, fifo, fifo_wr */
302*3bed4220SNeil Armstrong dw_hdmi_hhi_update_bits(priv, HHI_HDMI_PHY_CNTL1, 0xf, 0);
303*3bed4220SNeil Armstrong
304*3bed4220SNeil Armstrong mdelay(100);
305*3bed4220SNeil Armstrong
306*3bed4220SNeil Armstrong /* Reset PHY 3 times in a row */
307*3bed4220SNeil Armstrong meson_dw_hdmi_phy_reset(priv);
308*3bed4220SNeil Armstrong meson_dw_hdmi_phy_reset(priv);
309*3bed4220SNeil Armstrong meson_dw_hdmi_phy_reset(priv);
310*3bed4220SNeil Armstrong
311*3bed4220SNeil Armstrong return 0;
312*3bed4220SNeil Armstrong }
313*3bed4220SNeil Armstrong
meson_dw_hdmi_enable(struct udevice * dev,int panel_bpp,const struct display_timing * edid)314*3bed4220SNeil Armstrong static int meson_dw_hdmi_enable(struct udevice *dev, int panel_bpp,
315*3bed4220SNeil Armstrong const struct display_timing *edid)
316*3bed4220SNeil Armstrong {
317*3bed4220SNeil Armstrong struct meson_dw_hdmi *priv = dev_get_priv(dev);
318*3bed4220SNeil Armstrong
319*3bed4220SNeil Armstrong /* will back into meson_dw_hdmi_phy_init */
320*3bed4220SNeil Armstrong return dw_hdmi_enable(&priv->hdmi, edid);
321*3bed4220SNeil Armstrong }
322*3bed4220SNeil Armstrong
meson_dw_hdmi_wait_hpd(struct dw_hdmi * hdmi)323*3bed4220SNeil Armstrong static int meson_dw_hdmi_wait_hpd(struct dw_hdmi *hdmi)
324*3bed4220SNeil Armstrong {
325*3bed4220SNeil Armstrong int i;
326*3bed4220SNeil Armstrong
327*3bed4220SNeil Armstrong /* Poll 1 second for HPD signal */
328*3bed4220SNeil Armstrong for (i = 0; i < 10; ++i) {
329*3bed4220SNeil Armstrong if (dw_hdmi_top_read(hdmi, HDMITX_TOP_STAT0))
330*3bed4220SNeil Armstrong return 0;
331*3bed4220SNeil Armstrong
332*3bed4220SNeil Armstrong mdelay(100);
333*3bed4220SNeil Armstrong }
334*3bed4220SNeil Armstrong
335*3bed4220SNeil Armstrong return -ETIMEDOUT;
336*3bed4220SNeil Armstrong }
337*3bed4220SNeil Armstrong
meson_dw_hdmi_probe(struct udevice * dev)338*3bed4220SNeil Armstrong static int meson_dw_hdmi_probe(struct udevice *dev)
339*3bed4220SNeil Armstrong {
340*3bed4220SNeil Armstrong struct meson_dw_hdmi *priv = dev_get_priv(dev);
341*3bed4220SNeil Armstrong struct reset_ctl_bulk resets;
342*3bed4220SNeil Armstrong struct clk_bulk clocks;
343*3bed4220SNeil Armstrong struct udevice *supply;
344*3bed4220SNeil Armstrong int ret;
345*3bed4220SNeil Armstrong
346*3bed4220SNeil Armstrong priv->dev = dev;
347*3bed4220SNeil Armstrong
348*3bed4220SNeil Armstrong priv->hdmi.ioaddr = (ulong)dev_remap_addr_index(dev, 0);
349*3bed4220SNeil Armstrong if (!priv->hdmi.ioaddr)
350*3bed4220SNeil Armstrong return -EINVAL;
351*3bed4220SNeil Armstrong
352*3bed4220SNeil Armstrong priv->hhi_base = dev_remap_addr_index(dev, 1);
353*3bed4220SNeil Armstrong if (!priv->hhi_base)
354*3bed4220SNeil Armstrong return -EINVAL;
355*3bed4220SNeil Armstrong
356*3bed4220SNeil Armstrong priv->hdmi.hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
357*3bed4220SNeil Armstrong priv->hdmi.hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_YUV8_1X24;
358*3bed4220SNeil Armstrong priv->hdmi.phy_set = meson_dw_hdmi_phy_init;
359*3bed4220SNeil Armstrong priv->hdmi.write_reg = dw_hdmi_dwc_write;
360*3bed4220SNeil Armstrong priv->hdmi.read_reg = dw_hdmi_dwc_read;
361*3bed4220SNeil Armstrong priv->hdmi.i2c_clk_high = 0x67;
362*3bed4220SNeil Armstrong priv->hdmi.i2c_clk_low = 0x78;
363*3bed4220SNeil Armstrong
364*3bed4220SNeil Armstrong ret = device_get_supply_regulator(dev, "hdmi-supply", &supply);
365*3bed4220SNeil Armstrong if (ret)
366*3bed4220SNeil Armstrong return ret;
367*3bed4220SNeil Armstrong
368*3bed4220SNeil Armstrong ret = regulator_set_enable(supply, true);
369*3bed4220SNeil Armstrong if (ret)
370*3bed4220SNeil Armstrong return ret;
371*3bed4220SNeil Armstrong
372*3bed4220SNeil Armstrong ret = reset_get_bulk(dev, &resets);
373*3bed4220SNeil Armstrong if (ret)
374*3bed4220SNeil Armstrong return ret;
375*3bed4220SNeil Armstrong
376*3bed4220SNeil Armstrong ret = clk_get_bulk(dev, &clocks);
377*3bed4220SNeil Armstrong if (ret)
378*3bed4220SNeil Armstrong return ret;
379*3bed4220SNeil Armstrong
380*3bed4220SNeil Armstrong ret = clk_enable_bulk(&clocks);
381*3bed4220SNeil Armstrong if (ret)
382*3bed4220SNeil Armstrong return ret;
383*3bed4220SNeil Armstrong
384*3bed4220SNeil Armstrong /* Enable clocks */
385*3bed4220SNeil Armstrong dw_hdmi_hhi_update_bits(priv, HHI_HDMI_CLK_CNTL, 0xffff, 0x100);
386*3bed4220SNeil Armstrong
387*3bed4220SNeil Armstrong /* Bring HDMITX MEM output of power down */
388*3bed4220SNeil Armstrong dw_hdmi_hhi_update_bits(priv, HHI_MEM_PD_REG0, 0xff << 8, 0);
389*3bed4220SNeil Armstrong
390*3bed4220SNeil Armstrong /* Reset HDMITX APB & TX & PHY: cycle needed for EDID */
391*3bed4220SNeil Armstrong ret = reset_deassert_bulk(&resets);
392*3bed4220SNeil Armstrong if (ret)
393*3bed4220SNeil Armstrong return ret;
394*3bed4220SNeil Armstrong
395*3bed4220SNeil Armstrong ret = reset_assert_bulk(&resets);
396*3bed4220SNeil Armstrong if (ret)
397*3bed4220SNeil Armstrong return ret;
398*3bed4220SNeil Armstrong
399*3bed4220SNeil Armstrong ret = reset_deassert_bulk(&resets);
400*3bed4220SNeil Armstrong if (ret)
401*3bed4220SNeil Armstrong return ret;
402*3bed4220SNeil Armstrong
403*3bed4220SNeil Armstrong /* Enable APB3 fail on error */
404*3bed4220SNeil Armstrong writel_bits(BIT(15), BIT(15), priv->hdmi.ioaddr + HDMITX_TOP_CTRL_REG);
405*3bed4220SNeil Armstrong writel_bits(BIT(15), BIT(15), priv->hdmi.ioaddr + HDMITX_DWC_CTRL_REG);
406*3bed4220SNeil Armstrong
407*3bed4220SNeil Armstrong /* Bring out of reset */
408*3bed4220SNeil Armstrong dw_hdmi_top_write(&priv->hdmi, HDMITX_TOP_SW_RESET, 0);
409*3bed4220SNeil Armstrong mdelay(20);
410*3bed4220SNeil Armstrong dw_hdmi_top_write(&priv->hdmi, HDMITX_TOP_CLK_CNTL, 0xff);
411*3bed4220SNeil Armstrong
412*3bed4220SNeil Armstrong dw_hdmi_init(&priv->hdmi);
413*3bed4220SNeil Armstrong dw_hdmi_phy_init(&priv->hdmi);
414*3bed4220SNeil Armstrong
415*3bed4220SNeil Armstrong /* wait for connector */
416*3bed4220SNeil Armstrong ret = meson_dw_hdmi_wait_hpd(&priv->hdmi);
417*3bed4220SNeil Armstrong if (ret)
418*3bed4220SNeil Armstrong debug("hdmi can not get hpd signal\n");
419*3bed4220SNeil Armstrong
420*3bed4220SNeil Armstrong return ret;
421*3bed4220SNeil Armstrong }
422*3bed4220SNeil Armstrong
423*3bed4220SNeil Armstrong static const struct dm_display_ops meson_dw_hdmi_ops = {
424*3bed4220SNeil Armstrong .read_edid = meson_dw_hdmi_read_edid,
425*3bed4220SNeil Armstrong .enable = meson_dw_hdmi_enable,
426*3bed4220SNeil Armstrong };
427*3bed4220SNeil Armstrong
428*3bed4220SNeil Armstrong static const struct udevice_id meson_dw_hdmi_ids[] = {
429*3bed4220SNeil Armstrong { .compatible = "amlogic,meson-gxbb-dw-hdmi",
430*3bed4220SNeil Armstrong .data = HDMI_COMPATIBLE_GXBB },
431*3bed4220SNeil Armstrong { .compatible = "amlogic,meson-gxl-dw-hdmi",
432*3bed4220SNeil Armstrong .data = HDMI_COMPATIBLE_GXL },
433*3bed4220SNeil Armstrong { .compatible = "amlogic,meson-gxm-dw-hdmi",
434*3bed4220SNeil Armstrong .data = HDMI_COMPATIBLE_GXM },
435*3bed4220SNeil Armstrong { }
436*3bed4220SNeil Armstrong };
437*3bed4220SNeil Armstrong
438*3bed4220SNeil Armstrong U_BOOT_DRIVER(meson_dw_hdmi) = {
439*3bed4220SNeil Armstrong .name = "meson_dw_hdmi",
440*3bed4220SNeil Armstrong .id = UCLASS_DISPLAY,
441*3bed4220SNeil Armstrong .of_match = meson_dw_hdmi_ids,
442*3bed4220SNeil Armstrong .ops = &meson_dw_hdmi_ops,
443*3bed4220SNeil Armstrong .probe = meson_dw_hdmi_probe,
444*3bed4220SNeil Armstrong .priv_auto_alloc_size = sizeof(struct meson_dw_hdmi),
445*3bed4220SNeil Armstrong };
446