blob: 8da44a1bb6d8a0970f360e6d079174c67a23e617 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Luc Verhaegen7f2c5212014-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 Goede39920c82015-08-03 19:20:26 +02006 * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02007 */
8
Tom Rinid678a592024-05-18 20:20:43 -06009#include <common.h>
Jagan Teki5d235322021-02-22 00:12:34 +000010#include <display.h>
11#include <dm.h>
Simon Glass1eb69ae2019-11-14 12:57:39 -070012#include <cpu_func.h>
Heinrich Schuchardtd06717f2018-03-03 10:30:17 +010013#include <efi_loader.h>
Simon Glass67c4e9f2019-11-14 12:57:45 -070014#include <init.h>
Simon Glass10453152019-11-14 12:57:30 -070015#include <time.h>
Simon Glassc05ed002020-05-10 11:40:11 -060016#include <linux/delay.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020017
18#include <asm/arch/clock.h>
19#include <asm/arch/display.h>
Jernej Skrabec5e023e72017-03-27 19:22:29 +020020#include <asm/arch/lcdc.h>
Hans de Goede421c98d2016-08-19 15:25:41 +020021#include <asm/arch/pwm.h>
Jernej Skrabecc1080622017-05-10 18:46:28 +020022#include <asm/arch/tve.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020023#include <asm/global_data.h>
Hans de Goede2dae8002014-12-21 16:28:32 +010024#include <asm/gpio.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020025#include <asm/io.h>
Hans de Goede6944aff2015-10-03 15:18:33 +020026#include <axp_pmic.h>
Hans de Goede75481602014-12-19 16:05:12 +010027#include <errno.h>
Luc Verhaegen2d7a0842014-08-13 07:55:07 +020028#include <fdtdec.h>
29#include <fdt_support.h>
Hans de Goedeaad2ac22015-02-16 17:49:47 +010030#include <i2c.h>
Hans de Goede58332f82015-08-05 00:06:47 +020031#include <malloc.h>
Jagan Teki5d235322021-02-22 00:12:34 +000032#include <video.h>
Jagan Teki5d235322021-02-22 00:12:34 +000033#include <dm/uclass-internal.h>
Andre Przywara207ed0a2022-09-06 10:36:38 +010034#include <sunxi_gpio.h>
Jernej Skrabec5e023e72017-03-27 19:22:29 +020035#include "../videomodes.h"
36#include "../anx9804.h"
37#include "../hitachi_tx18d42vm_lcd.h"
38#include "../ssd2828.h"
Icenowy Zhenge5f92462017-10-26 11:14:45 +080039#include "simplefb_common.h"
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020040
Hans de Goedea7403ae2015-01-22 21:02:42 +010041#ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW
42#define PWM_ON 0
43#define PWM_OFF 1
44#else
45#define PWM_ON 1
46#define PWM_OFF 0
47#endif
48
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020049DECLARE_GLOBAL_DATA_PTR;
50
Jagan Teki5d235322021-02-22 00:12:34 +000051/* Maximum LCD size we support */
52#define LCD_MAX_WIDTH 3840
53#define LCD_MAX_HEIGHT 2160
54#define LCD_MAX_LOG2_BPP VIDEO_BPP32
55
Hans de Goede1c092202014-12-21 14:37:45 +010056enum sunxi_monitor {
57 sunxi_monitor_none,
58 sunxi_monitor_dvi,
59 sunxi_monitor_hdmi,
60 sunxi_monitor_lcd,
61 sunxi_monitor_vga,
Hans de Goede39920c82015-08-03 19:20:26 +020062 sunxi_monitor_composite_pal,
63 sunxi_monitor_composite_ntsc,
64 sunxi_monitor_composite_pal_m,
65 sunxi_monitor_composite_pal_nc,
Hans de Goede1c092202014-12-21 14:37:45 +010066};
Hans de Goede39920c82015-08-03 19:20:26 +020067#define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
Hans de Goede1c092202014-12-21 14:37:45 +010068
Jagan Teki5d235322021-02-22 00:12:34 +000069struct sunxi_display_priv {
Hans de Goede1c092202014-12-21 14:37:45 +010070 enum sunxi_monitor monitor;
Hans de Goede2dae8002014-12-21 16:28:32 +010071 unsigned int depth;
Hans de Goede58332f82015-08-05 00:06:47 +020072 unsigned int fb_addr;
Hans de Goede20779ec2015-02-02 18:00:53 +010073 unsigned int fb_size;
Jagan Teki5d235322021-02-22 00:12:34 +000074};
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020075
Hans de Goede39920c82015-08-03 19:20:26 +020076const struct ctfb_res_modes composite_video_modes[2] = {
77 /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */
78 { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED },
79 { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED },
80};
81
Hans de Goede2fbf0912014-12-23 23:04:35 +010082#ifdef CONFIG_VIDEO_HDMI
83
Hans de Goede75481602014-12-19 16:05:12 +010084/*
85 * Wait up to 200ms for value to be set in given part of reg.
86 */
87static int await_completion(u32 *reg, u32 mask, u32 val)
88{
89 unsigned long tmo = timer_get_us() + 200000;
90
91 while ((readl(reg) & mask) != val) {
92 if (timer_get_us() > tmo) {
93 printf("DDC: timeout reading EDID\n");
94 return -ETIME;
95 }
96 }
97 return 0;
98}
99
Hans de Goede7fad8a92014-12-28 09:13:21 +0100100static int sunxi_hdmi_hpd_detect(int hpd_delay)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200101{
102 struct sunxi_ccm_reg * const ccm =
103 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
104 struct sunxi_hdmi_reg * const hdmi =
105 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Hans de Goede7fad8a92014-12-28 09:13:21 +0100106 unsigned long tmo = timer_get_us() + hpd_delay * 1000;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200107
108 /* Set pll3 to 300MHz */
109 clock_set_pll3(300000000);
110
111 /* Set hdmi parent to pll3 */
112 clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
113 CCM_HDMI_CTRL_PLL3);
114
115 /* Set ahb gating to pass */
Hans de Goede44d8ae52015-04-06 20:33:34 +0200116#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100117 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
118#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200119 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
120
121 /* Clock on */
122 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
123
124 writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl);
125 writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0);
126
Priit Laes361604d2018-12-19 15:06:08 +0200127 /* Enable PLLs for eventual DDC */
128 writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
129 &hdmi->pad_ctrl1);
130 writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
131 &hdmi->pll_ctrl);
132 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
133
Hans de Goede40f1b872014-12-20 15:15:23 +0100134 while (timer_get_us() < tmo) {
135 if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT)
136 return 1;
137 }
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200138
Hans de Goede40f1b872014-12-20 15:15:23 +0100139 return 0;
Hans de Goede518cef22014-12-19 15:13:57 +0100140}
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200141
Hans de Goede518cef22014-12-19 15:13:57 +0100142static void sunxi_hdmi_shutdown(void)
143{
144 struct sunxi_ccm_reg * const ccm =
145 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
146 struct sunxi_hdmi_reg * const hdmi =
147 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
148
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200149 clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE);
150 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
151 clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
Hans de Goede44d8ae52015-04-06 20:33:34 +0200152#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100153 clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
154#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200155 clock_set_pll3(0);
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200156}
157
Hans de Goede75481602014-12-19 16:05:12 +0100158static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
159{
160 struct sunxi_hdmi_reg * const hdmi =
161 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
162
163 setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
164 writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
165 SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
166 SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
167 SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
168#ifndef CONFIG_MACH_SUN6I
169 writel(n, &hdmi->ddc_byte_count);
170 writel(cmnd, &hdmi->ddc_cmnd);
171#else
172 writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
173#endif
174 setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
175
176 return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
177}
178
179static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
180{
181 struct sunxi_hdmi_reg * const hdmi =
182 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
183 int i, n;
184
185 while (count > 0) {
186 if (count > 16)
187 n = 16;
188 else
189 n = count;
190
191 if (sunxi_hdmi_ddc_do_command(
192 SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
193 offset, n))
194 return -ETIME;
195
196 for (i = 0; i < n; i++)
197 *buf++ = readb(&hdmi->ddc_fifo_data);
198
199 offset += n;
200 count -= n;
201 }
202
203 return 0;
204}
205
Hans de Goede63c5fbd2014-12-20 14:01:48 +0100206static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
207{
208 int r, retries = 2;
209
210 do {
211 r = sunxi_hdmi_ddc_read(block * 128, buf, 128);
212 if (r)
213 continue;
214 r = edid_check_checksum(buf);
215 if (r) {
216 printf("EDID block %d: checksum error%s\n",
217 block, retries ? ", retrying" : "");
218 }
219 } while (r && retries--);
220
221 return r;
222}
223
Jagan Teki5d235322021-02-22 00:12:34 +0000224static int sunxi_hdmi_edid_get_mode(struct sunxi_display_priv *sunxi_display,
225 struct ctfb_res_modes *mode,
Priit Laes0220f8c2018-12-19 15:06:09 +0200226 bool verbose_mode)
Hans de Goede75481602014-12-19 16:05:12 +0100227{
228 struct edid1_info edid1;
Hans de Goedef3000682014-12-20 14:31:45 +0100229 struct edid_cea861_info cea681[4];
Hans de Goede75481602014-12-19 16:05:12 +0100230 struct edid_detailed_timing *t =
231 (struct edid_detailed_timing *)edid1.monitor_details.timing;
232 struct sunxi_hdmi_reg * const hdmi =
233 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
234 struct sunxi_ccm_reg * const ccm =
235 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Hans de Goedef3000682014-12-20 14:31:45 +0100236 int i, r, ext_blocks = 0;
Hans de Goede75481602014-12-19 16:05:12 +0100237
Hans de Goede75481602014-12-19 16:05:12 +0100238 /* Reset i2c controller */
239 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
240 writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
241 SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
242 SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
243 SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
244 if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
245 return -EIO;
246
247 writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
248#ifndef CONFIG_MACH_SUN6I
249 writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
250 SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
251#endif
252
Hans de Goede63c5fbd2014-12-20 14:01:48 +0100253 r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
Hans de Goedef3000682014-12-20 14:31:45 +0100254 if (r == 0) {
255 r = edid_check_info(&edid1);
256 if (r) {
Priit Laes0220f8c2018-12-19 15:06:09 +0200257 if (verbose_mode)
258 printf("EDID: invalid EDID data\n");
Hans de Goedef3000682014-12-20 14:31:45 +0100259 r = -EINVAL;
260 }
261 }
262 if (r == 0) {
263 ext_blocks = edid1.extension_flag;
264 if (ext_blocks > 4)
265 ext_blocks = 4;
266 for (i = 0; i < ext_blocks; i++) {
267 if (sunxi_hdmi_edid_get_block(1 + i,
268 (u8 *)&cea681[i]) != 0) {
269 ext_blocks = i;
270 break;
271 }
272 }
273 }
Hans de Goede75481602014-12-19 16:05:12 +0100274
275 /* Disable DDC engine, no longer needed */
276 clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
277 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
278
279 if (r)
280 return r;
281
Hans de Goede75481602014-12-19 16:05:12 +0100282 /* We want version 1.3 or 1.2 with detailed timing info */
283 if (edid1.version != 1 || (edid1.revision < 3 &&
284 !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
285 printf("EDID: unsupported version %d.%d\n",
286 edid1.version, edid1.revision);
287 return -EINVAL;
288 }
289
290 /* Take the first usable detailed timing */
291 for (i = 0; i < 4; i++, t++) {
292 r = video_edid_dtd_to_ctfb_res_modes(t, mode);
293 if (r == 0)
294 break;
295 }
296 if (i == 4) {
297 printf("EDID: no usable detailed timing found\n");
298 return -ENOENT;
299 }
300
Hans de Goedef3000682014-12-20 14:31:45 +0100301 /* Check for basic audio support, if found enable hdmi output */
Jagan Teki5d235322021-02-22 00:12:34 +0000302 sunxi_display->monitor = sunxi_monitor_dvi;
Hans de Goedef3000682014-12-20 14:31:45 +0100303 for (i = 0; i < ext_blocks; i++) {
304 if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
305 cea681[i].revision < 2)
306 continue;
307
308 if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
Jagan Teki5d235322021-02-22 00:12:34 +0000309 sunxi_display->monitor = sunxi_monitor_hdmi;
Hans de Goedef3000682014-12-20 14:31:45 +0100310 }
311
Hans de Goede75481602014-12-19 16:05:12 +0100312 return 0;
313}
314
Hans de Goede2fbf0912014-12-23 23:04:35 +0100315#endif /* CONFIG_VIDEO_HDMI */
316
Hans de Goede7cd6f922015-01-19 08:44:07 +0100317#ifdef CONFIG_MACH_SUN4I
318/*
319 * Testing has shown that on sun4i the display backend engine does not have
320 * deep enough fifo-s causing flickering / tearing in full-hd mode due to
321 * fifo underruns. So on sun4i we use the display frontend engine to do the
322 * dma from memory, as the frontend does have deep enough fifo-s.
323 */
324
325static const u32 sun4i_vert_coef[32] = {
326 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
327 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
328 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
329 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
330 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
331 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
332 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
333 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
334};
335
336static const u32 sun4i_horz_coef[64] = {
337 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
338 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
339 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
340 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
341 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
342 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
343 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
344 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
345 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
346 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
347 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
348 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
349 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
350 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
351 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
352 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
353};
354
355static void sunxi_frontend_init(void)
356{
357 struct sunxi_ccm_reg * const ccm =
358 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
359 struct sunxi_de_fe_reg * const de_fe =
360 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
361 int i;
362
363 /* Clocks on */
364 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0);
365 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0);
366 clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000);
367
368 setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN);
369
370 for (i = 0; i < 32; i++) {
371 writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]);
372 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]);
373 writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]);
374 writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]);
375 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]);
376 writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]);
377 }
378
379 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY);
380}
381
382static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
383 unsigned int address)
384{
385 struct sunxi_de_fe_reg * const de_fe =
386 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
387
388 setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS);
Tom Riniaa6e94d2022-11-16 13:10:37 -0500389 writel(CFG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100390 writel(mode->xres * 4, &de_fe->ch0_stride);
391 writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt);
392 writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt);
393
394 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
395 &de_fe->ch0_insize);
396 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
397 &de_fe->ch0_outsize);
398 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact);
399 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact);
400
401 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
402 &de_fe->ch1_insize);
403 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
404 &de_fe->ch1_outsize);
405 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact);
406 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact);
407
408 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY);
409}
410
411static void sunxi_frontend_enable(void)
412{
413 struct sunxi_de_fe_reg * const de_fe =
414 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
415
416 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START);
417}
418#else
419static void sunxi_frontend_init(void) {}
420static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
421 unsigned int address) {}
422static void sunxi_frontend_enable(void) {}
423#endif
424
Jagan Teki5d235322021-02-22 00:12:34 +0000425static bool sunxi_is_composite(enum sunxi_monitor monitor)
Hans de Goede39920c82015-08-03 19:20:26 +0200426{
Jagan Teki5d235322021-02-22 00:12:34 +0000427 switch (monitor) {
Hans de Goede39920c82015-08-03 19:20:26 +0200428 case sunxi_monitor_none:
429 case sunxi_monitor_dvi:
430 case sunxi_monitor_hdmi:
431 case sunxi_monitor_lcd:
432 case sunxi_monitor_vga:
433 return false;
434 case sunxi_monitor_composite_pal:
435 case sunxi_monitor_composite_ntsc:
436 case sunxi_monitor_composite_pal_m:
437 case sunxi_monitor_composite_pal_nc:
438 return true;
439 }
440
441 return false; /* Never reached */
442}
443
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200444/*
445 * This is the entity that mixes and matches the different layers and inputs.
446 * Allwinner calls it the back-end, but i like composer better.
447 */
448static void sunxi_composer_init(void)
449{
450 struct sunxi_ccm_reg * const ccm =
451 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
452 struct sunxi_de_be_reg * const de_be =
453 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
454 int i;
455
Hans de Goede7cd6f922015-01-19 08:44:07 +0100456 sunxi_frontend_init();
457
Hans de Goede44d8ae52015-04-06 20:33:34 +0200458#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100459 /* Reset off */
460 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0);
461#endif
462
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200463 /* Clocks on */
464 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100465#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200466 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100467#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200468 clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000);
469
470 /* Engine bug, clear registers after reset */
471 for (i = 0x0800; i < 0x1000; i += 4)
472 writel(0, SUNXI_DE_BE0_BASE + i);
473
474 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE);
475}
476
Priit Laes3d99a0b2018-10-23 20:20:31 +0300477static const u32 sunxi_rgb2yuv_coef[12] = {
Hans de Goede39920c82015-08-03 19:20:26 +0200478 0x00000107, 0x00000204, 0x00000064, 0x00000108,
479 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,
480 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
481};
482
Hans de Goedebe8ec632014-12-19 13:46:33 +0100483static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
Jagan Teki5d235322021-02-22 00:12:34 +0000484 unsigned int address,
485 enum sunxi_monitor monitor)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200486{
487 struct sunxi_de_be_reg * const de_be =
488 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
Hans de Goede39920c82015-08-03 19:20:26 +0200489 int i;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200490
Hans de Goede7cd6f922015-01-19 08:44:07 +0100491 sunxi_frontend_mode_set(mode, address);
492
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200493 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
494 &de_be->disp_size);
495 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
496 &de_be->layer0_size);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100497#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200498 writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride);
499 writel(address << 3, &de_be->layer0_addr_low32b);
500 writel(address >> 29, &de_be->layer0_addr_high4b);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100501#else
502 writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl);
503#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200504 writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl);
505
506 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE);
Hans de Goedef6d9d322015-08-02 16:49:29 +0200507 if (mode->vmode == FB_VMODE_INTERLACED)
508 setbits_le32(&de_be->mode,
Hans de Goeded8d07992015-08-06 12:08:33 +0200509#ifndef CONFIG_MACH_SUN5I
Hans de Goedef6d9d322015-08-02 16:49:29 +0200510 SUNXI_DE_BE_MODE_DEFLICKER_ENABLE |
Hans de Goeded8d07992015-08-06 12:08:33 +0200511#endif
Hans de Goedef6d9d322015-08-02 16:49:29 +0200512 SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
Hans de Goede39920c82015-08-03 19:20:26 +0200513
Jagan Teki5d235322021-02-22 00:12:34 +0000514 if (sunxi_is_composite(monitor)) {
Hans de Goede39920c82015-08-03 19:20:26 +0200515 writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
516 &de_be->output_color_ctrl);
517 for (i = 0; i < 12; i++)
518 writel(sunxi_rgb2yuv_coef[i],
519 &de_be->output_color_coef[i]);
520 }
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200521}
522
Hans de Goede0e045212014-12-21 14:49:34 +0100523static void sunxi_composer_enable(void)
524{
525 struct sunxi_de_be_reg * const de_be =
526 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
527
Hans de Goede7cd6f922015-01-19 08:44:07 +0100528 sunxi_frontend_enable();
529
Hans de Goede0e045212014-12-21 14:49:34 +0100530 setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS);
531 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
532}
533
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200534static void sunxi_lcdc_init(void)
535{
536 struct sunxi_ccm_reg * const ccm =
537 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
538 struct sunxi_lcdc_reg * const lcdc =
539 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
540
541 /* Reset off */
Hans de Goede44d8ae52015-04-06 20:33:34 +0200542#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100543 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
544#else
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200545 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST);
Hans de Goede211717a2014-11-14 17:42:14 +0100546#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200547
548 /* Clock on */
549 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
Hans de Goede213480e2015-01-01 22:04:34 +0100550#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Hans de Goede83edb2a2015-05-14 18:52:54 +0200551#ifdef CONFIG_SUNXI_GEN_SUN6I
552 setbits_le32(&ccm->ahb_reset2_cfg, 1 << AHB_RESET_OFFSET_LVDS);
553#else
Hans de Goede213480e2015-01-01 22:04:34 +0100554 setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
555#endif
Hans de Goede83edb2a2015-05-14 18:52:54 +0200556#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200557
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200558 lcdc_init(lcdc);
Hans de Goede0e045212014-12-21 14:49:34 +0100559}
560
Hans de Goede2dae8002014-12-21 16:28:32 +0100561static void sunxi_lcdc_panel_enable(void)
562{
Hans de Goede242e3d82015-02-16 17:26:41 +0100563 int pin, reset_pin;
Hans de Goede2dae8002014-12-21 16:28:32 +0100564
565 /*
566 * Start with backlight disabled to avoid the screen flashing to
567 * white while the lcd inits.
568 */
569 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede15728192015-04-22 17:45:59 +0200570 if (pin >= 0) {
Hans de Goede2dae8002014-12-21 16:28:32 +0100571 gpio_request(pin, "lcd_backlight_enable");
572 gpio_direction_output(pin, 0);
573 }
574
575 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede15728192015-04-22 17:45:59 +0200576 if (pin >= 0) {
Hans de Goede2dae8002014-12-21 16:28:32 +0100577 gpio_request(pin, "lcd_backlight_pwm");
Hans de Goedea7403ae2015-01-22 21:02:42 +0100578 gpio_direction_output(pin, PWM_OFF);
Hans de Goede2dae8002014-12-21 16:28:32 +0100579 }
580
Hans de Goede242e3d82015-02-16 17:26:41 +0100581 reset_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_RESET);
Hans de Goede15728192015-04-22 17:45:59 +0200582 if (reset_pin >= 0) {
Hans de Goede242e3d82015-02-16 17:26:41 +0100583 gpio_request(reset_pin, "lcd_reset");
584 gpio_direction_output(reset_pin, 0); /* Assert reset */
585 }
586
Hans de Goede2dae8002014-12-21 16:28:32 +0100587 /* Give the backlight some time to turn off and power up the panel. */
588 mdelay(40);
589 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
Hans de Goede15728192015-04-22 17:45:59 +0200590 if (pin >= 0) {
Hans de Goede2dae8002014-12-21 16:28:32 +0100591 gpio_request(pin, "lcd_power");
592 gpio_direction_output(pin, 1);
593 }
Hans de Goede242e3d82015-02-16 17:26:41 +0100594
Hans de Goede15728192015-04-22 17:45:59 +0200595 if (reset_pin >= 0)
Hans de Goede242e3d82015-02-16 17:26:41 +0100596 gpio_direction_output(reset_pin, 1); /* De-assert reset */
Hans de Goede2dae8002014-12-21 16:28:32 +0100597}
598
599static void sunxi_lcdc_backlight_enable(void)
600{
601 int pin;
602
603 /*
604 * We want to have scanned out at least one frame before enabling the
605 * backlight to avoid the screen flashing to white when we enable it.
606 */
607 mdelay(40);
608
609 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede15728192015-04-22 17:45:59 +0200610 if (pin >= 0)
Hans de Goede2dae8002014-12-21 16:28:32 +0100611 gpio_direction_output(pin, 1);
612
613 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede421c98d2016-08-19 15:25:41 +0200614#ifdef SUNXI_PWM_PIN0
615 if (pin == SUNXI_PWM_PIN0) {
616 writel(SUNXI_PWM_CTRL_POLARITY0(PWM_ON) |
617 SUNXI_PWM_CTRL_ENABLE0 |
618 SUNXI_PWM_CTRL_PRESCALE0(0xf), SUNXI_PWM_CTRL_REG);
619 writel(SUNXI_PWM_PERIOD_80PCT, SUNXI_PWM_CH0_PERIOD);
620 sunxi_gpio_set_cfgpin(pin, SUNXI_PWM_MUX);
621 return;
622 }
623#endif
Hans de Goede15728192015-04-22 17:45:59 +0200624 if (pin >= 0)
Hans de Goedea7403ae2015-01-22 21:02:42 +0100625 gpio_direction_output(pin, PWM_ON);
Hans de Goede2dae8002014-12-21 16:28:32 +0100626}
627
Jagan Teki5d235322021-02-22 00:12:34 +0000628static void sunxi_lcdc_tcon0_mode_set(struct sunxi_display_priv *sunxi_display,
629 const struct ctfb_res_modes *mode,
Hans de Goedefb75d972015-01-25 15:33:07 +0100630 bool for_ext_vga_dac)
Hans de Goede2dae8002014-12-21 16:28:32 +0100631{
632 struct sunxi_lcdc_reg * const lcdc =
633 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700634 struct sunxi_ccm_reg * const ccm =
635 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200636 int clk_div, clk_double, pin;
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200637 struct display_timing timing;
Hans de Goede2dae8002014-12-21 16:28:32 +0100638
Lawrence Yucf6eca72016-03-04 09:08:56 -0800639#if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS
640 for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) {
641#else
Hans de Goedec1cfd512015-08-08 16:13:53 +0200642 for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) {
Lawrence Yucf6eca72016-03-04 09:08:56 -0800643#endif
Hans de Goede213480e2015-01-01 22:04:34 +0100644#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Paul Kocialkowski487b3272015-03-22 18:12:22 +0100645 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0);
Hans de Goede213480e2015-01-01 22:04:34 +0100646#endif
647#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Paul Kocialkowski487b3272015-03-22 18:12:22 +0100648 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LVDS0);
Hans de Goede213480e2015-01-01 22:04:34 +0100649#endif
Hans de Goedec1cfd512015-08-08 16:13:53 +0200650#ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804
651 sunxi_gpio_set_drv(pin, 3);
652#endif
653 }
Hans de Goede2dae8002014-12-21 16:28:32 +0100654
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700655 lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double,
Jagan Teki5d235322021-02-22 00:12:34 +0000656 sunxi_is_composite(sunxi_display->monitor));
Hans de Goede2dae8002014-12-21 16:28:32 +0100657
Giulio Benetti92a68362020-04-08 17:10:12 +0200658 video_ctfb_mode_to_display_timing(mode, &timing);
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200659 lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
Jagan Teki5d235322021-02-22 00:12:34 +0000660 sunxi_display->depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
Hans de Goede2dae8002014-12-21 16:28:32 +0100661}
662
Hans de Goede39920c82015-08-03 19:20:26 +0200663#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goede0e045212014-12-21 14:49:34 +0100664static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
Hans de Goede3ffbe472014-12-27 15:19:23 +0100665 int *clk_div, int *clk_double,
Jagan Teki5d235322021-02-22 00:12:34 +0000666 bool use_portd_hvsync,
667 enum sunxi_monitor monitor)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200668{
669 struct sunxi_lcdc_reg * const lcdc =
670 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700671 struct sunxi_ccm_reg * const ccm =
672 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200673 struct display_timing timing;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200674
Giulio Benetti92a68362020-04-08 17:10:12 +0200675 video_ctfb_mode_to_display_timing(mode, &timing);
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200676 lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
Jagan Teki5d235322021-02-22 00:12:34 +0000677 sunxi_is_composite(monitor));
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200678
Hans de Goede3ffbe472014-12-27 15:19:23 +0100679 if (use_portd_hvsync) {
Paul Kocialkowski487b3272015-03-22 18:12:22 +0100680 sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
681 sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0);
Hans de Goede3ffbe472014-12-27 15:19:23 +0100682 }
Hans de Goeded8d07992015-08-06 12:08:33 +0200683
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700684 lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double,
Jagan Teki5d235322021-02-22 00:12:34 +0000685 sunxi_is_composite(monitor));
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200686}
Hans de Goede39920c82015-08-03 19:20:26 +0200687#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
Hans de Goeded9786d22014-12-25 13:58:06 +0100688
689#ifdef CONFIG_VIDEO_HDMI
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200690
Hans de Goede5ee0bea2014-12-20 13:38:06 +0100691static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
692{
693 struct sunxi_hdmi_reg * const hdmi =
694 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
695 u8 checksum = 0;
696 u8 avi_info_frame[17] = {
697 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00,
698 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
699 0x00
700 };
701 u8 vendor_info_frame[19] = {
702 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40,
703 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
704 0x00, 0x00, 0x00
705 };
706 int i;
707
708 if (mode->pixclock_khz <= 27000)
709 avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */
710 else
711 avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */
712
713 if (mode->xres * 100 / mode->yres < 156)
714 avi_info_frame[5] |= 0x18; /* 4 : 3 */
715 else
716 avi_info_frame[5] |= 0x28; /* 16 : 9 */
717
718 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
719 checksum += avi_info_frame[i];
720
721 avi_info_frame[3] = 0x100 - checksum;
722
723 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
724 writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]);
725
726 writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0);
727 writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1);
728
729 for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++)
730 writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]);
731
732 writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0);
733 writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1);
734
735 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI);
736}
737
Hans de Goedebe8ec632014-12-19 13:46:33 +0100738static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
Jagan Teki5d235322021-02-22 00:12:34 +0000739 int clk_div, int clk_double,
740 enum sunxi_monitor monitor)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200741{
742 struct sunxi_hdmi_reg * const hdmi =
743 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
744 int x, y;
745
746 /* Write clear interrupt status bits */
747 writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq);
748
Jagan Teki5d235322021-02-22 00:12:34 +0000749 if (monitor == sunxi_monitor_hdmi)
Hans de Goede5ee0bea2014-12-20 13:38:06 +0100750 sunxi_hdmi_setup_info_frames(mode);
751
Hans de Goede876aaaf2014-12-20 13:51:16 +0100752 /* Set input sync enable */
753 writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown);
754
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200755 /* Init various registers, select pll3 as clock source */
756 writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity);
757 writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0);
758 writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1);
759 writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl);
760 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
761
762 /* Setup clk div and doubler */
763 clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK,
764 SUNXI_HDMI_PLL_CTRL_DIV(clk_div));
765 if (!clk_double)
766 setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE);
767
768 /* Setup timing registers */
769 writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres),
770 &hdmi->video_size);
771
772 x = mode->hsync_len + mode->left_margin;
773 y = mode->vsync_len + mode->upper_margin;
774 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp);
775
776 x = mode->right_margin;
777 y = mode->lower_margin;
778 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp);
779
780 x = mode->hsync_len;
781 y = mode->vsync_len;
782 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw);
783
784 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
785 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR);
786
787 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
788 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER);
789}
790
Hans de Goede0e045212014-12-21 14:49:34 +0100791static void sunxi_hdmi_enable(void)
792{
793 struct sunxi_hdmi_reg * const hdmi =
794 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
795
796 udelay(100);
797 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE);
798}
799
Hans de Goede2fbf0912014-12-23 23:04:35 +0100800#endif /* CONFIG_VIDEO_HDMI */
801
Hans de Goede39920c82015-08-03 19:20:26 +0200802#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goeded9786d22014-12-25 13:58:06 +0100803
Jagan Teki5d235322021-02-22 00:12:34 +0000804static void sunxi_tvencoder_mode_set(enum sunxi_monitor monitor)
Hans de Goeded9786d22014-12-25 13:58:06 +0100805{
806 struct sunxi_ccm_reg * const ccm =
807 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
808 struct sunxi_tve_reg * const tve =
809 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
810
Hans de Goeded8d07992015-08-06 12:08:33 +0200811 /* Reset off */
812 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_TVE_RST);
Hans de Goeded9786d22014-12-25 13:58:06 +0100813 /* Clock on */
814 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
815
Jagan Teki5d235322021-02-22 00:12:34 +0000816 switch (monitor) {
Hans de Goede39920c82015-08-03 19:20:26 +0200817 case sunxi_monitor_vga:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200818 tvencoder_mode_set(tve, tve_mode_vga);
Hans de Goede39920c82015-08-03 19:20:26 +0200819 break;
820 case sunxi_monitor_composite_pal_nc:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200821 tvencoder_mode_set(tve, tve_mode_composite_pal_nc);
822 break;
Hans de Goede39920c82015-08-03 19:20:26 +0200823 case sunxi_monitor_composite_pal:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200824 tvencoder_mode_set(tve, tve_mode_composite_pal);
Hans de Goede39920c82015-08-03 19:20:26 +0200825 break;
826 case sunxi_monitor_composite_pal_m:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200827 tvencoder_mode_set(tve, tve_mode_composite_pal_m);
828 break;
Hans de Goede39920c82015-08-03 19:20:26 +0200829 case sunxi_monitor_composite_ntsc:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200830 tvencoder_mode_set(tve, tve_mode_composite_ntsc);
Hans de Goede39920c82015-08-03 19:20:26 +0200831 break;
832 case sunxi_monitor_none:
833 case sunxi_monitor_dvi:
834 case sunxi_monitor_hdmi:
835 case sunxi_monitor_lcd:
836 break;
837 }
Hans de Goeded9786d22014-12-25 13:58:06 +0100838}
839
Hans de Goede39920c82015-08-03 19:20:26 +0200840#endif /* CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE */
Hans de Goeded9786d22014-12-25 13:58:06 +0100841
Hans de Goedee8400792014-12-23 18:39:52 +0100842static void sunxi_drc_init(void)
843{
Hans de Goede44d8ae52015-04-06 20:33:34 +0200844#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedee8400792014-12-23 18:39:52 +0100845 struct sunxi_ccm_reg * const ccm =
846 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
847
848 /* On sun6i the drc must be clocked even when in pass-through mode */
Vishnu Patekar8c3dacf2015-03-01 23:47:48 +0530849#ifdef CONFIG_MACH_SUN8I_A33
850 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_SAT);
851#endif
Hans de Goedee8400792014-12-23 18:39:52 +0100852 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0);
853 clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000);
854#endif
855}
856
Chen-Yu Tsai507e27d2015-01-12 18:02:11 +0800857#ifdef CONFIG_VIDEO_VGA_VIA_LCD
858static void sunxi_vga_external_dac_enable(void)
859{
860 int pin;
861
862 pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
Hans de Goede15728192015-04-22 17:45:59 +0200863 if (pin >= 0) {
Chen-Yu Tsai507e27d2015-01-12 18:02:11 +0800864 gpio_request(pin, "vga_enable");
865 gpio_direction_output(pin, 1);
866 }
867}
868#endif /* CONFIG_VIDEO_VGA_VIA_LCD */
869
Siarhei Siamashka97ece832015-01-19 05:23:33 +0200870#ifdef CONFIG_VIDEO_LCD_SSD2828
871static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
872{
873 struct ssd2828_config cfg = {
Samuel Holland4d9958b2021-09-11 16:50:48 -0500874 .csx_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
875 .sck_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
876 .sdi_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
877 .sdo_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
878 .reset_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
Siarhei Siamashka97ece832015-01-19 05:23:33 +0200879 .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
880 .ssd2828_color_depth = 24,
881#ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
882 .mipi_dsi_number_of_data_lanes = 4,
883 .mipi_dsi_bitrate_per_data_lane_mbps = 513,
884 .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
885 .mipi_dsi_delay_after_set_display_on_ms = 200
886#else
887#error MIPI LCD panel needs configuration parameters
888#endif
889 };
890
891 if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
892 printf("SSD2828: SPI pins are not properly configured\n");
893 return 1;
894 }
895 if (cfg.reset_pin == -1) {
896 printf("SSD2828: Reset pin is not properly configured\n");
897 return 1;
898 }
899
900 return ssd2828_init(&cfg, mode);
901}
902#endif /* CONFIG_VIDEO_LCD_SSD2828 */
903
Samuel Holland24214972021-10-08 00:17:24 -0500904#ifdef CONFIG_VIDEO_LCD_PANEL_I2C
905static void sunxi_panel_i2c_init(struct sunxi_display_priv *sunxi_display)
906{
907 const char *name = CONFIG_VIDEO_LCD_PANEL_I2C_NAME;
908 struct udevice *i2c_bus;
909 int ret;
910
911 ret = uclass_get_device_by_name(UCLASS_I2C, name, &i2c_bus);
912 if (ret)
913 return;
914
915 if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804)) {
916 /*
917 * The anx9804 needs 1.8V from eldo3, we do this here
918 * and not via CONFIG_AXP_ELDO3_VOLT from board_init()
919 * to avoid turning this on when using hdmi output.
920 */
921 axp_set_eldo(3, 1800);
922 anx9804_init(i2c_bus, 4,
923 ANX9804_DATA_RATE_1620M,
924 sunxi_display->depth);
925 }
926 if (IS_ENABLED(CONFIG_VIDEO_LCD_TL059WV5C0)) {
927 struct udevice *chip;
928
929 ret = i2c_get_chip(i2c_bus, 0x5c, 1, &chip);
930 if (ret)
931 return;
932
933 dm_i2c_reg_write(chip, 0x04, 0x42); /* Turn on the LCD */
934 }
935}
936#else
937static void sunxi_panel_i2c_init(struct sunxi_display_priv *sunxi_display) {}
938#endif
939
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200940static void sunxi_engines_init(void)
941{
942 sunxi_composer_init();
943 sunxi_lcdc_init();
Hans de Goede211717a2014-11-14 17:42:14 +0100944 sunxi_drc_init();
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200945}
946
Jagan Teki5d235322021-02-22 00:12:34 +0000947static void sunxi_mode_set(struct sunxi_display_priv *sunxi_display,
948 const struct ctfb_res_modes *mode,
Hans de Goede5ee0bea2014-12-20 13:38:06 +0100949 unsigned int address)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200950{
Jagan Teki5d235322021-02-22 00:12:34 +0000951 enum sunxi_monitor monitor = sunxi_display->monitor;
Hans de Goeded9786d22014-12-25 13:58:06 +0100952 int __maybe_unused clk_div, clk_double;
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200953 struct sunxi_lcdc_reg * const lcdc =
954 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Jernej Skrabecc1080622017-05-10 18:46:28 +0200955 struct sunxi_tve_reg * __maybe_unused const tve =
956 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
Hans de Goeded9786d22014-12-25 13:58:06 +0100957
Jagan Teki5d235322021-02-22 00:12:34 +0000958 switch (sunxi_display->monitor) {
Hans de Goede0e045212014-12-21 14:49:34 +0100959 case sunxi_monitor_none:
960 break;
961 case sunxi_monitor_dvi:
Hans de Goeded9786d22014-12-25 13:58:06 +0100962 case sunxi_monitor_hdmi:
Hans de Goede2fbf0912014-12-23 23:04:35 +0100963#ifdef CONFIG_VIDEO_HDMI
Jagan Teki5d235322021-02-22 00:12:34 +0000964 sunxi_composer_mode_set(mode, address, monitor);
965 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
966 sunxi_hdmi_mode_set(mode, clk_div, clk_double, monitor);
Hans de Goede0e045212014-12-21 14:49:34 +0100967 sunxi_composer_enable();
Jagan Teki5d235322021-02-22 00:12:34 +0000968 lcdc_enable(lcdc, sunxi_display->depth);
Hans de Goede0e045212014-12-21 14:49:34 +0100969 sunxi_hdmi_enable();
Hans de Goede2fbf0912014-12-23 23:04:35 +0100970#endif
Hans de Goede0e045212014-12-21 14:49:34 +0100971 break;
972 case sunxi_monitor_lcd:
Hans de Goede2dae8002014-12-21 16:28:32 +0100973 sunxi_lcdc_panel_enable();
Hans de Goede27515b22015-01-20 09:23:36 +0100974 if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
975 mdelay(50); /* Wait for lcd controller power on */
976 hitachi_tx18d42vm_init();
977 }
Samuel Holland24214972021-10-08 00:17:24 -0500978 if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_I2C))
979 sunxi_panel_i2c_init(sunxi_display);
Jagan Teki5d235322021-02-22 00:12:34 +0000980 sunxi_composer_mode_set(mode, address, monitor);
981 sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, false);
Hans de Goede2dae8002014-12-21 16:28:32 +0100982 sunxi_composer_enable();
Jagan Teki5d235322021-02-22 00:12:34 +0000983 lcdc_enable(lcdc, sunxi_display->depth);
Siarhei Siamashka97ece832015-01-19 05:23:33 +0200984#ifdef CONFIG_VIDEO_LCD_SSD2828
985 sunxi_ssd2828_init(mode);
986#endif
Hans de Goede2dae8002014-12-21 16:28:32 +0100987 sunxi_lcdc_backlight_enable();
Hans de Goede0e045212014-12-21 14:49:34 +0100988 break;
989 case sunxi_monitor_vga:
Hans de Goeded9786d22014-12-25 13:58:06 +0100990#ifdef CONFIG_VIDEO_VGA
Jagan Teki5d235322021-02-22 00:12:34 +0000991 sunxi_composer_mode_set(mode, address, monitor);
992 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1, monitor);
993 sunxi_tvencoder_mode_set(monitor);
Hans de Goeded9786d22014-12-25 13:58:06 +0100994 sunxi_composer_enable();
Jagan Teki5d235322021-02-22 00:12:34 +0000995 lcdc_enable(lcdc, sunxi_display->depth);
Jernej Skrabecc1080622017-05-10 18:46:28 +0200996 tvencoder_enable(tve);
Hans de Goeded9786d22014-12-25 13:58:06 +0100997#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Jagan Teki5d235322021-02-22 00:12:34 +0000998 sunxi_composer_mode_set(mode, address, monitor);
999 sunxi_lcdc_tcon0_mode_set(sunxi_display, mode, true);
Hans de Goedee2bbdfb2014-12-24 12:17:07 +01001000 sunxi_composer_enable();
Jagan Teki5d235322021-02-22 00:12:34 +00001001 lcdc_enable(lcdc, sunxi_display->depth);
Chen-Yu Tsai507e27d2015-01-12 18:02:11 +08001002 sunxi_vga_external_dac_enable();
Hans de Goedee2bbdfb2014-12-24 12:17:07 +01001003#endif
Hans de Goede0e045212014-12-21 14:49:34 +01001004 break;
Hans de Goede39920c82015-08-03 19:20:26 +02001005 case sunxi_monitor_composite_pal:
1006 case sunxi_monitor_composite_ntsc:
1007 case sunxi_monitor_composite_pal_m:
1008 case sunxi_monitor_composite_pal_nc:
1009#ifdef CONFIG_VIDEO_COMPOSITE
Jagan Teki5d235322021-02-22 00:12:34 +00001010 sunxi_composer_mode_set(mode, address, monitor);
1011 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0, monitor);
1012 sunxi_tvencoder_mode_set(monitor);
Hans de Goede39920c82015-08-03 19:20:26 +02001013 sunxi_composer_enable();
Jagan Teki5d235322021-02-22 00:12:34 +00001014 lcdc_enable(lcdc, sunxi_display->depth);
Jernej Skrabecc1080622017-05-10 18:46:28 +02001015 tvencoder_enable(tve);
Hans de Goede39920c82015-08-03 19:20:26 +02001016#endif
1017 break;
Hans de Goede0e045212014-12-21 14:49:34 +01001018 }
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001019}
1020
Hans de Goede1c092202014-12-21 14:37:45 +01001021static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
1022{
1023 switch (monitor) {
Hans de Goede39920c82015-08-03 19:20:26 +02001024 case sunxi_monitor_dvi: return "dvi";
1025 case sunxi_monitor_hdmi: return "hdmi";
1026 case sunxi_monitor_lcd: return "lcd";
1027 case sunxi_monitor_vga: return "vga";
1028 case sunxi_monitor_composite_pal: return "composite-pal";
1029 case sunxi_monitor_composite_ntsc: return "composite-ntsc";
1030 case sunxi_monitor_composite_pal_m: return "composite-pal-m";
1031 case sunxi_monitor_composite_pal_nc: return "composite-pal-nc";
Bin Menga0c91fe2020-04-06 06:06:58 -07001032 case sunxi_monitor_none: /* fall through */
1033 default: return "none";
Hans de Goede1c092202014-12-21 14:37:45 +01001034 }
Hans de Goede1c092202014-12-21 14:37:45 +01001035}
1036
Hans de Goedebf689342015-08-03 23:01:38 +02001037static bool sunxi_has_hdmi(void)
1038{
1039#ifdef CONFIG_VIDEO_HDMI
1040 return true;
1041#else
1042 return false;
1043#endif
1044}
1045
1046static bool sunxi_has_lcd(void)
1047{
1048 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
1049
1050 return lcd_mode[0] != 0;
1051}
1052
1053static bool sunxi_has_vga(void)
1054{
1055#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_VGA_VIA_LCD
1056 return true;
1057#else
1058 return false;
1059#endif
1060}
1061
Hans de Goede39920c82015-08-03 19:20:26 +02001062static bool sunxi_has_composite(void)
1063{
1064#ifdef CONFIG_VIDEO_COMPOSITE
1065 return true;
1066#else
1067 return false;
1068#endif
1069}
1070
Hans de Goedebf689342015-08-03 23:01:38 +02001071static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
1072{
1073 if (allow_hdmi && sunxi_has_hdmi())
1074 return sunxi_monitor_dvi;
1075 else if (sunxi_has_lcd())
1076 return sunxi_monitor_lcd;
1077 else if (sunxi_has_vga())
1078 return sunxi_monitor_vga;
Hans de Goede39920c82015-08-03 19:20:26 +02001079 else if (sunxi_has_composite())
1080 return sunxi_monitor_composite_pal;
Hans de Goedebf689342015-08-03 23:01:38 +02001081 else
1082 return sunxi_monitor_none;
1083}
1084
Jagan Teki5d235322021-02-22 00:12:34 +00001085static int sunxi_de_probe(struct udevice *dev)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001086{
Jagan Teki5d235322021-02-22 00:12:34 +00001087 struct video_priv *uc_priv = dev_get_uclass_priv(dev);
1088 struct video_uc_plat *plat = dev_get_uclass_plat(dev);
1089 struct sunxi_display_priv *sunxi_display = dev_get_priv(dev);
Hans de Goede5f339932014-12-19 14:03:40 +01001090 const struct ctfb_res_modes *mode;
Hans de Goede2dae8002014-12-21 16:28:32 +01001091 struct ctfb_res_modes custom;
Hans de Goede5f339932014-12-19 14:03:40 +01001092 const char *options;
Hans de Goede2fbf0912014-12-23 23:04:35 +01001093#ifdef CONFIG_VIDEO_HDMI
Priit Laes0220f8c2018-12-19 15:06:09 +02001094 int hpd, hpd_delay, edid;
1095 bool hdmi_present;
Hans de Goede2fbf0912014-12-23 23:04:35 +01001096#endif
Hans de Goede58332f82015-08-05 00:06:47 +02001097 int i, overscan_offset, overscan_x, overscan_y;
1098 unsigned int fb_dma_addr;
Hans de Goede1c092202014-12-21 14:37:45 +01001099 char mon[16];
Hans de Goede2dae8002014-12-21 16:28:32 +01001100 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001101
Hans de Goede2dae8002014-12-21 16:28:32 +01001102 video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
Jagan Teki5d235322021-02-22 00:12:34 +00001103 &sunxi_display->depth, &options);
Hans de Goede2fbf0912014-12-23 23:04:35 +01001104#ifdef CONFIG_VIDEO_HDMI
Hans de Goede518cef22014-12-19 15:13:57 +01001105 hpd = video_get_option_int(options, "hpd", 1);
Hans de Goede7fad8a92014-12-28 09:13:21 +01001106 hpd_delay = video_get_option_int(options, "hpd_delay", 500);
Hans de Goede75481602014-12-19 16:05:12 +01001107 edid = video_get_option_int(options, "edid", 1);
Hans de Goede2fbf0912014-12-23 23:04:35 +01001108#endif
Hans de Goede58332f82015-08-05 00:06:47 +02001109 overscan_x = video_get_option_int(options, "overscan_x", -1);
1110 overscan_y = video_get_option_int(options, "overscan_y", -1);
Jagan Teki5d235322021-02-22 00:12:34 +00001111 sunxi_display->monitor = sunxi_get_default_mon(true);
Hans de Goede1c092202014-12-21 14:37:45 +01001112 video_get_option_string(options, "monitor", mon, sizeof(mon),
Jagan Teki5d235322021-02-22 00:12:34 +00001113 sunxi_get_mon_desc(sunxi_display->monitor));
Hans de Goede1c092202014-12-21 14:37:45 +01001114 for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
1115 if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
Jagan Teki5d235322021-02-22 00:12:34 +00001116 sunxi_display->monitor = i;
Hans de Goede1c092202014-12-21 14:37:45 +01001117 break;
1118 }
1119 }
1120 if (i > SUNXI_MONITOR_LAST)
1121 printf("Unknown monitor: '%s', falling back to '%s'\n",
Jagan Teki5d235322021-02-22 00:12:34 +00001122 mon, sunxi_get_mon_desc(sunxi_display->monitor));
Hans de Goede5f339932014-12-19 14:03:40 +01001123
Hans de Goede49d27032014-12-25 13:52:04 +01001124#ifdef CONFIG_VIDEO_HDMI
1125 /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
Jagan Teki5d235322021-02-22 00:12:34 +00001126 if (sunxi_display->monitor == sunxi_monitor_dvi ||
1127 sunxi_display->monitor == sunxi_monitor_hdmi) {
Hans de Goede0e045212014-12-21 14:49:34 +01001128 /* Always call hdp_detect, as it also enables clocks, etc. */
Priit Laes0220f8c2018-12-19 15:06:09 +02001129 hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
1130 if (hdmi_present && edid) {
Hans de Goede0e045212014-12-21 14:49:34 +01001131 printf("HDMI connected: ");
Jagan Teki5d235322021-02-22 00:12:34 +00001132 if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, true) == 0)
Hans de Goede2dae8002014-12-21 16:28:32 +01001133 mode = &custom;
Priit Laes0220f8c2018-12-19 15:06:09 +02001134 else
1135 hdmi_present = false;
1136 }
1137 /* Fall back to EDID in case HPD failed */
1138 if (edid && !hdmi_present) {
Jagan Teki5d235322021-02-22 00:12:34 +00001139 if (sunxi_hdmi_edid_get_mode(sunxi_display, &custom, false) == 0) {
Priit Laes0220f8c2018-12-19 15:06:09 +02001140 mode = &custom;
1141 hdmi_present = true;
1142 }
1143 }
1144 /* Shut down when display was not found */
1145 if ((hpd || edid) && !hdmi_present) {
Hans de Goede49d27032014-12-25 13:52:04 +01001146 sunxi_hdmi_shutdown();
Jagan Teki5d235322021-02-22 00:12:34 +00001147 sunxi_display->monitor = sunxi_get_default_mon(false);
Hans de Goede49d27032014-12-25 13:52:04 +01001148 } /* else continue with hdmi/dvi without a cable connected */
1149 }
1150#endif
Hans de Goede0e045212014-12-21 14:49:34 +01001151
Jagan Teki5d235322021-02-22 00:12:34 +00001152 switch (sunxi_display->monitor) {
Hans de Goede49d27032014-12-25 13:52:04 +01001153 case sunxi_monitor_none:
Jagan Teki5d235322021-02-22 00:12:34 +00001154 printf("Unknown monitor\n");
1155 return -EINVAL;
Hans de Goede49d27032014-12-25 13:52:04 +01001156 case sunxi_monitor_dvi:
1157 case sunxi_monitor_hdmi:
Hans de Goedebf689342015-08-03 23:01:38 +02001158 if (!sunxi_has_hdmi()) {
1159 printf("HDMI/DVI not supported on this board\n");
Jagan Teki5d235322021-02-22 00:12:34 +00001160 sunxi_display->monitor = sunxi_monitor_none;
1161 return -EINVAL;
Hans de Goede2dae8002014-12-21 16:28:32 +01001162 }
Hans de Goedebf689342015-08-03 23:01:38 +02001163 break;
1164 case sunxi_monitor_lcd:
1165 if (!sunxi_has_lcd()) {
1166 printf("LCD not supported on this board\n");
Jagan Teki5d235322021-02-22 00:12:34 +00001167 sunxi_display->monitor = sunxi_monitor_none;
1168 return -EINVAL;
Hans de Goedebf689342015-08-03 23:01:38 +02001169 }
Jagan Teki5d235322021-02-22 00:12:34 +00001170 sunxi_display->depth = video_get_params(&custom, lcd_mode);
Hans de Goedebf689342015-08-03 23:01:38 +02001171 mode = &custom;
1172 break;
Hans de Goede0e045212014-12-21 14:49:34 +01001173 case sunxi_monitor_vga:
Hans de Goedebf689342015-08-03 23:01:38 +02001174 if (!sunxi_has_vga()) {
1175 printf("VGA not supported on this board\n");
Jagan Teki5d235322021-02-22 00:12:34 +00001176 sunxi_display->monitor = sunxi_monitor_none;
1177 return -EINVAL;
Hans de Goedebf689342015-08-03 23:01:38 +02001178 }
Jagan Teki5d235322021-02-22 00:12:34 +00001179 sunxi_display->depth = 18;
Hans de Goedee2bbdfb2014-12-24 12:17:07 +01001180 break;
Hans de Goede39920c82015-08-03 19:20:26 +02001181 case sunxi_monitor_composite_pal:
1182 case sunxi_monitor_composite_ntsc:
1183 case sunxi_monitor_composite_pal_m:
1184 case sunxi_monitor_composite_pal_nc:
1185 if (!sunxi_has_composite()) {
1186 printf("Composite video not supported on this board\n");
Jagan Teki5d235322021-02-22 00:12:34 +00001187 sunxi_display->monitor = sunxi_monitor_none;
1188 return -EINVAL;
Hans de Goede39920c82015-08-03 19:20:26 +02001189 }
Jagan Teki5d235322021-02-22 00:12:34 +00001190 if (sunxi_display->monitor == sunxi_monitor_composite_pal ||
1191 sunxi_display->monitor == sunxi_monitor_composite_pal_nc)
Hans de Goede39920c82015-08-03 19:20:26 +02001192 mode = &composite_video_modes[0];
1193 else
1194 mode = &composite_video_modes[1];
Jagan Teki5d235322021-02-22 00:12:34 +00001195 sunxi_display->depth = 24;
Hans de Goede39920c82015-08-03 19:20:26 +02001196 break;
Hans de Goede75481602014-12-19 16:05:12 +01001197 }
1198
Hans de Goede58332f82015-08-05 00:06:47 +02001199 /* Yes these defaults are quite high, overscan on composite sucks... */
1200 if (overscan_x == -1)
Jagan Teki5d235322021-02-22 00:12:34 +00001201 overscan_x = sunxi_is_composite(sunxi_display->monitor) ? 32 : 0;
Hans de Goede58332f82015-08-05 00:06:47 +02001202 if (overscan_y == -1)
Jagan Teki5d235322021-02-22 00:12:34 +00001203 overscan_y = sunxi_is_composite(sunxi_display->monitor) ? 20 : 0;
Hans de Goede58332f82015-08-05 00:06:47 +02001204
Jagan Teki5d235322021-02-22 00:12:34 +00001205 sunxi_display->fb_size = plat->size;
Hans de Goede58332f82015-08-05 00:06:47 +02001206 overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
1207 /* We want to keep the fb_base for simplefb page aligned, where as
1208 * the sunxi dma engines will happily accept an unaligned address. */
1209 if (overscan_offset)
Jagan Teki5d235322021-02-22 00:12:34 +00001210 sunxi_display->fb_size += 0x1000;
Hans de Goede20779ec2015-02-02 18:00:53 +01001211
Hans de Goede58332f82015-08-05 00:06:47 +02001212 printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
1213 mode->xres, mode->yres,
Hans de Goedef6d9d322015-08-02 16:49:29 +02001214 (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
Jagan Teki5d235322021-02-22 00:12:34 +00001215 sunxi_get_mon_desc(sunxi_display->monitor),
Hans de Goede58332f82015-08-05 00:06:47 +02001216 overscan_x, overscan_y);
Hans de Goedef6d9d322015-08-02 16:49:29 +02001217
Jagan Teki5d235322021-02-22 00:12:34 +00001218 sunxi_display->fb_addr = plat->base;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001219 sunxi_engines_init();
Hans de Goede58332f82015-08-05 00:06:47 +02001220
Heinrich Schuchardtd06717f2018-03-03 10:30:17 +01001221#ifdef CONFIG_EFI_LOADER
Jagan Teki5d235322021-02-22 00:12:34 +00001222 efi_add_memory_map(sunxi_display->fb_addr, sunxi_display->fb_size,
Michael Walle714497e2020-05-17 12:29:19 +02001223 EFI_RESERVED_MEMORY_TYPE);
Heinrich Schuchardtd06717f2018-03-03 10:30:17 +01001224#endif
1225
Tom Riniaa6e94d2022-11-16 13:10:37 -05001226 fb_dma_addr = sunxi_display->fb_addr - CFG_SYS_SDRAM_BASE;
Hans de Goede58332f82015-08-05 00:06:47 +02001227 if (overscan_offset) {
1228 fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
Jagan Teki5d235322021-02-22 00:12:34 +00001229 sunxi_display->fb_addr += ALIGN(overscan_offset, 0x1000);
1230 memset((void *)sunxi_display->fb_addr, 0, sunxi_display->fb_size);
1231 flush_cache(sunxi_display->fb_addr, sunxi_display->fb_size);
Hans de Goede58332f82015-08-05 00:06:47 +02001232 }
Jagan Teki5d235322021-02-22 00:12:34 +00001233 sunxi_mode_set(sunxi_display, mode, fb_dma_addr);
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001234
Jagan Teki5d235322021-02-22 00:12:34 +00001235 /* The members of struct video_priv to be set by the driver. */
1236 uc_priv->bpix = VIDEO_BPP32;
1237 uc_priv->xsize = mode->xres;
1238 uc_priv->ysize = mode->yres;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001239
Jagan Teki5d235322021-02-22 00:12:34 +00001240 video_set_flush_dcache(dev, true);
1241
1242 return 0;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001243}
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001244
Jagan Teki5d235322021-02-22 00:12:34 +00001245static int sunxi_de_bind(struct udevice *dev)
1246{
1247 struct video_uc_plat *plat = dev_get_uclass_plat(dev);
1248
1249 plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * VNBYTES(LCD_MAX_LOG2_BPP);
1250
1251 return 0;
1252}
1253
1254static const struct video_ops sunxi_de_ops = {
1255};
1256
1257U_BOOT_DRIVER(sunxi_de) = {
1258 .name = "sunxi_de",
1259 .id = UCLASS_VIDEO,
1260 .ops = &sunxi_de_ops,
1261 .bind = sunxi_de_bind,
1262 .probe = sunxi_de_probe,
1263 .priv_auto = sizeof(struct sunxi_display_priv),
1264 .flags = DM_FLAG_PRE_RELOC,
1265};
1266
1267U_BOOT_DRVINFO(sunxi_de) = {
1268 .name = "sunxi_de"
1269};
1270
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001271/*
1272 * Simplefb support.
1273 */
1274#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
1275int sunxi_simplefb_setup(void *blob)
1276{
Jagan Teki5d235322021-02-22 00:12:34 +00001277 struct sunxi_display_priv *sunxi_display;
1278 struct video_priv *uc_priv;
1279 struct udevice *de;
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001280 int offset, ret;
Hans de Goede5633a292015-02-02 17:13:29 +01001281 u64 start, size;
Hans de Goede2dae8002014-12-21 16:28:32 +01001282 const char *pipeline = NULL;
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001283
Hans de Goede7cd6f922015-01-19 08:44:07 +01001284#ifdef CONFIG_MACH_SUN4I
1285#define PIPELINE_PREFIX "de_fe0-"
1286#else
1287#define PIPELINE_PREFIX
1288#endif
1289
Jagan Teki5d235322021-02-22 00:12:34 +00001290 ret = uclass_find_device_by_name(UCLASS_VIDEO, "sunxi_de", &de);
1291 if (ret) {
1292 printf("DE not present\n");
1293 return 0;
1294 } else if (!device_active(de)) {
1295 printf("DE is present but not probed\n");
1296 return 0;
1297 }
1298
1299 uc_priv = dev_get_uclass_priv(de);
1300 sunxi_display = dev_get_priv(de);
1301
1302 switch (sunxi_display->monitor) {
Hans de Goede2dae8002014-12-21 16:28:32 +01001303 case sunxi_monitor_none:
1304 return 0;
1305 case sunxi_monitor_dvi:
1306 case sunxi_monitor_hdmi:
Hans de Goede7cd6f922015-01-19 08:44:07 +01001307 pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi";
Hans de Goede2dae8002014-12-21 16:28:32 +01001308 break;
1309 case sunxi_monitor_lcd:
Hans de Goede7cd6f922015-01-19 08:44:07 +01001310 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede2dae8002014-12-21 16:28:32 +01001311 break;
1312 case sunxi_monitor_vga:
Hans de Goeded9786d22014-12-25 13:58:06 +01001313#ifdef CONFIG_VIDEO_VGA
Hans de Goede7cd6f922015-01-19 08:44:07 +01001314 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
Hans de Goeded9786d22014-12-25 13:58:06 +01001315#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goede7cd6f922015-01-19 08:44:07 +01001316 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goeded9786d22014-12-25 13:58:06 +01001317#endif
Hans de Goede2dae8002014-12-21 16:28:32 +01001318 break;
Hans de Goede39920c82015-08-03 19:20:26 +02001319 case sunxi_monitor_composite_pal:
1320 case sunxi_monitor_composite_ntsc:
1321 case sunxi_monitor_composite_pal_m:
1322 case sunxi_monitor_composite_pal_nc:
1323 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
1324 break;
Hans de Goede2dae8002014-12-21 16:28:32 +01001325 }
1326
Icenowy Zhenge5f92462017-10-26 11:14:45 +08001327 offset = sunxi_simplefb_fdt_match(blob, pipeline);
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001328 if (offset < 0) {
1329 eprintf("Cannot setup simplefb: node not found\n");
1330 return 0; /* Keep older kernels working */
1331 }
1332
Hans de Goede5633a292015-02-02 17:13:29 +01001333 /*
1334 * Do not report the framebuffer as free RAM to the OS, note we cannot
1335 * use fdt_add_mem_rsv() here, because then it is still seen as RAM,
1336 * and e.g. Linux refuses to iomap RAM on ARM, see:
1337 * linux/arch/arm/mm/ioremap.c around line 301.
1338 */
1339 start = gd->bd->bi_dram[0].start;
Jagan Teki5d235322021-02-22 00:12:34 +00001340 size = sunxi_display->fb_addr - start;
Hans de Goede5633a292015-02-02 17:13:29 +01001341 ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
1342 if (ret) {
1343 eprintf("Cannot setup simplefb: Error reserving memory\n");
1344 return ret;
1345 }
1346
Jagan Teki5d235322021-02-22 00:12:34 +00001347 ret = fdt_setup_simplefb_node(blob, offset, sunxi_display->fb_addr,
1348 uc_priv->xsize, uc_priv->ysize,
1349 VNBYTES(uc_priv->bpix) * uc_priv->xsize,
1350 "x8r8g8b8");
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001351 if (ret)
1352 eprintf("Cannot setup simplefb: Error setting properties\n");
1353
1354 return ret;
1355}
1356#endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */