blob: 9110a4848211772cde55361f13ed16237657a520 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02002/*
3 * Display driver for Allwinner SoCs.
4 *
5 * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
Hans de Goedec06e00e2015-08-03 19:20:26 +02006 * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02007 */
8
9#include <common.h>
Jagan Teki5bc34cb2021-02-22 00:12:34 +000010#include <display.h>
11#include <dm.h>
Simon Glass63334482019-11-14 12:57:39 -070012#include <cpu_func.h>
Heinrich Schuchardt084d3d92018-03-03 10:30:17 +010013#include <efi_loader.h>
Simon Glass6980b6b2019-11-14 12:57:45 -070014#include <init.h>
Simon Glass495a5dc2019-11-14 12:57:30 -070015#include <time.h>
Simon Glassdbd79542020-05-10 11:40:11 -060016#include <linux/delay.h>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020017
18#include <asm/arch/clock.h>
19#include <asm/arch/display.h>
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +020020#include <asm/arch/lcdc.h>
Hans de Goede663ae652016-08-19 15:25:41 +020021#include <asm/arch/pwm.h>
Jernej Skrabec8531d082017-05-10 18:46:28 +020022#include <asm/arch/tve.h>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020023#include <asm/global_data.h>
Hans de Goede7e68a1b2014-12-21 16:28:32 +010024#include <asm/gpio.h>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020025#include <asm/io.h>
Hans de Goeded9ee84b2015-10-03 15:18:33 +020026#include <axp_pmic.h>
Hans de Goedea5aa95f2014-12-19 16:05:12 +010027#include <errno.h>
Luc Verhaegen4869a8c2014-08-13 07:55:07 +020028#include <fdtdec.h>
29#include <fdt_support.h>
Hans de Goede613dade2015-02-16 17:49:47 +010030#include <i2c.h>
Hans de Goeded955f442015-08-05 00:06:47 +020031#include <malloc.h>
Jagan Teki5bc34cb2021-02-22 00:12:34 +000032#include <video.h>
Jagan Teki5bc34cb2021-02-22 00:12:34 +000033#include <dm/uclass-internal.h>
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +020034#include "../videomodes.h"
35#include "../anx9804.h"
36#include "../hitachi_tx18d42vm_lcd.h"
37#include "../ssd2828.h"
Icenowy Zheng067a6972017-10-26 11:14:45 +080038#include "simplefb_common.h"
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020039
Hans de Goede2d5d3022015-01-22 21:02:42 +010040#ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW
41#define PWM_ON 0
42#define PWM_OFF 1
43#else
44#define PWM_ON 1
45#define PWM_OFF 0
46#endif
47
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020048DECLARE_GLOBAL_DATA_PTR;
49
Jagan Teki5bc34cb2021-02-22 00:12:34 +000050/* Maximum LCD size we support */
51#define LCD_MAX_WIDTH 3840
52#define LCD_MAX_HEIGHT 2160
53#define LCD_MAX_LOG2_BPP VIDEO_BPP32
54
Hans de Goedea0b1b732014-12-21 14:37:45 +010055enum sunxi_monitor {
56 sunxi_monitor_none,
57 sunxi_monitor_dvi,
58 sunxi_monitor_hdmi,
59 sunxi_monitor_lcd,
60 sunxi_monitor_vga,
Hans de Goedec06e00e2015-08-03 19:20:26 +020061 sunxi_monitor_composite_pal,
62 sunxi_monitor_composite_ntsc,
63 sunxi_monitor_composite_pal_m,
64 sunxi_monitor_composite_pal_nc,
Hans de Goedea0b1b732014-12-21 14:37:45 +010065};
Hans de Goedec06e00e2015-08-03 19:20:26 +020066#define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
Hans de Goedea0b1b732014-12-21 14:37:45 +010067
Jagan Teki5bc34cb2021-02-22 00:12:34 +000068struct sunxi_display_priv {
Hans de Goedea0b1b732014-12-21 14:37:45 +010069 enum sunxi_monitor monitor;
Hans de Goede7e68a1b2014-12-21 16:28:32 +010070 unsigned int depth;
Hans de Goeded955f442015-08-05 00:06:47 +020071 unsigned int fb_addr;
Hans de Goede18799252015-02-02 18:00:53 +010072 unsigned int fb_size;
Jagan Teki5bc34cb2021-02-22 00:12:34 +000073};
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020074
Hans de Goedec06e00e2015-08-03 19:20:26 +020075const struct ctfb_res_modes composite_video_modes[2] = {
76 /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */
77 { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED },
78 { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED },
79};
80
Hans de Goedee9544592014-12-23 23:04:35 +010081#ifdef CONFIG_VIDEO_HDMI
82
Hans de Goedea5aa95f2014-12-19 16:05:12 +010083/*
84 * Wait up to 200ms for value to be set in given part of reg.
85 */
86static int await_completion(u32 *reg, u32 mask, u32 val)
87{
88 unsigned long tmo = timer_get_us() + 200000;
89
90 while ((readl(reg) & mask) != val) {
91 if (timer_get_us() > tmo) {
92 printf("DDC: timeout reading EDID\n");
93 return -ETIME;
94 }
95 }
96 return 0;
97}
98
Hans de Goede91593712014-12-28 09:13:21 +010099static int sunxi_hdmi_hpd_detect(int hpd_delay)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200100{
101 struct sunxi_ccm_reg * const ccm =
102 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
103 struct sunxi_hdmi_reg * const hdmi =
104 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Hans de Goede91593712014-12-28 09:13:21 +0100105 unsigned long tmo = timer_get_us() + hpd_delay * 1000;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200106
107 /* Set pll3 to 300MHz */
108 clock_set_pll3(300000000);
109
110 /* Set hdmi parent to pll3 */
111 clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
112 CCM_HDMI_CTRL_PLL3);
113
114 /* Set ahb gating to pass */
Hans de Goedef07872b2015-04-06 20:33:34 +0200115#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100116 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
117#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200118 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
119
120 /* Clock on */
121 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
122
123 writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl);
124 writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0);
125
Priit Laesc9ecd5c2018-12-19 15:06:08 +0200126 /* Enable PLLs for eventual DDC */
127 writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
128 &hdmi->pad_ctrl1);
129 writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
130 &hdmi->pll_ctrl);
131 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
132
Hans de Goede205a30c2014-12-20 15:15:23 +0100133 while (timer_get_us() < tmo) {
134 if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT)
135 return 1;
136 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200137
Hans de Goede205a30c2014-12-20 15:15:23 +0100138 return 0;
Hans de Goede695bda42014-12-19 15:13:57 +0100139}
140
141static void sunxi_hdmi_shutdown(void)
142{
143 struct sunxi_ccm_reg * const ccm =
144 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
145 struct sunxi_hdmi_reg * const hdmi =
146 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200147
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200148 clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE);
149 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
150 clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
Hans de Goedef07872b2015-04-06 20:33:34 +0200151#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100152 clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
153#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200154 clock_set_pll3(0);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200155}
156
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100157static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
158{
159 struct sunxi_hdmi_reg * const hdmi =
160 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
161
162 setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
163 writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
164 SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
165 SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
166 SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
167#ifndef CONFIG_MACH_SUN6I
168 writel(n, &hdmi->ddc_byte_count);
169 writel(cmnd, &hdmi->ddc_cmnd);
170#else
171 writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
172#endif
173 setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
174
175 return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
176}
177
178static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
179{
180 struct sunxi_hdmi_reg * const hdmi =
181 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
182 int i, n;
183
184 while (count > 0) {
185 if (count > 16)
186 n = 16;
187 else
188 n = count;
189
190 if (sunxi_hdmi_ddc_do_command(
191 SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
192 offset, n))
193 return -ETIME;
194
195 for (i = 0; i < n; i++)
196 *buf++ = readb(&hdmi->ddc_fifo_data);
197
198 offset += n;
199 count -= n;
200 }
201
202 return 0;
203}
204
Hans de Goede45b8f7b2014-12-20 14:01:48 +0100205static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
206{
207 int r, retries = 2;
208
209 do {
210 r = sunxi_hdmi_ddc_read(block * 128, buf, 128);
211 if (r)
212 continue;
213 r = edid_check_checksum(buf);
214 if (r) {
215 printf("EDID block %d: checksum error%s\n",
216 block, retries ? ", retrying" : "");
217 }
218 } while (r && retries--);
219
220 return r;
221}
222
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000223static int sunxi_hdmi_edid_get_mode(struct sunxi_display_priv *sunxi_display,
224 struct ctfb_res_modes *mode,
Priit Laesa9d6db72018-12-19 15:06:09 +0200225 bool verbose_mode)
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100226{
227 struct edid1_info edid1;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100228 struct edid_cea861_info cea681[4];
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100229 struct edid_detailed_timing *t =
230 (struct edid_detailed_timing *)edid1.monitor_details.timing;
231 struct sunxi_hdmi_reg * const hdmi =
232 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
233 struct sunxi_ccm_reg * const ccm =
234 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100235 int i, r, ext_blocks = 0;
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100236
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100237 /* Reset i2c controller */
238 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
239 writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
240 SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
241 SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
242 SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
243 if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
244 return -EIO;
245
246 writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
247#ifndef CONFIG_MACH_SUN6I
248 writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
249 SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
250#endif
251
Hans de Goede45b8f7b2014-12-20 14:01:48 +0100252 r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100253 if (r == 0) {
254 r = edid_check_info(&edid1);
255 if (r) {
Priit Laesa9d6db72018-12-19 15:06:09 +0200256 if (verbose_mode)
257 printf("EDID: invalid EDID data\n");
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100258 r = -EINVAL;
259 }
260 }
261 if (r == 0) {
262 ext_blocks = edid1.extension_flag;
263 if (ext_blocks > 4)
264 ext_blocks = 4;
265 for (i = 0; i < ext_blocks; i++) {
266 if (sunxi_hdmi_edid_get_block(1 + i,
267 (u8 *)&cea681[i]) != 0) {
268 ext_blocks = i;
269 break;
270 }
271 }
272 }
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100273
274 /* Disable DDC engine, no longer needed */
275 clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
276 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
277
278 if (r)
279 return r;
280
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100281 /* We want version 1.3 or 1.2 with detailed timing info */
282 if (edid1.version != 1 || (edid1.revision < 3 &&
283 !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
284 printf("EDID: unsupported version %d.%d\n",
285 edid1.version, edid1.revision);
286 return -EINVAL;
287 }
288
289 /* Take the first usable detailed timing */
290 for (i = 0; i < 4; i++, t++) {
291 r = video_edid_dtd_to_ctfb_res_modes(t, mode);
292 if (r == 0)
293 break;
294 }
295 if (i == 4) {
296 printf("EDID: no usable detailed timing found\n");
297 return -ENOENT;
298 }
299
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100300 /* Check for basic audio support, if found enable hdmi output */
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000301 sunxi_display->monitor = sunxi_monitor_dvi;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100302 for (i = 0; i < ext_blocks; i++) {
303 if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
304 cea681[i].revision < 2)
305 continue;
306
307 if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000308 sunxi_display->monitor = sunxi_monitor_hdmi;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100309 }
310
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100311 return 0;
312}
313
Hans de Goedee9544592014-12-23 23:04:35 +0100314#endif /* CONFIG_VIDEO_HDMI */
315
Hans de Goedec3cc4262015-01-19 08:44:07 +0100316#ifdef CONFIG_MACH_SUN4I
317/*
318 * Testing has shown that on sun4i the display backend engine does not have
319 * deep enough fifo-s causing flickering / tearing in full-hd mode due to
320 * fifo underruns. So on sun4i we use the display frontend engine to do the
321 * dma from memory, as the frontend does have deep enough fifo-s.
322 */
323
324static const u32 sun4i_vert_coef[32] = {
325 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
326 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
327 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
328 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
329 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
330 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
331 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
332 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
333};
334
335static const u32 sun4i_horz_coef[64] = {
336 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
337 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
338 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
339 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
340 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
341 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
342 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
343 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
344 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
345 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
346 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
347 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
348 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
349 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
350 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
351 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
352};
353
354static void sunxi_frontend_init(void)
355{
356 struct sunxi_ccm_reg * const ccm =
357 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
358 struct sunxi_de_fe_reg * const de_fe =
359 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
360 int i;
361
362 /* Clocks on */
363 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0);
364 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0);
365 clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000);
366
367 setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN);
368
369 for (i = 0; i < 32; i++) {
370 writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]);
371 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]);
372 writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]);
373 writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]);
374 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]);
375 writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]);
376 }
377
378 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY);
379}
380
381static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
382 unsigned int address)
383{
384 struct sunxi_de_fe_reg * const de_fe =
385 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
386
387 setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS);
Tom Rinibb4dd962022-11-16 13:10:37 -0500388 writel(CFG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100389 writel(mode->xres * 4, &de_fe->ch0_stride);
390 writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt);
391 writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt);
392
393 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
394 &de_fe->ch0_insize);
395 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
396 &de_fe->ch0_outsize);
397 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact);
398 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact);
399
400 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
401 &de_fe->ch1_insize);
402 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
403 &de_fe->ch1_outsize);
404 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact);
405 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact);
406
407 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY);
408}
409
410static void sunxi_frontend_enable(void)
411{
412 struct sunxi_de_fe_reg * const de_fe =
413 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
414
415 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START);
416}
417#else
418static void sunxi_frontend_init(void) {}
419static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
420 unsigned int address) {}
421static void sunxi_frontend_enable(void) {}
422#endif
423
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000424static bool sunxi_is_composite(enum sunxi_monitor monitor)
Hans de Goedec06e00e2015-08-03 19:20:26 +0200425{
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000426 switch (monitor) {
Hans de Goedec06e00e2015-08-03 19:20:26 +0200427 case sunxi_monitor_none:
428 case sunxi_monitor_dvi:
429 case sunxi_monitor_hdmi:
430 case sunxi_monitor_lcd:
431 case sunxi_monitor_vga:
432 return false;
433 case sunxi_monitor_composite_pal:
434 case sunxi_monitor_composite_ntsc:
435 case sunxi_monitor_composite_pal_m:
436 case sunxi_monitor_composite_pal_nc:
437 return true;
438 }
439
440 return false; /* Never reached */
441}
442
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200443/*
444 * This is the entity that mixes and matches the different layers and inputs.
445 * Allwinner calls it the back-end, but i like composer better.
446 */
447static void sunxi_composer_init(void)
448{
449 struct sunxi_ccm_reg * const ccm =
450 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
451 struct sunxi_de_be_reg * const de_be =
452 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
453 int i;
454
Hans de Goedec3cc4262015-01-19 08:44:07 +0100455 sunxi_frontend_init();
456
Hans de Goedef07872b2015-04-06 20:33:34 +0200457#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100458 /* Reset off */
459 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0);
460#endif
461
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200462 /* Clocks on */
463 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100464#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200465 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100466#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200467 clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000);
468
469 /* Engine bug, clear registers after reset */
470 for (i = 0x0800; i < 0x1000; i += 4)
471 writel(0, SUNXI_DE_BE0_BASE + i);
472
473 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE);
474}
475
Priit Laes82482d62018-10-23 20:20:31 +0300476static const u32 sunxi_rgb2yuv_coef[12] = {
Hans de Goedec06e00e2015-08-03 19:20:26 +0200477 0x00000107, 0x00000204, 0x00000064, 0x00000108,
478 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,
479 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
480};
481
Hans de Goedeccb0ed52014-12-19 13:46:33 +0100482static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000483 unsigned int address,
484 enum sunxi_monitor monitor)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200485{
486 struct sunxi_de_be_reg * const de_be =
487 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
Hans de Goedec06e00e2015-08-03 19:20:26 +0200488 int i;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200489
Hans de Goedec3cc4262015-01-19 08:44:07 +0100490 sunxi_frontend_mode_set(mode, address);
491
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200492 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
493 &de_be->disp_size);
494 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
495 &de_be->layer0_size);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100496#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200497 writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride);
498 writel(address << 3, &de_be->layer0_addr_low32b);
499 writel(address >> 29, &de_be->layer0_addr_high4b);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100500#else
501 writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl);
502#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200503 writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl);
504
505 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE);
Hans de Goede1977bbb2015-08-02 16:49:29 +0200506 if (mode->vmode == FB_VMODE_INTERLACED)
507 setbits_le32(&de_be->mode,
Hans de Goede8a195ca2015-08-06 12:08:33 +0200508#ifndef CONFIG_MACH_SUN5I
Hans de Goede1977bbb2015-08-02 16:49:29 +0200509 SUNXI_DE_BE_MODE_DEFLICKER_ENABLE |
Hans de Goede8a195ca2015-08-06 12:08:33 +0200510#endif
Hans de Goede1977bbb2015-08-02 16:49:29 +0200511 SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200512
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000513 if (sunxi_is_composite(monitor)) {
Hans de Goedec06e00e2015-08-03 19:20:26 +0200514 writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
515 &de_be->output_color_ctrl);
516 for (i = 0; i < 12; i++)
517 writel(sunxi_rgb2yuv_coef[i],
518 &de_be->output_color_coef[i]);
519 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200520}
521
Hans de Goede4125f922014-12-21 14:49:34 +0100522static void sunxi_composer_enable(void)
523{
524 struct sunxi_de_be_reg * const de_be =
525 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
526
Hans de Goedec3cc4262015-01-19 08:44:07 +0100527 sunxi_frontend_enable();
528
Hans de Goede4125f922014-12-21 14:49:34 +0100529 setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS);
530 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
531}
532
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200533static void sunxi_lcdc_init(void)
534{
535 struct sunxi_ccm_reg * const ccm =
536 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
537 struct sunxi_lcdc_reg * const lcdc =
538 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
539
540 /* Reset off */
Hans de Goedef07872b2015-04-06 20:33:34 +0200541#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100542 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
543#else
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200544 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST);
Hans de Goedef651e0a2014-11-14 17:42:14 +0100545#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200546
547 /* Clock on */
548 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100549#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Hans de Goede5f67b862015-05-14 18:52:54 +0200550#ifdef CONFIG_SUNXI_GEN_SUN6I
551 setbits_le32(&ccm->ahb_reset2_cfg, 1 << AHB_RESET_OFFSET_LVDS);
552#else
Hans de Goede797a0f52015-01-01 22:04:34 +0100553 setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
554#endif
Hans de Goede5f67b862015-05-14 18:52:54 +0200555#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200556
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200557 lcdc_init(lcdc);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200558}
559
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100560static void sunxi_lcdc_panel_enable(void)
561{
Hans de Goedece9e3322015-02-16 17:26:41 +0100562 int pin, reset_pin;
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100563
564 /*
565 * Start with backlight disabled to avoid the screen flashing to
566 * white while the lcd inits.
567 */
568 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede7783ab22015-04-22 17:45:59 +0200569 if (pin >= 0) {
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100570 gpio_request(pin, "lcd_backlight_enable");
571 gpio_direction_output(pin, 0);
572 }
573
574 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede7783ab22015-04-22 17:45:59 +0200575 if (pin >= 0) {
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100576 gpio_request(pin, "lcd_backlight_pwm");
Hans de Goede2d5d3022015-01-22 21:02:42 +0100577 gpio_direction_output(pin, PWM_OFF);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100578 }
579
Hans de Goedece9e3322015-02-16 17:26:41 +0100580 reset_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_RESET);
Hans de Goede7783ab22015-04-22 17:45:59 +0200581 if (reset_pin >= 0) {
Hans de Goedece9e3322015-02-16 17:26:41 +0100582 gpio_request(reset_pin, "lcd_reset");
583 gpio_direction_output(reset_pin, 0); /* Assert reset */
584 }
585
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100586 /* Give the backlight some time to turn off and power up the panel. */
587 mdelay(40);
588 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
Hans de Goede7783ab22015-04-22 17:45:59 +0200589 if (pin >= 0) {
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100590 gpio_request(pin, "lcd_power");
591 gpio_direction_output(pin, 1);
592 }
Hans de Goedece9e3322015-02-16 17:26:41 +0100593
Hans de Goede7783ab22015-04-22 17:45:59 +0200594 if (reset_pin >= 0)
Hans de Goedece9e3322015-02-16 17:26:41 +0100595 gpio_direction_output(reset_pin, 1); /* De-assert reset */
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100596}
597
598static void sunxi_lcdc_backlight_enable(void)
599{
600 int pin;
601
602 /*
603 * We want to have scanned out at least one frame before enabling the
604 * backlight to avoid the screen flashing to white when we enable it.
605 */
606 mdelay(40);
607
608 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede7783ab22015-04-22 17:45:59 +0200609 if (pin >= 0)
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100610 gpio_direction_output(pin, 1);
611
612 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede663ae652016-08-19 15:25:41 +0200613#ifdef SUNXI_PWM_PIN0
614 if (pin == SUNXI_PWM_PIN0) {
615 writel(SUNXI_PWM_CTRL_POLARITY0(PWM_ON) |
616 SUNXI_PWM_CTRL_ENABLE0 |
617 SUNXI_PWM_CTRL_PRESCALE0(0xf), SUNXI_PWM_CTRL_REG);
618 writel(SUNXI_PWM_PERIOD_80PCT, SUNXI_PWM_CH0_PERIOD);
619 sunxi_gpio_set_cfgpin(pin, SUNXI_PWM_MUX);
620 return;
621 }
622#endif
Hans de Goede7783ab22015-04-22 17:45:59 +0200623 if (pin >= 0)
Hans de Goede2d5d3022015-01-22 21:02:42 +0100624 gpio_direction_output(pin, PWM_ON);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100625}
626
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000627static void sunxi_lcdc_tcon0_mode_set(struct sunxi_display_priv *sunxi_display,
628 const struct ctfb_res_modes *mode,
Hans de Goede18366f72015-01-25 15:33:07 +0100629 bool for_ext_vga_dac)
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100630{
631 struct sunxi_lcdc_reg * const lcdc =
632 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Vasily Khoruzhickf079f932017-10-26 21:51:51 -0700633 struct sunxi_ccm_reg * const ccm =
634 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200635 int clk_div, clk_double, pin;
Jernej Skrabecccfbe5f2017-03-27 19:22:30 +0200636 struct display_timing timing;
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100637
Lawrence Yuf721f962016-03-04 09:08:56 -0800638#if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS
639 for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) {
640#else
Hans de Goede91f1b822015-08-08 16:13:53 +0200641 for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) {
Lawrence Yuf721f962016-03-04 09:08:56 -0800642#endif
Hans de Goede797a0f52015-01-01 22:04:34 +0100643#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Paul Kocialkowskiae358a42015-03-22 18:12:22 +0100644 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100645#endif
646#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Paul Kocialkowskiae358a42015-03-22 18:12:22 +0100647 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LVDS0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100648#endif
Hans de Goede91f1b822015-08-08 16:13:53 +0200649#ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804
650 sunxi_gpio_set_drv(pin, 3);
651#endif
652 }
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100653
Vasily Khoruzhickf079f932017-10-26 21:51:51 -0700654 lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double,
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000655 sunxi_is_composite(sunxi_display->monitor));
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100656
Giulio Benetti953f5ed2020-04-08 17:10:12 +0200657 video_ctfb_mode_to_display_timing(mode, &timing);
Jernej Skrabecccfbe5f2017-03-27 19:22:30 +0200658 lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000659 sunxi_display->depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100660}
661
Hans de Goedec06e00e2015-08-03 19:20:26 +0200662#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goede4125f922014-12-21 14:49:34 +0100663static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
Hans de Goedec3d15042014-12-27 15:19:23 +0100664 int *clk_div, int *clk_double,
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000665 bool use_portd_hvsync,
666 enum sunxi_monitor monitor)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200667{
668 struct sunxi_lcdc_reg * const lcdc =
669 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Vasily Khoruzhickf079f932017-10-26 21:51:51 -0700670 struct sunxi_ccm_reg * const ccm =
671 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Jernej Skrabecccfbe5f2017-03-27 19:22:30 +0200672 struct display_timing timing;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200673
Giulio Benetti953f5ed2020-04-08 17:10:12 +0200674 video_ctfb_mode_to_display_timing(mode, &timing);
Jernej Skrabecccfbe5f2017-03-27 19:22:30 +0200675 lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000676 sunxi_is_composite(monitor));
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200677
Hans de Goedec3d15042014-12-27 15:19:23 +0100678 if (use_portd_hvsync) {
Paul Kocialkowskiae358a42015-03-22 18:12:22 +0100679 sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
680 sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0);
Hans de Goedec3d15042014-12-27 15:19:23 +0100681 }
Hans de Goede8a195ca2015-08-06 12:08:33 +0200682
Vasily Khoruzhickf079f932017-10-26 21:51:51 -0700683 lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double,
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000684 sunxi_is_composite(monitor));
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200685}
Hans de Goedec06e00e2015-08-03 19:20:26 +0200686#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
Hans de Goede260f5202014-12-25 13:58:06 +0100687
688#ifdef CONFIG_VIDEO_HDMI
Hans de Goedef651e0a2014-11-14 17:42:14 +0100689
Hans de Goedea2017e82014-12-20 13:38:06 +0100690static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
691{
692 struct sunxi_hdmi_reg * const hdmi =
693 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
694 u8 checksum = 0;
695 u8 avi_info_frame[17] = {
696 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00,
697 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
698 0x00
699 };
700 u8 vendor_info_frame[19] = {
701 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40,
702 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
703 0x00, 0x00, 0x00
704 };
705 int i;
706
707 if (mode->pixclock_khz <= 27000)
708 avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */
709 else
710 avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */
711
712 if (mode->xres * 100 / mode->yres < 156)
713 avi_info_frame[5] |= 0x18; /* 4 : 3 */
714 else
715 avi_info_frame[5] |= 0x28; /* 16 : 9 */
716
717 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
718 checksum += avi_info_frame[i];
719
720 avi_info_frame[3] = 0x100 - checksum;
721
722 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
723 writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]);
724
725 writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0);
726 writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1);
727
728 for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++)
729 writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]);
730
731 writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0);
732 writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1);
733
734 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI);
735}
736
Hans de Goedeccb0ed52014-12-19 13:46:33 +0100737static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000738 int clk_div, int clk_double,
739 enum sunxi_monitor monitor)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200740{
741 struct sunxi_hdmi_reg * const hdmi =
742 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
743 int x, y;
744
745 /* Write clear interrupt status bits */
746 writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq);
747
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000748 if (monitor == sunxi_monitor_hdmi)
Hans de Goedea2017e82014-12-20 13:38:06 +0100749 sunxi_hdmi_setup_info_frames(mode);
750
Hans de Goede95576692014-12-20 13:51:16 +0100751 /* Set input sync enable */
752 writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown);
753
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200754 /* Init various registers, select pll3 as clock source */
755 writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity);
756 writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0);
757 writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1);
758 writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl);
759 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
760
761 /* Setup clk div and doubler */
762 clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK,
763 SUNXI_HDMI_PLL_CTRL_DIV(clk_div));
764 if (!clk_double)
765 setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE);
766
767 /* Setup timing registers */
768 writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres),
769 &hdmi->video_size);
770
771 x = mode->hsync_len + mode->left_margin;
772 y = mode->vsync_len + mode->upper_margin;
773 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp);
774
775 x = mode->right_margin;
776 y = mode->lower_margin;
777 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp);
778
779 x = mode->hsync_len;
780 y = mode->vsync_len;
781 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw);
782
783 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
784 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR);
785
786 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
787 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER);
788}
789
Hans de Goede4125f922014-12-21 14:49:34 +0100790static void sunxi_hdmi_enable(void)
791{
792 struct sunxi_hdmi_reg * const hdmi =
793 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
794
795 udelay(100);
796 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE);
797}
798
Hans de Goedee9544592014-12-23 23:04:35 +0100799#endif /* CONFIG_VIDEO_HDMI */
800
Hans de Goedec06e00e2015-08-03 19:20:26 +0200801#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goede260f5202014-12-25 13:58:06 +0100802
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000803static void sunxi_tvencoder_mode_set(enum sunxi_monitor monitor)
Hans de Goede260f5202014-12-25 13:58:06 +0100804{
805 struct sunxi_ccm_reg * const ccm =
806 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
807 struct sunxi_tve_reg * const tve =
808 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
809
Hans de Goede8a195ca2015-08-06 12:08:33 +0200810 /* Reset off */
811 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_TVE_RST);
Hans de Goede260f5202014-12-25 13:58:06 +0100812 /* Clock on */
813 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
814
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000815 switch (monitor) {
Hans de Goedec06e00e2015-08-03 19:20:26 +0200816 case sunxi_monitor_vga:
Jernej Skrabec8531d082017-05-10 18:46:28 +0200817 tvencoder_mode_set(tve, tve_mode_vga);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200818 break;
819 case sunxi_monitor_composite_pal_nc:
Jernej Skrabec8531d082017-05-10 18:46:28 +0200820 tvencoder_mode_set(tve, tve_mode_composite_pal_nc);
821 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +0200822 case sunxi_monitor_composite_pal:
Jernej Skrabec8531d082017-05-10 18:46:28 +0200823 tvencoder_mode_set(tve, tve_mode_composite_pal);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200824 break;
825 case sunxi_monitor_composite_pal_m:
Jernej Skrabec8531d082017-05-10 18:46:28 +0200826 tvencoder_mode_set(tve, tve_mode_composite_pal_m);
827 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +0200828 case sunxi_monitor_composite_ntsc:
Jernej Skrabec8531d082017-05-10 18:46:28 +0200829 tvencoder_mode_set(tve, tve_mode_composite_ntsc);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200830 break;
831 case sunxi_monitor_none:
832 case sunxi_monitor_dvi:
833 case sunxi_monitor_hdmi:
834 case sunxi_monitor_lcd:
835 break;
836 }
Hans de Goede260f5202014-12-25 13:58:06 +0100837}
838
Hans de Goedec06e00e2015-08-03 19:20:26 +0200839#endif /* CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE */
Hans de Goede260f5202014-12-25 13:58:06 +0100840
Hans de Goede115e4b42014-12-23 18:39:52 +0100841static void sunxi_drc_init(void)
842{
Hans de Goedef07872b2015-04-06 20:33:34 +0200843#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede115e4b42014-12-23 18:39:52 +0100844 struct sunxi_ccm_reg * const ccm =
845 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
846
847 /* On sun6i the drc must be clocked even when in pass-through mode */
Vishnu Patekar3702f142015-03-01 23:47:48 +0530848#ifdef CONFIG_MACH_SUN8I_A33
849 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_SAT);
850#endif
Hans de Goede115e4b42014-12-23 18:39:52 +0100851 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0);
852 clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000);
853#endif
854}
855
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +0800856#ifdef CONFIG_VIDEO_VGA_VIA_LCD
857static void sunxi_vga_external_dac_enable(void)
858{
859 int pin;
860
861 pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
Hans de Goede7783ab22015-04-22 17:45:59 +0200862 if (pin >= 0) {
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +0800863 gpio_request(pin, "vga_enable");
864 gpio_direction_output(pin, 1);
865 }
866}
867#endif /* CONFIG_VIDEO_VGA_VIA_LCD */
868
Siarhei Siamashkac02f0522015-01-19 05:23:33 +0200869#ifdef CONFIG_VIDEO_LCD_SSD2828
870static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
871{
872 struct ssd2828_config cfg = {
Samuel Hollandc7ab95d2021-09-11 16:50:48 -0500873 .csx_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
874 .sck_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
875 .sdi_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
876 .sdo_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
877 .reset_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
Siarhei Siamashkac02f0522015-01-19 05:23:33 +0200878 .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
879 .ssd2828_color_depth = 24,
880#ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
881 .mipi_dsi_number_of_data_lanes = 4,
882 .mipi_dsi_bitrate_per_data_lane_mbps = 513,
883 .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
884 .mipi_dsi_delay_after_set_display_on_ms = 200
885#else
886#error MIPI LCD panel needs configuration parameters
887#endif
888 };
889
890 if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
891 printf("SSD2828: SPI pins are not properly configured\n");
892 return 1;
893 }
894 if (cfg.reset_pin == -1) {
895 printf("SSD2828: Reset pin is not properly configured\n");
896 return 1;
897 }
898
899 return ssd2828_init(&cfg, mode);
900}
901#endif /* CONFIG_VIDEO_LCD_SSD2828 */
902
Samuel Holland75fe0f42021-10-08 00:17:24 -0500903#ifdef CONFIG_VIDEO_LCD_PANEL_I2C
904static void sunxi_panel_i2c_init(struct sunxi_display_priv *sunxi_display)
905{
906 const char *name = CONFIG_VIDEO_LCD_PANEL_I2C_NAME;
907 struct udevice *i2c_bus;
908 int ret;
909
910 ret = uclass_get_device_by_name(UCLASS_I2C, name, &i2c_bus);
911 if (ret)
912 return;
913
914 if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804)) {
915 /*
916 * The anx9804 needs 1.8V from eldo3, we do this here
917 * and not via CONFIG_AXP_ELDO3_VOLT from board_init()
918 * to avoid turning this on when using hdmi output.
919 */
920 axp_set_eldo(3, 1800);
921 anx9804_init(i2c_bus, 4,
922 ANX9804_DATA_RATE_1620M,
923 sunxi_display->depth);
924 }
925 if (IS_ENABLED(CONFIG_VIDEO_LCD_TL059WV5C0)) {
926 struct udevice *chip;
927
928 ret = i2c_get_chip(i2c_bus, 0x5c, 1, &chip);
929 if (ret)
930 return;
931
932 dm_i2c_reg_write(chip, 0x04, 0x42); /* Turn on the LCD */
933 }
934}
935#else
936static void sunxi_panel_i2c_init(struct sunxi_display_priv *sunxi_display) {}
937#endif
938
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200939static void sunxi_engines_init(void)
940{
941 sunxi_composer_init();
942 sunxi_lcdc_init();
Hans de Goedef651e0a2014-11-14 17:42:14 +0100943 sunxi_drc_init();
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200944}
945
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000946static void sunxi_mode_set(struct sunxi_display_priv *sunxi_display,
947 const struct ctfb_res_modes *mode,
Hans de Goedea2017e82014-12-20 13:38:06 +0100948 unsigned int address)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200949{
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000950 enum sunxi_monitor monitor = sunxi_display->monitor;
Hans de Goede260f5202014-12-25 13:58:06 +0100951 int __maybe_unused clk_div, clk_double;
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200952 struct sunxi_lcdc_reg * const lcdc =
953 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Jernej Skrabec8531d082017-05-10 18:46:28 +0200954 struct sunxi_tve_reg * __maybe_unused const tve =
955 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
Hans de Goede260f5202014-12-25 13:58:06 +0100956
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000957 switch (sunxi_display->monitor) {
Hans de Goede4125f922014-12-21 14:49:34 +0100958 case sunxi_monitor_none:
959 break;
960 case sunxi_monitor_dvi:
Hans de Goede260f5202014-12-25 13:58:06 +0100961 case sunxi_monitor_hdmi:
Hans de Goedee9544592014-12-23 23:04:35 +0100962#ifdef CONFIG_VIDEO_HDMI
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000963 sunxi_composer_mode_set(mode, address, monitor);
964 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
965 sunxi_hdmi_mode_set(mode, clk_div, clk_double, monitor);
Hans de Goede4125f922014-12-21 14:49:34 +0100966 sunxi_composer_enable();
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000967 lcdc_enable(lcdc, sunxi_display->depth);
Hans de Goede4125f922014-12-21 14:49:34 +0100968 sunxi_hdmi_enable();
Hans de Goedee9544592014-12-23 23:04:35 +0100969#endif
Hans de Goede4125f922014-12-21 14:49:34 +0100970 break;
971 case sunxi_monitor_lcd:
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100972 sunxi_lcdc_panel_enable();
Hans de Goede743fb9552015-01-20 09:23:36 +0100973 if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
974 mdelay(50); /* Wait for lcd controller power on */
975 hitachi_tx18d42vm_init();
976 }
Samuel Holland75fe0f42021-10-08 00:17:24 -0500977 if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_I2C))
978 sunxi_panel_i2c_init(sunxi_display);
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000979 sunxi_composer_mode_set(mode, address, monitor);
980 sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, false);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100981 sunxi_composer_enable();
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000982 lcdc_enable(lcdc, sunxi_display->depth);
Siarhei Siamashkac02f0522015-01-19 05:23:33 +0200983#ifdef CONFIG_VIDEO_LCD_SSD2828
984 sunxi_ssd2828_init(mode);
985#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100986 sunxi_lcdc_backlight_enable();
Hans de Goede4125f922014-12-21 14:49:34 +0100987 break;
988 case sunxi_monitor_vga:
Hans de Goede260f5202014-12-25 13:58:06 +0100989#ifdef CONFIG_VIDEO_VGA
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000990 sunxi_composer_mode_set(mode, address, monitor);
991 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1, monitor);
992 sunxi_tvencoder_mode_set(monitor);
Hans de Goede260f5202014-12-25 13:58:06 +0100993 sunxi_composer_enable();
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000994 lcdc_enable(lcdc, sunxi_display->depth);
Jernej Skrabec8531d082017-05-10 18:46:28 +0200995 tvencoder_enable(tve);
Hans de Goede260f5202014-12-25 13:58:06 +0100996#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Jagan Teki5bc34cb2021-02-22 00:12:34 +0000997 sunxi_composer_mode_set(mode, address, monitor);
998 sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, true);
Hans de Goedeac1633c2014-12-24 12:17:07 +0100999 sunxi_composer_enable();
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001000 lcdc_enable(lcdc, sunxi_display->depth);
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +08001001 sunxi_vga_external_dac_enable();
Hans de Goedeac1633c2014-12-24 12:17:07 +01001002#endif
Hans de Goede4125f922014-12-21 14:49:34 +01001003 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001004 case sunxi_monitor_composite_pal:
1005 case sunxi_monitor_composite_ntsc:
1006 case sunxi_monitor_composite_pal_m:
1007 case sunxi_monitor_composite_pal_nc:
1008#ifdef CONFIG_VIDEO_COMPOSITE
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001009 sunxi_composer_mode_set(mode, address, monitor);
1010 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
1011 sunxi_tvencoder_mode_set(monitor);
Hans de Goedec06e00e2015-08-03 19:20:26 +02001012 sunxi_composer_enable();
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001013 lcdc_enable(lcdc, sunxi_display->depth);
Jernej Skrabec8531d082017-05-10 18:46:28 +02001014 tvencoder_enable(tve);
Hans de Goedec06e00e2015-08-03 19:20:26 +02001015#endif
1016 break;
Hans de Goede4125f922014-12-21 14:49:34 +01001017 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001018}
1019
Hans de Goedea0b1b732014-12-21 14:37:45 +01001020static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
1021{
1022 switch (monitor) {
Hans de Goedec06e00e2015-08-03 19:20:26 +02001023 case sunxi_monitor_dvi: return "dvi";
1024 case sunxi_monitor_hdmi: return "hdmi";
1025 case sunxi_monitor_lcd: return "lcd";
1026 case sunxi_monitor_vga: return "vga";
1027 case sunxi_monitor_composite_pal: return "composite-pal";
1028 case sunxi_monitor_composite_ntsc: return "composite-ntsc";
1029 case sunxi_monitor_composite_pal_m: return "composite-pal-m";
1030 case sunxi_monitor_composite_pal_nc: return "composite-pal-nc";
Bin Meng12de4aa2020-04-06 06:06:58 -07001031 case sunxi_monitor_none: /* fall through */
1032 default: return "none";
Hans de Goedea0b1b732014-12-21 14:37:45 +01001033 }
Hans de Goedea0b1b732014-12-21 14:37:45 +01001034}
1035
Hans de Goedea2bba492015-08-03 23:01:38 +02001036static bool sunxi_has_hdmi(void)
1037{
1038#ifdef CONFIG_VIDEO_HDMI
1039 return true;
1040#else
1041 return false;
1042#endif
1043}
1044
1045static bool sunxi_has_lcd(void)
1046{
1047 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
1048
1049 return lcd_mode[0] != 0;
1050}
1051
1052static bool sunxi_has_vga(void)
1053{
1054#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_VGA_VIA_LCD
1055 return true;
1056#else
1057 return false;
1058#endif
1059}
1060
Hans de Goedec06e00e2015-08-03 19:20:26 +02001061static bool sunxi_has_composite(void)
1062{
1063#ifdef CONFIG_VIDEO_COMPOSITE
1064 return true;
1065#else
1066 return false;
1067#endif
1068}
1069
Hans de Goedea2bba492015-08-03 23:01:38 +02001070static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
1071{
1072 if (allow_hdmi && sunxi_has_hdmi())
1073 return sunxi_monitor_dvi;
1074 else if (sunxi_has_lcd())
1075 return sunxi_monitor_lcd;
1076 else if (sunxi_has_vga())
1077 return sunxi_monitor_vga;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001078 else if (sunxi_has_composite())
1079 return sunxi_monitor_composite_pal;
Hans de Goedea2bba492015-08-03 23:01:38 +02001080 else
1081 return sunxi_monitor_none;
1082}
1083
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001084static int sunxi_de_probe(struct udevice *dev)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001085{
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001086 struct video_priv *uc_priv = dev_get_uclass_priv(dev);
1087 struct video_uc_plat *plat = dev_get_uclass_plat(dev);
1088 struct sunxi_display_priv *sunxi_display = dev_get_priv(dev);
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001089 const struct ctfb_res_modes *mode;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001090 struct ctfb_res_modes custom;
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001091 const char *options;
Hans de Goedee9544592014-12-23 23:04:35 +01001092#ifdef CONFIG_VIDEO_HDMI
Priit Laesa9d6db72018-12-19 15:06:09 +02001093 int hpd, hpd_delay, edid;
1094 bool hdmi_present;
Hans de Goedee9544592014-12-23 23:04:35 +01001095#endif
Hans de Goeded955f442015-08-05 00:06:47 +02001096 int i, overscan_offset, overscan_x, overscan_y;
1097 unsigned int fb_dma_addr;
Hans de Goedea0b1b732014-12-21 14:37:45 +01001098 char mon[16];
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001099 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001100
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001101 video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001102 &sunxi_display->depth, &options);
Hans de Goedee9544592014-12-23 23:04:35 +01001103#ifdef CONFIG_VIDEO_HDMI
Hans de Goede695bda42014-12-19 15:13:57 +01001104 hpd = video_get_option_int(options, "hpd", 1);
Hans de Goede91593712014-12-28 09:13:21 +01001105 hpd_delay = video_get_option_int(options, "hpd_delay", 500);
Hans de Goedea5aa95f2014-12-19 16:05:12 +01001106 edid = video_get_option_int(options, "edid", 1);
Hans de Goedee9544592014-12-23 23:04:35 +01001107#endif
Hans de Goeded955f442015-08-05 00:06:47 +02001108 overscan_x = video_get_option_int(options, "overscan_x", -1);
1109 overscan_y = video_get_option_int(options, "overscan_y", -1);
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001110 sunxi_display->monitor = sunxi_get_default_mon(true);
Hans de Goedea0b1b732014-12-21 14:37:45 +01001111 video_get_option_string(options, "monitor", mon, sizeof(mon),
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001112 sunxi_get_mon_desc(sunxi_display->monitor));
Hans de Goedea0b1b732014-12-21 14:37:45 +01001113 for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
1114 if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001115 sunxi_display->monitor = i;
Hans de Goedea0b1b732014-12-21 14:37:45 +01001116 break;
1117 }
1118 }
1119 if (i > SUNXI_MONITOR_LAST)
1120 printf("Unknown monitor: '%s', falling back to '%s'\n",
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001121 mon, sunxi_get_mon_desc(sunxi_display->monitor));
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001122
Hans de Goede7977ec22014-12-25 13:52:04 +01001123#ifdef CONFIG_VIDEO_HDMI
1124 /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001125 if (sunxi_display->monitor == sunxi_monitor_dvi ||
1126 sunxi_display->monitor == sunxi_monitor_hdmi) {
Hans de Goede7977ec22014-12-25 13:52:04 +01001127 /* Always call hdp_detect, as it also enables clocks, etc. */
Priit Laesa9d6db72018-12-19 15:06:09 +02001128 hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
1129 if (hdmi_present && edid) {
Hans de Goede7977ec22014-12-25 13:52:04 +01001130 printf("HDMI connected: ");
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001131 if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, true) == 0)
Hans de Goede7977ec22014-12-25 13:52:04 +01001132 mode = &custom;
Priit Laesa9d6db72018-12-19 15:06:09 +02001133 else
1134 hdmi_present = false;
1135 }
1136 /* Fall back to EDID in case HPD failed */
1137 if (edid && !hdmi_present) {
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001138 if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, false) == 0) {
Priit Laesa9d6db72018-12-19 15:06:09 +02001139 mode = &custom;
1140 hdmi_present = true;
1141 }
1142 }
1143 /* Shut down when display was not found */
1144 if ((hpd || edid) && !hdmi_present) {
Hans de Goede7977ec22014-12-25 13:52:04 +01001145 sunxi_hdmi_shutdown();
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001146 sunxi_display->monitor = sunxi_get_default_mon(false);
Hans de Goede7977ec22014-12-25 13:52:04 +01001147 } /* else continue with hdmi/dvi without a cable connected */
1148 }
1149#endif
1150
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001151 switch (sunxi_display->monitor) {
Hans de Goede4125f922014-12-21 14:49:34 +01001152 case sunxi_monitor_none:
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001153 printf("Unknown monitor\n");
1154 return -EINVAL;
Hans de Goede4125f922014-12-21 14:49:34 +01001155 case sunxi_monitor_dvi:
1156 case sunxi_monitor_hdmi:
Hans de Goedea2bba492015-08-03 23:01:38 +02001157 if (!sunxi_has_hdmi()) {
1158 printf("HDMI/DVI not supported on this board\n");
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001159 sunxi_display->monitor = sunxi_monitor_none;
1160 return -EINVAL;
Hans de Goedea2bba492015-08-03 23:01:38 +02001161 }
Hans de Goede7977ec22014-12-25 13:52:04 +01001162 break;
Hans de Goede4125f922014-12-21 14:49:34 +01001163 case sunxi_monitor_lcd:
Hans de Goedea2bba492015-08-03 23:01:38 +02001164 if (!sunxi_has_lcd()) {
1165 printf("LCD not supported on this board\n");
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001166 sunxi_display->monitor = sunxi_monitor_none;
1167 return -EINVAL;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001168 }
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001169 sunxi_display->depth = video_get_params(&custom, lcd_mode);
Hans de Goedea2bba492015-08-03 23:01:38 +02001170 mode = &custom;
1171 break;
Hans de Goede4125f922014-12-21 14:49:34 +01001172 case sunxi_monitor_vga:
Hans de Goedea2bba492015-08-03 23:01:38 +02001173 if (!sunxi_has_vga()) {
1174 printf("VGA not supported on this board\n");
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001175 sunxi_display->monitor = sunxi_monitor_none;
1176 return -EINVAL;
Hans de Goedea2bba492015-08-03 23:01:38 +02001177 }
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001178 sunxi_display->depth = 18;
Hans de Goedeac1633c2014-12-24 12:17:07 +01001179 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001180 case sunxi_monitor_composite_pal:
1181 case sunxi_monitor_composite_ntsc:
1182 case sunxi_monitor_composite_pal_m:
1183 case sunxi_monitor_composite_pal_nc:
1184 if (!sunxi_has_composite()) {
1185 printf("Composite video not supported on this board\n");
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001186 sunxi_display->monitor = sunxi_monitor_none;
1187 return -EINVAL;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001188 }
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001189 if (sunxi_display->monitor == sunxi_monitor_composite_pal ||
1190 sunxi_display->monitor == sunxi_monitor_composite_pal_nc)
Hans de Goedec06e00e2015-08-03 19:20:26 +02001191 mode = &composite_video_modes[0];
1192 else
1193 mode = &composite_video_modes[1];
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001194 sunxi_display->depth = 24;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001195 break;
Hans de Goedea5aa95f2014-12-19 16:05:12 +01001196 }
1197
Hans de Goeded955f442015-08-05 00:06:47 +02001198 /* Yes these defaults are quite high, overscan on composite sucks... */
1199 if (overscan_x == -1)
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001200 overscan_x = sunxi_is_composite(sunxi_display->monitor) ? 32 : 0;
Hans de Goeded955f442015-08-05 00:06:47 +02001201 if (overscan_y == -1)
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001202 overscan_y = sunxi_is_composite(sunxi_display->monitor) ? 20 : 0;
Hans de Goeded955f442015-08-05 00:06:47 +02001203
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001204 sunxi_display->fb_size = plat->size;
Hans de Goeded955f442015-08-05 00:06:47 +02001205 overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
1206 /* We want to keep the fb_base for simplefb page aligned, where as
1207 * the sunxi dma engines will happily accept an unaligned address. */
1208 if (overscan_offset)
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001209 sunxi_display->fb_size += 0x1000;
Hans de Goede18799252015-02-02 18:00:53 +01001210
Hans de Goeded955f442015-08-05 00:06:47 +02001211 printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
1212 mode->xres, mode->yres,
Hans de Goede1977bbb2015-08-02 16:49:29 +02001213 (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001214 sunxi_get_mon_desc(sunxi_display->monitor),
Hans de Goeded955f442015-08-05 00:06:47 +02001215 overscan_x, overscan_y);
Hans de Goede1977bbb2015-08-02 16:49:29 +02001216
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001217 sunxi_display->fb_addr = plat->base;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001218 sunxi_engines_init();
Hans de Goeded955f442015-08-05 00:06:47 +02001219
Heinrich Schuchardt084d3d92018-03-03 10:30:17 +01001220#ifdef CONFIG_EFI_LOADER
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001221 efi_add_memory_map(sunxi_display->fb_addr, sunxi_display->fb_size,
Michael Walle282d3862020-05-17 12:29:19 +02001222 EFI_RESERVED_MEMORY_TYPE);
Heinrich Schuchardt084d3d92018-03-03 10:30:17 +01001223#endif
1224
Tom Rinibb4dd962022-11-16 13:10:37 -05001225 fb_dma_addr = sunxi_display->fb_addr - CFG_SYS_SDRAM_BASE;
Hans de Goeded955f442015-08-05 00:06:47 +02001226 if (overscan_offset) {
1227 fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001228 sunxi_display->fb_addr += ALIGN(overscan_offset, 0x1000);
1229 memset((void *)sunxi_display->fb_addr, 0, sunxi_display->fb_size);
1230 flush_cache(sunxi_display->fb_addr, sunxi_display->fb_size);
Hans de Goeded955f442015-08-05 00:06:47 +02001231 }
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001232 sunxi_mode_set(sunxi_display, mode, fb_dma_addr);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001233
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001234 /* The members of struct video_priv to be set by the driver. */
1235 uc_priv->bpix = VIDEO_BPP32;
1236 uc_priv->xsize = mode->xres;
1237 uc_priv->ysize = mode->yres;
1238
1239 video_set_flush_dcache(dev, true);
1240
1241 return 0;
1242}
1243
1244static int sunxi_de_bind(struct udevice *dev)
1245{
1246 struct video_uc_plat *plat = dev_get_uclass_plat(dev);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001247
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001248 plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * VNBYTES(LCD_MAX_LOG2_BPP);
1249
1250 return 0;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001251}
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001252
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001253static const struct video_ops sunxi_de_ops = {
1254};
1255
1256U_BOOT_DRIVER(sunxi_de) = {
1257 .name = "sunxi_de",
1258 .id = UCLASS_VIDEO,
1259 .ops = &sunxi_de_ops,
1260 .bind = sunxi_de_bind,
1261 .probe = sunxi_de_probe,
1262 .priv_auto = sizeof(struct sunxi_display_priv),
1263 .flags = DM_FLAG_PRE_RELOC,
1264};
1265
1266U_BOOT_DRVINFO(sunxi_de) = {
1267 .name = "sunxi_de"
1268};
1269
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001270/*
1271 * Simplefb support.
1272 */
1273#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
1274int sunxi_simplefb_setup(void *blob)
1275{
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001276 struct sunxi_display_priv *sunxi_display;
1277 struct video_priv *uc_priv;
1278 struct udevice *de;
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001279 int offset, ret;
Hans de Goede6c912862015-02-02 17:13:29 +01001280 u64 start, size;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001281 const char *pipeline = NULL;
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001282
Hans de Goedec3cc4262015-01-19 08:44:07 +01001283#ifdef CONFIG_MACH_SUN4I
1284#define PIPELINE_PREFIX "de_fe0-"
1285#else
1286#define PIPELINE_PREFIX
1287#endif
1288
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001289 ret = uclass_find_device_by_name(UCLASS_VIDEO, "sunxi_de", &de);
1290 if (ret) {
1291 printf("DE not present\n");
1292 return 0;
1293 } else if (!device_active(de)) {
1294 printf("DE is present but not probed\n");
1295 return 0;
1296 }
1297
1298 uc_priv = dev_get_uclass_priv(de);
1299 sunxi_display = dev_get_priv(de);
1300
1301 switch (sunxi_display->monitor) {
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001302 case sunxi_monitor_none:
1303 return 0;
1304 case sunxi_monitor_dvi:
1305 case sunxi_monitor_hdmi:
Hans de Goedec3cc4262015-01-19 08:44:07 +01001306 pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi";
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001307 break;
1308 case sunxi_monitor_lcd:
Hans de Goedec3cc4262015-01-19 08:44:07 +01001309 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001310 break;
1311 case sunxi_monitor_vga:
Hans de Goede260f5202014-12-25 13:58:06 +01001312#ifdef CONFIG_VIDEO_VGA
Hans de Goedec3cc4262015-01-19 08:44:07 +01001313 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
Hans de Goede260f5202014-12-25 13:58:06 +01001314#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goedec3cc4262015-01-19 08:44:07 +01001315 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede260f5202014-12-25 13:58:06 +01001316#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001317 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001318 case sunxi_monitor_composite_pal:
1319 case sunxi_monitor_composite_ntsc:
1320 case sunxi_monitor_composite_pal_m:
1321 case sunxi_monitor_composite_pal_nc:
1322 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
1323 break;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001324 }
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001325
Icenowy Zheng067a6972017-10-26 11:14:45 +08001326 offset = sunxi_simplefb_fdt_match(blob, pipeline);
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001327 if (offset < 0) {
1328 eprintf("Cannot setup simplefb: node not found\n");
1329 return 0; /* Keep older kernels working */
1330 }
1331
Hans de Goede6c912862015-02-02 17:13:29 +01001332 /*
1333 * Do not report the framebuffer as free RAM to the OS, note we cannot
1334 * use fdt_add_mem_rsv() here, because then it is still seen as RAM,
1335 * and e.g. Linux refuses to iomap RAM on ARM, see:
1336 * linux/arch/arm/mm/ioremap.c around line 301.
1337 */
1338 start = gd->bd->bi_dram[0].start;
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001339 size = sunxi_display->fb_addr - start;
Hans de Goede6c912862015-02-02 17:13:29 +01001340 ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
1341 if (ret) {
1342 eprintf("Cannot setup simplefb: Error reserving memory\n");
1343 return ret;
1344 }
1345
Jagan Teki5bc34cb2021-02-22 00:12:34 +00001346 ret = fdt_setup_simplefb_node(blob, offset, sunxi_display->fb_addr,
1347 uc_priv->xsize, uc_priv->ysize,
1348 VNBYTES(uc_priv->bpix) * uc_priv->xsize,
1349 "x8r8g8b8");
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001350 if (ret)
1351 eprintf("Cannot setup simplefb: Error setting properties\n");
1352
1353 return ret;
1354}
1355#endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */