Prepare v2023.10
[platform/kernel/u-boot.git] / drivers / video / sunxi / sunxi_dw_hdmi.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Allwinner DW HDMI bridge
4  *
5  * (C) Copyright 2017 Jernej Skrabec <jernej.skrabec@siol.net>
6  */
7
8 #include <clk.h>
9 #include <common.h>
10 #include <display.h>
11 #include <dm.h>
12 #include <dw_hdmi.h>
13 #include <edid.h>
14 #include <log.h>
15 #include <reset.h>
16 #include <time.h>
17 #include <asm/io.h>
18 #include <asm/arch/clock.h>
19 #include <asm/arch/lcdc.h>
20 #include <linux/bitops.h>
21 #include <linux/delay.h>
22 #include <power/regulator.h>
23
24 struct sunxi_dw_hdmi_priv {
25         struct dw_hdmi hdmi;
26         struct reset_ctl_bulk resets;
27         struct clk_bulk clocks;
28         struct udevice *hvcc;
29 };
30
31 struct sunxi_hdmi_phy {
32         u32 pol;
33         u32 res1[3];
34         u32 read_en;
35         u32 unscramble;
36         u32 res2[2];
37         u32 ctrl;
38         u32 unk1;
39         u32 unk2;
40         u32 pll;
41         u32 clk;
42         u32 unk3;
43         u32 status;
44 };
45
46 #define HDMI_PHY_OFFS 0x10000
47
48 static int sunxi_dw_hdmi_get_divider(uint clock)
49 {
50         /*
51          * Due to missing documentaion of HDMI PHY, we know correct
52          * settings only for following four PHY dividers. Select one
53          * based on clock speed.
54          */
55         if (clock <= 27000000)
56                 return 11;
57         else if (clock <= 74250000)
58                 return 4;
59         else if (clock <= 148500000)
60                 return 2;
61         else
62                 return 1;
63 }
64
65 static void sunxi_dw_hdmi_phy_init(struct dw_hdmi *hdmi)
66 {
67         struct sunxi_hdmi_phy * const phy =
68                 (struct sunxi_hdmi_phy *)(hdmi->ioaddr + HDMI_PHY_OFFS);
69         unsigned long tmo;
70         u32 tmp;
71
72         /*
73          * HDMI PHY settings are taken as-is from Allwinner BSP code.
74          * There is no documentation.
75          */
76         writel(0, &phy->ctrl);
77         setbits_le32(&phy->ctrl, BIT(0));
78         udelay(5);
79         setbits_le32(&phy->ctrl, BIT(16));
80         setbits_le32(&phy->ctrl, BIT(1));
81         udelay(10);
82         setbits_le32(&phy->ctrl, BIT(2));
83         udelay(5);
84         setbits_le32(&phy->ctrl, BIT(3));
85         udelay(40);
86         setbits_le32(&phy->ctrl, BIT(19));
87         udelay(100);
88         setbits_le32(&phy->ctrl, BIT(18));
89         setbits_le32(&phy->ctrl, 7 << 4);
90
91         /* Note that Allwinner code doesn't fail in case of timeout */
92         tmo = timer_get_us() + 2000;
93         while ((readl(&phy->status) & 0x80) == 0) {
94                 if (timer_get_us() > tmo) {
95                         printf("Warning: HDMI PHY init timeout!\n");
96                         break;
97                 }
98         }
99
100         setbits_le32(&phy->ctrl, 0xf << 8);
101         setbits_le32(&phy->ctrl, BIT(7));
102
103         writel(0x39dc5040, &phy->pll);
104         writel(0x80084343, &phy->clk);
105         udelay(10000);
106         writel(1, &phy->unk3);
107         setbits_le32(&phy->pll, BIT(25));
108         udelay(100000);
109         tmp = (readl(&phy->status) & 0x1f800) >> 11;
110         setbits_le32(&phy->pll, BIT(31) | BIT(30));
111         setbits_le32(&phy->pll, tmp);
112         writel(0x01FF0F7F, &phy->ctrl);
113         writel(0x80639000, &phy->unk1);
114         writel(0x0F81C405, &phy->unk2);
115
116         /* enable read access to HDMI controller */
117         writel(0x54524545, &phy->read_en);
118         /* descramble register offsets */
119         writel(0x42494E47, &phy->unscramble);
120 }
121
122 static void sunxi_dw_hdmi_phy_set(struct dw_hdmi *hdmi, uint clock, int phy_div)
123 {
124         struct sunxi_hdmi_phy * const phy =
125                 (struct sunxi_hdmi_phy *)(hdmi->ioaddr + HDMI_PHY_OFFS);
126         int div = sunxi_dw_hdmi_get_divider(clock);
127         u32 tmp;
128
129         /*
130          * Unfortunately, we don't know much about those magic
131          * numbers. They are taken from Allwinner BSP driver.
132          */
133         switch (div) {
134         case 1:
135                 writel(0x30dc5fc0, &phy->pll);
136                 writel(0x800863C0 | (phy_div - 1), &phy->clk);
137                 mdelay(10);
138                 writel(0x00000001, &phy->unk3);
139                 setbits_le32(&phy->pll, BIT(25));
140                 mdelay(200);
141                 tmp = (readl(&phy->status) & 0x1f800) >> 11;
142                 setbits_le32(&phy->pll, BIT(31) | BIT(30));
143                 if (tmp < 0x3d)
144                         setbits_le32(&phy->pll, tmp + 2);
145                 else
146                         setbits_le32(&phy->pll, 0x3f);
147                 mdelay(100);
148                 writel(0x01FFFF7F, &phy->ctrl);
149                 writel(0x8063b000, &phy->unk1);
150                 writel(0x0F8246B5, &phy->unk2);
151                 break;
152         case 2:
153                 writel(0x39dc5040, &phy->pll);
154                 writel(0x80084380 | (phy_div - 1), &phy->clk);
155                 mdelay(10);
156                 writel(0x00000001, &phy->unk3);
157                 setbits_le32(&phy->pll, BIT(25));
158                 mdelay(100);
159                 tmp = (readl(&phy->status) & 0x1f800) >> 11;
160                 setbits_le32(&phy->pll, BIT(31) | BIT(30));
161                 setbits_le32(&phy->pll, tmp);
162                 writel(0x01FFFF7F, &phy->ctrl);
163                 writel(0x8063a800, &phy->unk1);
164                 writel(0x0F81C485, &phy->unk2);
165                 break;
166         case 4:
167                 writel(0x39dc5040, &phy->pll);
168                 writel(0x80084340 | (phy_div - 1), &phy->clk);
169                 mdelay(10);
170                 writel(0x00000001, &phy->unk3);
171                 setbits_le32(&phy->pll, BIT(25));
172                 mdelay(100);
173                 tmp = (readl(&phy->status) & 0x1f800) >> 11;
174                 setbits_le32(&phy->pll, BIT(31) | BIT(30));
175                 setbits_le32(&phy->pll, tmp);
176                 writel(0x01FFFF7F, &phy->ctrl);
177                 writel(0x8063b000, &phy->unk1);
178                 writel(0x0F81C405, &phy->unk2);
179                 break;
180         case 11:
181                 writel(0x39dc5040, &phy->pll);
182                 writel(0x80084300 | (phy_div - 1), &phy->clk);
183                 mdelay(10);
184                 writel(0x00000001, &phy->unk3);
185                 setbits_le32(&phy->pll, BIT(25));
186                 mdelay(100);
187                 tmp = (readl(&phy->status) & 0x1f800) >> 11;
188                 setbits_le32(&phy->pll, BIT(31) | BIT(30));
189                 setbits_le32(&phy->pll, tmp);
190                 writel(0x01FFFF7F, &phy->ctrl);
191                 writel(0x8063b000, &phy->unk1);
192                 writel(0x0F81C405, &phy->unk2);
193                 break;
194         }
195 }
196
197 static void sunxi_dw_hdmi_pll_set(uint clk_khz, int *phy_div)
198 {
199         int value, n, m, div, diff;
200         int best_n = 0, best_m = 0, best_div = 0, best_diff = 0x0FFFFFFF;
201
202         /*
203          * Find the lowest divider resulting in a matching clock. If there
204          * is no match, pick the closest lower clock, as monitors tend to
205          * not sync to higher frequencies.
206          */
207         for (div = 1; div <= 16; div++) {
208                 int target = clk_khz * div;
209
210                 if (target < 192000)
211                         continue;
212                 if (target > 912000)
213                         continue;
214
215                 for (m = 1; m <= 16; m++) {
216                         n = (m * target) / 24000;
217
218                         if (n >= 1 && n <= 128) {
219                                 value = (24000 * n) / m / div;
220                                 diff = clk_khz - value;
221                                 if (diff < best_diff) {
222                                         best_diff = diff;
223                                         best_m = m;
224                                         best_n = n;
225                                         best_div = div;
226                                 }
227                         }
228                 }
229         }
230
231         *phy_div = best_div;
232
233         clock_set_pll3_factors(best_m, best_n);
234         debug("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n",
235               clk_khz, (clock_get_pll3() / 1000) / best_div,
236               best_n, best_m, best_div);
237 }
238
239 static void sunxi_dw_hdmi_lcdc_init(int mux, const struct display_timing *edid,
240                                     int bpp)
241 {
242         struct sunxi_ccm_reg * const ccm =
243                 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
244         int div = DIV_ROUND_UP(clock_get_pll3(), edid->pixelclock.typ);
245         struct sunxi_lcdc_reg *lcdc;
246
247         if (mux == 0) {
248                 lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
249
250                 /* Reset off */
251                 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
252
253                 /* Clock on */
254                 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
255                 writel(CCM_LCD0_CTRL_GATE | CCM_LCD0_CTRL_M(div),
256                        &ccm->lcd0_clk_cfg);
257         } else {
258                 lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD1_BASE;
259
260                 /* Reset off */
261                 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD1);
262
263                 /* Clock on */
264                 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD1);
265                 writel(CCM_LCD1_CTRL_GATE | CCM_LCD1_CTRL_M(div),
266                        &ccm->lcd1_clk_cfg);
267         }
268
269         lcdc_init(lcdc);
270         lcdc_tcon1_mode_set(lcdc, edid, false, false);
271         lcdc_enable(lcdc, bpp);
272 }
273
274 static int sunxi_dw_hdmi_phy_cfg(struct dw_hdmi *hdmi, uint mpixelclock)
275 {
276         int phy_div;
277
278         sunxi_dw_hdmi_pll_set(mpixelclock / 1000, &phy_div);
279         sunxi_dw_hdmi_phy_set(hdmi, mpixelclock, phy_div);
280
281         return 0;
282 }
283
284 static int sunxi_dw_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size)
285 {
286         struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
287
288         return dw_hdmi_read_edid(&priv->hdmi, buf, buf_size);
289 }
290
291 static bool sunxi_dw_hdmi_mode_valid(struct udevice *dev,
292                                      const struct display_timing *timing)
293 {
294         return timing->pixelclock.typ <= 297000000;
295 }
296
297 static int sunxi_dw_hdmi_enable(struct udevice *dev, int panel_bpp,
298                                 const struct display_timing *edid)
299 {
300         struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
301         struct sunxi_hdmi_phy * const phy =
302                 (struct sunxi_hdmi_phy *)(priv->hdmi.ioaddr + HDMI_PHY_OFFS);
303         struct display_plat *uc_plat = dev_get_uclass_plat(dev);
304         int ret;
305
306         ret = dw_hdmi_enable(&priv->hdmi, edid);
307         if (ret)
308                 return ret;
309
310         sunxi_dw_hdmi_lcdc_init(uc_plat->source_id, edid, panel_bpp);
311
312         if (edid->flags & DISPLAY_FLAGS_VSYNC_LOW)
313                 setbits_le32(&phy->pol, 0x200);
314
315         if (edid->flags & DISPLAY_FLAGS_HSYNC_LOW)
316                 setbits_le32(&phy->pol, 0x100);
317
318         setbits_le32(&phy->ctrl, 0xf << 12);
319
320         /*
321          * This is last hdmi access before boot, so scramble addresses
322          * again or othwerwise BSP driver won't work. Dummy read is
323          * needed or otherwise last write doesn't get written correctly.
324          */
325         (void)readb(priv->hdmi.ioaddr);
326         writel(0, &phy->unscramble);
327
328         return 0;
329 }
330
331 static int sunxi_dw_hdmi_probe(struct udevice *dev)
332 {
333         struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
334         struct sunxi_ccm_reg * const ccm =
335                 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
336         int ret;
337
338         if (priv->hvcc)
339                 regulator_set_enable(priv->hvcc, true);
340
341         /* Set pll3 to 297 MHz */
342         clock_set_pll3(297000000);
343
344         /* Set hdmi parent to pll3 */
345         clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
346                         CCM_HDMI_CTRL_PLL3);
347
348         /* This reset is referenced from the PHY devicetree node. */
349         setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI2);
350
351         ret = reset_deassert_bulk(&priv->resets);
352         if (ret)
353                 return ret;
354
355         ret = clk_enable_bulk(&priv->clocks);
356         if (ret)
357                 return ret;
358
359         sunxi_dw_hdmi_phy_init(&priv->hdmi);
360
361         ret = dw_hdmi_phy_wait_for_hpd(&priv->hdmi);
362         if (ret < 0) {
363                 debug("hdmi can not get hpd signal\n");
364                 return -1;
365         }
366
367         dw_hdmi_init(&priv->hdmi);
368
369         return 0;
370 }
371
372 static int sunxi_dw_hdmi_of_to_plat(struct udevice *dev)
373 {
374         struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
375         struct dw_hdmi *hdmi = &priv->hdmi;
376         int ret;
377
378         hdmi->ioaddr = (ulong)dev_read_addr(dev);
379         hdmi->i2c_clk_high = 0xd8;
380         hdmi->i2c_clk_low = 0xfe;
381         hdmi->reg_io_width = 1;
382         hdmi->phy_set = sunxi_dw_hdmi_phy_cfg;
383
384         ret = reset_get_bulk(dev, &priv->resets);
385         if (ret)
386                 return ret;
387
388         ret = clk_get_bulk(dev, &priv->clocks);
389         if (ret)
390                 return ret;
391
392         ret = device_get_supply_regulator(dev, "hvcc-supply", &priv->hvcc);
393         if (ret)
394                 priv->hvcc = NULL;
395
396         return 0;
397 }
398
399 static const struct dm_display_ops sunxi_dw_hdmi_ops = {
400         .read_edid = sunxi_dw_hdmi_read_edid,
401         .enable = sunxi_dw_hdmi_enable,
402         .mode_valid = sunxi_dw_hdmi_mode_valid,
403 };
404
405 static const struct udevice_id sunxi_dw_hdmi_ids[] = {
406         { .compatible = "allwinner,sun8i-a83t-dw-hdmi" },
407         { }
408 };
409
410 U_BOOT_DRIVER(sunxi_dw_hdmi) = {
411         .name           = "sunxi_dw_hdmi",
412         .id             = UCLASS_DISPLAY,
413         .of_match       = sunxi_dw_hdmi_ids,
414         .probe          = sunxi_dw_hdmi_probe,
415         .of_to_plat     = sunxi_dw_hdmi_of_to_plat,
416         .priv_auto      = sizeof(struct sunxi_dw_hdmi_priv),
417         .ops            = &sunxi_dw_hdmi_ops,
418 };