blob: c4c1d1b8d3daba25577ecb0471f3c67703136e5c [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
9#include <common.h>
Simon Glass1eb69ae2019-11-14 12:57:39 -070010#include <cpu_func.h>
Heinrich Schuchardtd06717f2018-03-03 10:30:17 +010011#include <efi_loader.h>
Simon Glass67c4e9f2019-11-14 12:57:45 -070012#include <init.h>
Simon Glass10453152019-11-14 12:57:30 -070013#include <time.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020014
15#include <asm/arch/clock.h>
16#include <asm/arch/display.h>
Hans de Goede2dae8002014-12-21 16:28:32 +010017#include <asm/arch/gpio.h>
Jernej Skrabec5e023e72017-03-27 19:22:29 +020018#include <asm/arch/lcdc.h>
Hans de Goede421c98d2016-08-19 15:25:41 +020019#include <asm/arch/pwm.h>
Jernej Skrabecc1080622017-05-10 18:46:28 +020020#include <asm/arch/tve.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020021#include <asm/global_data.h>
Hans de Goede2dae8002014-12-21 16:28:32 +010022#include <asm/gpio.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020023#include <asm/io.h>
Hans de Goede6944aff2015-10-03 15:18:33 +020024#include <axp_pmic.h>
Hans de Goede75481602014-12-19 16:05:12 +010025#include <errno.h>
Luc Verhaegen2d7a0842014-08-13 07:55:07 +020026#include <fdtdec.h>
27#include <fdt_support.h>
Hans de Goedeaad2ac22015-02-16 17:49:47 +010028#include <i2c.h>
Hans de Goede58332f82015-08-05 00:06:47 +020029#include <malloc.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020030#include <video_fb.h>
Jernej Skrabec5e023e72017-03-27 19:22:29 +020031#include "../videomodes.h"
32#include "../anx9804.h"
33#include "../hitachi_tx18d42vm_lcd.h"
34#include "../ssd2828.h"
Icenowy Zhenge5f92462017-10-26 11:14:45 +080035#include "simplefb_common.h"
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020036
Hans de Goedea7403ae2015-01-22 21:02:42 +010037#ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW
38#define PWM_ON 0
39#define PWM_OFF 1
40#else
41#define PWM_ON 1
42#define PWM_OFF 0
43#endif
44
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020045DECLARE_GLOBAL_DATA_PTR;
46
Hans de Goede1c092202014-12-21 14:37:45 +010047enum sunxi_monitor {
48 sunxi_monitor_none,
49 sunxi_monitor_dvi,
50 sunxi_monitor_hdmi,
51 sunxi_monitor_lcd,
52 sunxi_monitor_vga,
Hans de Goede39920c82015-08-03 19:20:26 +020053 sunxi_monitor_composite_pal,
54 sunxi_monitor_composite_ntsc,
55 sunxi_monitor_composite_pal_m,
56 sunxi_monitor_composite_pal_nc,
Hans de Goede1c092202014-12-21 14:37:45 +010057};
Hans de Goede39920c82015-08-03 19:20:26 +020058#define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
Hans de Goede1c092202014-12-21 14:37:45 +010059
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020060struct sunxi_display {
61 GraphicDevice graphic_device;
Hans de Goede1c092202014-12-21 14:37:45 +010062 enum sunxi_monitor monitor;
Hans de Goede2dae8002014-12-21 16:28:32 +010063 unsigned int depth;
Hans de Goede58332f82015-08-05 00:06:47 +020064 unsigned int fb_addr;
Hans de Goede20779ec2015-02-02 18:00:53 +010065 unsigned int fb_size;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020066} sunxi_display;
67
Hans de Goede39920c82015-08-03 19:20:26 +020068const struct ctfb_res_modes composite_video_modes[2] = {
69 /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */
70 { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED },
71 { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED },
72};
73
Hans de Goede2fbf0912014-12-23 23:04:35 +010074#ifdef CONFIG_VIDEO_HDMI
75
Hans de Goede75481602014-12-19 16:05:12 +010076/*
77 * Wait up to 200ms for value to be set in given part of reg.
78 */
79static int await_completion(u32 *reg, u32 mask, u32 val)
80{
81 unsigned long tmo = timer_get_us() + 200000;
82
83 while ((readl(reg) & mask) != val) {
84 if (timer_get_us() > tmo) {
85 printf("DDC: timeout reading EDID\n");
86 return -ETIME;
87 }
88 }
89 return 0;
90}
91
Hans de Goede7fad8a92014-12-28 09:13:21 +010092static int sunxi_hdmi_hpd_detect(int hpd_delay)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020093{
94 struct sunxi_ccm_reg * const ccm =
95 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
96 struct sunxi_hdmi_reg * const hdmi =
97 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Hans de Goede7fad8a92014-12-28 09:13:21 +010098 unsigned long tmo = timer_get_us() + hpd_delay * 1000;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020099
100 /* Set pll3 to 300MHz */
101 clock_set_pll3(300000000);
102
103 /* Set hdmi parent to pll3 */
104 clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
105 CCM_HDMI_CTRL_PLL3);
106
107 /* Set ahb gating to pass */
Hans de Goede44d8ae52015-04-06 20:33:34 +0200108#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100109 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
110#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200111 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
112
113 /* Clock on */
114 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
115
116 writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl);
117 writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0);
118
Priit Laes361604d2018-12-19 15:06:08 +0200119 /* Enable PLLs for eventual DDC */
120 writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
121 &hdmi->pad_ctrl1);
122 writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
123 &hdmi->pll_ctrl);
124 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
125
Hans de Goede40f1b872014-12-20 15:15:23 +0100126 while (timer_get_us() < tmo) {
127 if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT)
128 return 1;
129 }
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200130
Hans de Goede40f1b872014-12-20 15:15:23 +0100131 return 0;
Hans de Goede518cef22014-12-19 15:13:57 +0100132}
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200133
Hans de Goede518cef22014-12-19 15:13:57 +0100134static void sunxi_hdmi_shutdown(void)
135{
136 struct sunxi_ccm_reg * const ccm =
137 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
138 struct sunxi_hdmi_reg * const hdmi =
139 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
140
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200141 clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE);
142 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
143 clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
Hans de Goede44d8ae52015-04-06 20:33:34 +0200144#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100145 clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
146#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200147 clock_set_pll3(0);
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200148}
149
Hans de Goede75481602014-12-19 16:05:12 +0100150static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
151{
152 struct sunxi_hdmi_reg * const hdmi =
153 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
154
155 setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
156 writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
157 SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
158 SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
159 SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
160#ifndef CONFIG_MACH_SUN6I
161 writel(n, &hdmi->ddc_byte_count);
162 writel(cmnd, &hdmi->ddc_cmnd);
163#else
164 writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
165#endif
166 setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
167
168 return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
169}
170
171static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
172{
173 struct sunxi_hdmi_reg * const hdmi =
174 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
175 int i, n;
176
177 while (count > 0) {
178 if (count > 16)
179 n = 16;
180 else
181 n = count;
182
183 if (sunxi_hdmi_ddc_do_command(
184 SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
185 offset, n))
186 return -ETIME;
187
188 for (i = 0; i < n; i++)
189 *buf++ = readb(&hdmi->ddc_fifo_data);
190
191 offset += n;
192 count -= n;
193 }
194
195 return 0;
196}
197
Hans de Goede63c5fbd2014-12-20 14:01:48 +0100198static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
199{
200 int r, retries = 2;
201
202 do {
203 r = sunxi_hdmi_ddc_read(block * 128, buf, 128);
204 if (r)
205 continue;
206 r = edid_check_checksum(buf);
207 if (r) {
208 printf("EDID block %d: checksum error%s\n",
209 block, retries ? ", retrying" : "");
210 }
211 } while (r && retries--);
212
213 return r;
214}
215
Priit Laes0220f8c2018-12-19 15:06:09 +0200216static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
217 bool verbose_mode)
Hans de Goede75481602014-12-19 16:05:12 +0100218{
219 struct edid1_info edid1;
Hans de Goedef3000682014-12-20 14:31:45 +0100220 struct edid_cea861_info cea681[4];
Hans de Goede75481602014-12-19 16:05:12 +0100221 struct edid_detailed_timing *t =
222 (struct edid_detailed_timing *)edid1.monitor_details.timing;
223 struct sunxi_hdmi_reg * const hdmi =
224 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
225 struct sunxi_ccm_reg * const ccm =
226 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Hans de Goedef3000682014-12-20 14:31:45 +0100227 int i, r, ext_blocks = 0;
Hans de Goede75481602014-12-19 16:05:12 +0100228
Hans de Goede75481602014-12-19 16:05:12 +0100229 /* Reset i2c controller */
230 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
231 writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
232 SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
233 SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
234 SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
235 if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
236 return -EIO;
237
238 writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
239#ifndef CONFIG_MACH_SUN6I
240 writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
241 SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
242#endif
243
Hans de Goede63c5fbd2014-12-20 14:01:48 +0100244 r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
Hans de Goedef3000682014-12-20 14:31:45 +0100245 if (r == 0) {
246 r = edid_check_info(&edid1);
247 if (r) {
Priit Laes0220f8c2018-12-19 15:06:09 +0200248 if (verbose_mode)
249 printf("EDID: invalid EDID data\n");
Hans de Goedef3000682014-12-20 14:31:45 +0100250 r = -EINVAL;
251 }
252 }
253 if (r == 0) {
254 ext_blocks = edid1.extension_flag;
255 if (ext_blocks > 4)
256 ext_blocks = 4;
257 for (i = 0; i < ext_blocks; i++) {
258 if (sunxi_hdmi_edid_get_block(1 + i,
259 (u8 *)&cea681[i]) != 0) {
260 ext_blocks = i;
261 break;
262 }
263 }
264 }
Hans de Goede75481602014-12-19 16:05:12 +0100265
266 /* Disable DDC engine, no longer needed */
267 clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
268 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
269
270 if (r)
271 return r;
272
Hans de Goede75481602014-12-19 16:05:12 +0100273 /* We want version 1.3 or 1.2 with detailed timing info */
274 if (edid1.version != 1 || (edid1.revision < 3 &&
275 !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
276 printf("EDID: unsupported version %d.%d\n",
277 edid1.version, edid1.revision);
278 return -EINVAL;
279 }
280
281 /* Take the first usable detailed timing */
282 for (i = 0; i < 4; i++, t++) {
283 r = video_edid_dtd_to_ctfb_res_modes(t, mode);
284 if (r == 0)
285 break;
286 }
287 if (i == 4) {
288 printf("EDID: no usable detailed timing found\n");
289 return -ENOENT;
290 }
291
Hans de Goedef3000682014-12-20 14:31:45 +0100292 /* Check for basic audio support, if found enable hdmi output */
Hans de Goede1c092202014-12-21 14:37:45 +0100293 sunxi_display.monitor = sunxi_monitor_dvi;
Hans de Goedef3000682014-12-20 14:31:45 +0100294 for (i = 0; i < ext_blocks; i++) {
295 if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
296 cea681[i].revision < 2)
297 continue;
298
299 if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
Hans de Goede1c092202014-12-21 14:37:45 +0100300 sunxi_display.monitor = sunxi_monitor_hdmi;
Hans de Goedef3000682014-12-20 14:31:45 +0100301 }
302
Hans de Goede75481602014-12-19 16:05:12 +0100303 return 0;
304}
305
Hans de Goede2fbf0912014-12-23 23:04:35 +0100306#endif /* CONFIG_VIDEO_HDMI */
307
Hans de Goede7cd6f922015-01-19 08:44:07 +0100308#ifdef CONFIG_MACH_SUN4I
309/*
310 * Testing has shown that on sun4i the display backend engine does not have
311 * deep enough fifo-s causing flickering / tearing in full-hd mode due to
312 * fifo underruns. So on sun4i we use the display frontend engine to do the
313 * dma from memory, as the frontend does have deep enough fifo-s.
314 */
315
316static const u32 sun4i_vert_coef[32] = {
317 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
318 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
319 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
320 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
321 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
322 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
323 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
324 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
325};
326
327static const u32 sun4i_horz_coef[64] = {
328 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
329 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
330 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
331 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
332 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
333 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
334 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
335 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
336 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
337 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
338 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
339 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
340 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
341 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
342 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
343 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
344};
345
346static void sunxi_frontend_init(void)
347{
348 struct sunxi_ccm_reg * const ccm =
349 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
350 struct sunxi_de_fe_reg * const de_fe =
351 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
352 int i;
353
354 /* Clocks on */
355 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0);
356 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0);
357 clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000);
358
359 setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN);
360
361 for (i = 0; i < 32; i++) {
362 writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]);
363 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]);
364 writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]);
365 writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]);
366 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]);
367 writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]);
368 }
369
370 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY);
371}
372
373static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
374 unsigned int address)
375{
376 struct sunxi_de_fe_reg * const de_fe =
377 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
378
379 setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS);
380 writel(CONFIG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr);
381 writel(mode->xres * 4, &de_fe->ch0_stride);
382 writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt);
383 writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt);
384
385 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
386 &de_fe->ch0_insize);
387 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
388 &de_fe->ch0_outsize);
389 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact);
390 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact);
391
392 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
393 &de_fe->ch1_insize);
394 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
395 &de_fe->ch1_outsize);
396 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact);
397 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact);
398
399 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY);
400}
401
402static void sunxi_frontend_enable(void)
403{
404 struct sunxi_de_fe_reg * const de_fe =
405 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
406
407 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START);
408}
409#else
410static void sunxi_frontend_init(void) {}
411static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
412 unsigned int address) {}
413static void sunxi_frontend_enable(void) {}
414#endif
415
Hans de Goede39920c82015-08-03 19:20:26 +0200416static bool sunxi_is_composite(void)
417{
418 switch (sunxi_display.monitor) {
419 case sunxi_monitor_none:
420 case sunxi_monitor_dvi:
421 case sunxi_monitor_hdmi:
422 case sunxi_monitor_lcd:
423 case sunxi_monitor_vga:
424 return false;
425 case sunxi_monitor_composite_pal:
426 case sunxi_monitor_composite_ntsc:
427 case sunxi_monitor_composite_pal_m:
428 case sunxi_monitor_composite_pal_nc:
429 return true;
430 }
431
432 return false; /* Never reached */
433}
434
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200435/*
436 * This is the entity that mixes and matches the different layers and inputs.
437 * Allwinner calls it the back-end, but i like composer better.
438 */
439static void sunxi_composer_init(void)
440{
441 struct sunxi_ccm_reg * const ccm =
442 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
443 struct sunxi_de_be_reg * const de_be =
444 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
445 int i;
446
Hans de Goede7cd6f922015-01-19 08:44:07 +0100447 sunxi_frontend_init();
448
Hans de Goede44d8ae52015-04-06 20:33:34 +0200449#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100450 /* Reset off */
451 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0);
452#endif
453
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200454 /* Clocks on */
455 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100456#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200457 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100458#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200459 clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000);
460
461 /* Engine bug, clear registers after reset */
462 for (i = 0x0800; i < 0x1000; i += 4)
463 writel(0, SUNXI_DE_BE0_BASE + i);
464
465 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE);
466}
467
Priit Laes3d99a0b2018-10-23 20:20:31 +0300468static const u32 sunxi_rgb2yuv_coef[12] = {
Hans de Goede39920c82015-08-03 19:20:26 +0200469 0x00000107, 0x00000204, 0x00000064, 0x00000108,
470 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,
471 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
472};
473
Hans de Goedebe8ec632014-12-19 13:46:33 +0100474static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200475 unsigned int address)
476{
477 struct sunxi_de_be_reg * const de_be =
478 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
Hans de Goede39920c82015-08-03 19:20:26 +0200479 int i;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200480
Hans de Goede7cd6f922015-01-19 08:44:07 +0100481 sunxi_frontend_mode_set(mode, address);
482
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200483 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
484 &de_be->disp_size);
485 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
486 &de_be->layer0_size);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100487#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200488 writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride);
489 writel(address << 3, &de_be->layer0_addr_low32b);
490 writel(address >> 29, &de_be->layer0_addr_high4b);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100491#else
492 writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl);
493#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200494 writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl);
495
496 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE);
Hans de Goedef6d9d322015-08-02 16:49:29 +0200497 if (mode->vmode == FB_VMODE_INTERLACED)
498 setbits_le32(&de_be->mode,
Hans de Goeded8d07992015-08-06 12:08:33 +0200499#ifndef CONFIG_MACH_SUN5I
Hans de Goedef6d9d322015-08-02 16:49:29 +0200500 SUNXI_DE_BE_MODE_DEFLICKER_ENABLE |
Hans de Goeded8d07992015-08-06 12:08:33 +0200501#endif
Hans de Goedef6d9d322015-08-02 16:49:29 +0200502 SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
Hans de Goede39920c82015-08-03 19:20:26 +0200503
504 if (sunxi_is_composite()) {
505 writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
506 &de_be->output_color_ctrl);
507 for (i = 0; i < 12; i++)
508 writel(sunxi_rgb2yuv_coef[i],
509 &de_be->output_color_coef[i]);
510 }
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200511}
512
Hans de Goede0e045212014-12-21 14:49:34 +0100513static void sunxi_composer_enable(void)
514{
515 struct sunxi_de_be_reg * const de_be =
516 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
517
Hans de Goede7cd6f922015-01-19 08:44:07 +0100518 sunxi_frontend_enable();
519
Hans de Goede0e045212014-12-21 14:49:34 +0100520 setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS);
521 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
522}
523
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200524static void sunxi_lcdc_init(void)
525{
526 struct sunxi_ccm_reg * const ccm =
527 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
528 struct sunxi_lcdc_reg * const lcdc =
529 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
530
531 /* Reset off */
Hans de Goede44d8ae52015-04-06 20:33:34 +0200532#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100533 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
534#else
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200535 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST);
Hans de Goede211717a2014-11-14 17:42:14 +0100536#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200537
538 /* Clock on */
539 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
Hans de Goede213480e2015-01-01 22:04:34 +0100540#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Hans de Goede83edb2a2015-05-14 18:52:54 +0200541#ifdef CONFIG_SUNXI_GEN_SUN6I
542 setbits_le32(&ccm->ahb_reset2_cfg, 1 << AHB_RESET_OFFSET_LVDS);
543#else
Hans de Goede213480e2015-01-01 22:04:34 +0100544 setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
545#endif
Hans de Goede83edb2a2015-05-14 18:52:54 +0200546#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200547
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200548 lcdc_init(lcdc);
Hans de Goede0e045212014-12-21 14:49:34 +0100549}
550
Hans de Goede2dae8002014-12-21 16:28:32 +0100551static void sunxi_lcdc_panel_enable(void)
552{
Hans de Goede242e3d82015-02-16 17:26:41 +0100553 int pin, reset_pin;
Hans de Goede2dae8002014-12-21 16:28:32 +0100554
555 /*
556 * Start with backlight disabled to avoid the screen flashing to
557 * white while the lcd inits.
558 */
559 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede15728192015-04-22 17:45:59 +0200560 if (pin >= 0) {
Hans de Goede2dae8002014-12-21 16:28:32 +0100561 gpio_request(pin, "lcd_backlight_enable");
562 gpio_direction_output(pin, 0);
563 }
564
565 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede15728192015-04-22 17:45:59 +0200566 if (pin >= 0) {
Hans de Goede2dae8002014-12-21 16:28:32 +0100567 gpio_request(pin, "lcd_backlight_pwm");
Hans de Goedea7403ae2015-01-22 21:02:42 +0100568 gpio_direction_output(pin, PWM_OFF);
Hans de Goede2dae8002014-12-21 16:28:32 +0100569 }
570
Hans de Goede242e3d82015-02-16 17:26:41 +0100571 reset_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_RESET);
Hans de Goede15728192015-04-22 17:45:59 +0200572 if (reset_pin >= 0) {
Hans de Goede242e3d82015-02-16 17:26:41 +0100573 gpio_request(reset_pin, "lcd_reset");
574 gpio_direction_output(reset_pin, 0); /* Assert reset */
575 }
576
Hans de Goede2dae8002014-12-21 16:28:32 +0100577 /* Give the backlight some time to turn off and power up the panel. */
578 mdelay(40);
579 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
Hans de Goede15728192015-04-22 17:45:59 +0200580 if (pin >= 0) {
Hans de Goede2dae8002014-12-21 16:28:32 +0100581 gpio_request(pin, "lcd_power");
582 gpio_direction_output(pin, 1);
583 }
Hans de Goede242e3d82015-02-16 17:26:41 +0100584
Hans de Goede15728192015-04-22 17:45:59 +0200585 if (reset_pin >= 0)
Hans de Goede242e3d82015-02-16 17:26:41 +0100586 gpio_direction_output(reset_pin, 1); /* De-assert reset */
Hans de Goede2dae8002014-12-21 16:28:32 +0100587}
588
589static void sunxi_lcdc_backlight_enable(void)
590{
591 int pin;
592
593 /*
594 * We want to have scanned out at least one frame before enabling the
595 * backlight to avoid the screen flashing to white when we enable it.
596 */
597 mdelay(40);
598
599 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede15728192015-04-22 17:45:59 +0200600 if (pin >= 0)
Hans de Goede2dae8002014-12-21 16:28:32 +0100601 gpio_direction_output(pin, 1);
602
603 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede421c98d2016-08-19 15:25:41 +0200604#ifdef SUNXI_PWM_PIN0
605 if (pin == SUNXI_PWM_PIN0) {
606 writel(SUNXI_PWM_CTRL_POLARITY0(PWM_ON) |
607 SUNXI_PWM_CTRL_ENABLE0 |
608 SUNXI_PWM_CTRL_PRESCALE0(0xf), SUNXI_PWM_CTRL_REG);
609 writel(SUNXI_PWM_PERIOD_80PCT, SUNXI_PWM_CH0_PERIOD);
610 sunxi_gpio_set_cfgpin(pin, SUNXI_PWM_MUX);
611 return;
612 }
613#endif
Hans de Goede15728192015-04-22 17:45:59 +0200614 if (pin >= 0)
Hans de Goedea7403ae2015-01-22 21:02:42 +0100615 gpio_direction_output(pin, PWM_ON);
Hans de Goede2dae8002014-12-21 16:28:32 +0100616}
617
Hans de Goedefb75d972015-01-25 15:33:07 +0100618static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
619 bool for_ext_vga_dac)
Hans de Goede2dae8002014-12-21 16:28:32 +0100620{
621 struct sunxi_lcdc_reg * const lcdc =
622 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700623 struct sunxi_ccm_reg * const ccm =
624 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200625 int clk_div, clk_double, pin;
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200626 struct display_timing timing;
Hans de Goede2dae8002014-12-21 16:28:32 +0100627
Lawrence Yucf6eca72016-03-04 09:08:56 -0800628#if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS
629 for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) {
630#else
Hans de Goedec1cfd512015-08-08 16:13:53 +0200631 for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) {
Lawrence Yucf6eca72016-03-04 09:08:56 -0800632#endif
Hans de Goede213480e2015-01-01 22:04:34 +0100633#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Paul Kocialkowski487b3272015-03-22 18:12:22 +0100634 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0);
Hans de Goede213480e2015-01-01 22:04:34 +0100635#endif
636#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Paul Kocialkowski487b3272015-03-22 18:12:22 +0100637 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LVDS0);
Hans de Goede213480e2015-01-01 22:04:34 +0100638#endif
Hans de Goedec1cfd512015-08-08 16:13:53 +0200639#ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804
640 sunxi_gpio_set_drv(pin, 3);
641#endif
642 }
Hans de Goede2dae8002014-12-21 16:28:32 +0100643
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700644 lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double,
645 sunxi_is_composite());
Hans de Goede2dae8002014-12-21 16:28:32 +0100646
Giulio Benetti92a68362020-04-08 17:10:12 +0200647 video_ctfb_mode_to_display_timing(mode, &timing);
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200648 lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200649 sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
Hans de Goede2dae8002014-12-21 16:28:32 +0100650}
651
Hans de Goede39920c82015-08-03 19:20:26 +0200652#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goede0e045212014-12-21 14:49:34 +0100653static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
Hans de Goede3ffbe472014-12-27 15:19:23 +0100654 int *clk_div, int *clk_double,
655 bool use_portd_hvsync)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200656{
657 struct sunxi_lcdc_reg * const lcdc =
658 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700659 struct sunxi_ccm_reg * const ccm =
660 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200661 struct display_timing timing;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200662
Giulio Benetti92a68362020-04-08 17:10:12 +0200663 video_ctfb_mode_to_display_timing(mode, &timing);
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200664 lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200665 sunxi_is_composite());
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200666
Hans de Goede3ffbe472014-12-27 15:19:23 +0100667 if (use_portd_hvsync) {
Paul Kocialkowski487b3272015-03-22 18:12:22 +0100668 sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
669 sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0);
Hans de Goede3ffbe472014-12-27 15:19:23 +0100670 }
Hans de Goeded8d07992015-08-06 12:08:33 +0200671
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700672 lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double,
673 sunxi_is_composite());
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200674}
Hans de Goede39920c82015-08-03 19:20:26 +0200675#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
Hans de Goeded9786d22014-12-25 13:58:06 +0100676
677#ifdef CONFIG_VIDEO_HDMI
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200678
Hans de Goede5ee0bea2014-12-20 13:38:06 +0100679static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
680{
681 struct sunxi_hdmi_reg * const hdmi =
682 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
683 u8 checksum = 0;
684 u8 avi_info_frame[17] = {
685 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00,
686 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
687 0x00
688 };
689 u8 vendor_info_frame[19] = {
690 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40,
691 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
692 0x00, 0x00, 0x00
693 };
694 int i;
695
696 if (mode->pixclock_khz <= 27000)
697 avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */
698 else
699 avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */
700
701 if (mode->xres * 100 / mode->yres < 156)
702 avi_info_frame[5] |= 0x18; /* 4 : 3 */
703 else
704 avi_info_frame[5] |= 0x28; /* 16 : 9 */
705
706 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
707 checksum += avi_info_frame[i];
708
709 avi_info_frame[3] = 0x100 - checksum;
710
711 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
712 writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]);
713
714 writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0);
715 writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1);
716
717 for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++)
718 writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]);
719
720 writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0);
721 writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1);
722
723 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI);
724}
725
Hans de Goedebe8ec632014-12-19 13:46:33 +0100726static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
Hans de Goede1c092202014-12-21 14:37:45 +0100727 int clk_div, int clk_double)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200728{
729 struct sunxi_hdmi_reg * const hdmi =
730 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
731 int x, y;
732
733 /* Write clear interrupt status bits */
734 writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq);
735
Hans de Goede1c092202014-12-21 14:37:45 +0100736 if (sunxi_display.monitor == sunxi_monitor_hdmi)
Hans de Goede5ee0bea2014-12-20 13:38:06 +0100737 sunxi_hdmi_setup_info_frames(mode);
738
Hans de Goede876aaaf2014-12-20 13:51:16 +0100739 /* Set input sync enable */
740 writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown);
741
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200742 /* Init various registers, select pll3 as clock source */
743 writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity);
744 writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0);
745 writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1);
746 writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl);
747 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
748
749 /* Setup clk div and doubler */
750 clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK,
751 SUNXI_HDMI_PLL_CTRL_DIV(clk_div));
752 if (!clk_double)
753 setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE);
754
755 /* Setup timing registers */
756 writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres),
757 &hdmi->video_size);
758
759 x = mode->hsync_len + mode->left_margin;
760 y = mode->vsync_len + mode->upper_margin;
761 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp);
762
763 x = mode->right_margin;
764 y = mode->lower_margin;
765 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp);
766
767 x = mode->hsync_len;
768 y = mode->vsync_len;
769 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw);
770
771 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
772 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR);
773
774 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
775 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER);
776}
777
Hans de Goede0e045212014-12-21 14:49:34 +0100778static void sunxi_hdmi_enable(void)
779{
780 struct sunxi_hdmi_reg * const hdmi =
781 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
782
783 udelay(100);
784 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE);
785}
786
Hans de Goede2fbf0912014-12-23 23:04:35 +0100787#endif /* CONFIG_VIDEO_HDMI */
788
Hans de Goede39920c82015-08-03 19:20:26 +0200789#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goeded9786d22014-12-25 13:58:06 +0100790
Hans de Goede39920c82015-08-03 19:20:26 +0200791static void sunxi_tvencoder_mode_set(void)
Hans de Goeded9786d22014-12-25 13:58:06 +0100792{
793 struct sunxi_ccm_reg * const ccm =
794 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
795 struct sunxi_tve_reg * const tve =
796 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
797
Hans de Goeded8d07992015-08-06 12:08:33 +0200798 /* Reset off */
799 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_TVE_RST);
Hans de Goeded9786d22014-12-25 13:58:06 +0100800 /* Clock on */
801 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
802
Hans de Goede39920c82015-08-03 19:20:26 +0200803 switch (sunxi_display.monitor) {
804 case sunxi_monitor_vga:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200805 tvencoder_mode_set(tve, tve_mode_vga);
Hans de Goede39920c82015-08-03 19:20:26 +0200806 break;
807 case sunxi_monitor_composite_pal_nc:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200808 tvencoder_mode_set(tve, tve_mode_composite_pal_nc);
809 break;
Hans de Goede39920c82015-08-03 19:20:26 +0200810 case sunxi_monitor_composite_pal:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200811 tvencoder_mode_set(tve, tve_mode_composite_pal);
Hans de Goede39920c82015-08-03 19:20:26 +0200812 break;
813 case sunxi_monitor_composite_pal_m:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200814 tvencoder_mode_set(tve, tve_mode_composite_pal_m);
815 break;
Hans de Goede39920c82015-08-03 19:20:26 +0200816 case sunxi_monitor_composite_ntsc:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200817 tvencoder_mode_set(tve, tve_mode_composite_ntsc);
Hans de Goede39920c82015-08-03 19:20:26 +0200818 break;
819 case sunxi_monitor_none:
820 case sunxi_monitor_dvi:
821 case sunxi_monitor_hdmi:
822 case sunxi_monitor_lcd:
823 break;
824 }
Hans de Goeded9786d22014-12-25 13:58:06 +0100825}
826
Hans de Goede39920c82015-08-03 19:20:26 +0200827#endif /* CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE */
Hans de Goeded9786d22014-12-25 13:58:06 +0100828
Hans de Goedee8400792014-12-23 18:39:52 +0100829static void sunxi_drc_init(void)
830{
Hans de Goede44d8ae52015-04-06 20:33:34 +0200831#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedee8400792014-12-23 18:39:52 +0100832 struct sunxi_ccm_reg * const ccm =
833 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
834
835 /* On sun6i the drc must be clocked even when in pass-through mode */
Vishnu Patekar8c3dacf2015-03-01 23:47:48 +0530836#ifdef CONFIG_MACH_SUN8I_A33
837 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_SAT);
838#endif
Hans de Goedee8400792014-12-23 18:39:52 +0100839 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0);
840 clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000);
841#endif
842}
843
Chen-Yu Tsai507e27d2015-01-12 18:02:11 +0800844#ifdef CONFIG_VIDEO_VGA_VIA_LCD
845static void sunxi_vga_external_dac_enable(void)
846{
847 int pin;
848
849 pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
Hans de Goede15728192015-04-22 17:45:59 +0200850 if (pin >= 0) {
Chen-Yu Tsai507e27d2015-01-12 18:02:11 +0800851 gpio_request(pin, "vga_enable");
852 gpio_direction_output(pin, 1);
853 }
854}
855#endif /* CONFIG_VIDEO_VGA_VIA_LCD */
856
Siarhei Siamashka97ece832015-01-19 05:23:33 +0200857#ifdef CONFIG_VIDEO_LCD_SSD2828
858static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
859{
860 struct ssd2828_config cfg = {
861 .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
862 .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
863 .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
864 .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
865 .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
866 .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
867 .ssd2828_color_depth = 24,
868#ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
869 .mipi_dsi_number_of_data_lanes = 4,
870 .mipi_dsi_bitrate_per_data_lane_mbps = 513,
871 .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
872 .mipi_dsi_delay_after_set_display_on_ms = 200
873#else
874#error MIPI LCD panel needs configuration parameters
875#endif
876 };
877
878 if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
879 printf("SSD2828: SPI pins are not properly configured\n");
880 return 1;
881 }
882 if (cfg.reset_pin == -1) {
883 printf("SSD2828: Reset pin is not properly configured\n");
884 return 1;
885 }
886
887 return ssd2828_init(&cfg, mode);
888}
889#endif /* CONFIG_VIDEO_LCD_SSD2828 */
890
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200891static void sunxi_engines_init(void)
892{
893 sunxi_composer_init();
894 sunxi_lcdc_init();
Hans de Goede211717a2014-11-14 17:42:14 +0100895 sunxi_drc_init();
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200896}
897
Hans de Goede1c092202014-12-21 14:37:45 +0100898static void sunxi_mode_set(const struct ctfb_res_modes *mode,
Hans de Goede5ee0bea2014-12-20 13:38:06 +0100899 unsigned int address)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200900{
Hans de Goeded9786d22014-12-25 13:58:06 +0100901 int __maybe_unused clk_div, clk_double;
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200902 struct sunxi_lcdc_reg * const lcdc =
903 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Jernej Skrabecc1080622017-05-10 18:46:28 +0200904 struct sunxi_tve_reg * __maybe_unused const tve =
905 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
Hans de Goeded9786d22014-12-25 13:58:06 +0100906
Hans de Goede0e045212014-12-21 14:49:34 +0100907 switch (sunxi_display.monitor) {
908 case sunxi_monitor_none:
909 break;
910 case sunxi_monitor_dvi:
Hans de Goeded9786d22014-12-25 13:58:06 +0100911 case sunxi_monitor_hdmi:
Hans de Goede2fbf0912014-12-23 23:04:35 +0100912#ifdef CONFIG_VIDEO_HDMI
Hans de Goede0e045212014-12-21 14:49:34 +0100913 sunxi_composer_mode_set(mode, address);
Hans de Goede3ffbe472014-12-27 15:19:23 +0100914 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
Hans de Goede0e045212014-12-21 14:49:34 +0100915 sunxi_hdmi_mode_set(mode, clk_div, clk_double);
916 sunxi_composer_enable();
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200917 lcdc_enable(lcdc, sunxi_display.depth);
Hans de Goede0e045212014-12-21 14:49:34 +0100918 sunxi_hdmi_enable();
Hans de Goede2fbf0912014-12-23 23:04:35 +0100919#endif
Hans de Goede0e045212014-12-21 14:49:34 +0100920 break;
921 case sunxi_monitor_lcd:
Hans de Goede2dae8002014-12-21 16:28:32 +0100922 sunxi_lcdc_panel_enable();
Hans de Goedec1cfd512015-08-08 16:13:53 +0200923 if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804)) {
924 /*
925 * The anx9804 needs 1.8V from eldo3, we do this here
Hans de Goede6944aff2015-10-03 15:18:33 +0200926 * and not via CONFIG_AXP_ELDO3_VOLT from board_init()
Hans de Goedec1cfd512015-08-08 16:13:53 +0200927 * to avoid turning this on when using hdmi output.
928 */
Hans de Goede6944aff2015-10-03 15:18:33 +0200929 axp_set_eldo(3, 1800);
Hans de Goedec1cfd512015-08-08 16:13:53 +0200930 anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4,
931 ANX9804_DATA_RATE_1620M,
932 sunxi_display.depth);
933 }
Hans de Goede27515b22015-01-20 09:23:36 +0100934 if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
935 mdelay(50); /* Wait for lcd controller power on */
936 hitachi_tx18d42vm_init();
937 }
Hans de Goedeaad2ac22015-02-16 17:49:47 +0100938 if (IS_ENABLED(CONFIG_VIDEO_LCD_TL059WV5C0)) {
939 unsigned int orig_i2c_bus = i2c_get_bus_num();
940 i2c_set_bus_num(CONFIG_VIDEO_LCD_I2C_BUS);
941 i2c_reg_write(0x5c, 0x04, 0x42); /* Turn on the LCD */
942 i2c_set_bus_num(orig_i2c_bus);
943 }
Hans de Goede2dae8002014-12-21 16:28:32 +0100944 sunxi_composer_mode_set(mode, address);
Hans de Goedefb75d972015-01-25 15:33:07 +0100945 sunxi_lcdc_tcon0_mode_set(mode, false);
Hans de Goede2dae8002014-12-21 16:28:32 +0100946 sunxi_composer_enable();
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200947 lcdc_enable(lcdc, sunxi_display.depth);
Siarhei Siamashka97ece832015-01-19 05:23:33 +0200948#ifdef CONFIG_VIDEO_LCD_SSD2828
949 sunxi_ssd2828_init(mode);
950#endif
Hans de Goede2dae8002014-12-21 16:28:32 +0100951 sunxi_lcdc_backlight_enable();
Hans de Goede0e045212014-12-21 14:49:34 +0100952 break;
953 case sunxi_monitor_vga:
Hans de Goeded9786d22014-12-25 13:58:06 +0100954#ifdef CONFIG_VIDEO_VGA
955 sunxi_composer_mode_set(mode, address);
956 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
Hans de Goede39920c82015-08-03 19:20:26 +0200957 sunxi_tvencoder_mode_set();
Hans de Goeded9786d22014-12-25 13:58:06 +0100958 sunxi_composer_enable();
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200959 lcdc_enable(lcdc, sunxi_display.depth);
Jernej Skrabecc1080622017-05-10 18:46:28 +0200960 tvencoder_enable(tve);
Hans de Goeded9786d22014-12-25 13:58:06 +0100961#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goedee2bbdfb2014-12-24 12:17:07 +0100962 sunxi_composer_mode_set(mode, address);
Hans de Goedefb75d972015-01-25 15:33:07 +0100963 sunxi_lcdc_tcon0_mode_set(mode, true);
Hans de Goedee2bbdfb2014-12-24 12:17:07 +0100964 sunxi_composer_enable();
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200965 lcdc_enable(lcdc, sunxi_display.depth);
Chen-Yu Tsai507e27d2015-01-12 18:02:11 +0800966 sunxi_vga_external_dac_enable();
Hans de Goedee2bbdfb2014-12-24 12:17:07 +0100967#endif
Hans de Goede0e045212014-12-21 14:49:34 +0100968 break;
Hans de Goede39920c82015-08-03 19:20:26 +0200969 case sunxi_monitor_composite_pal:
970 case sunxi_monitor_composite_ntsc:
971 case sunxi_monitor_composite_pal_m:
972 case sunxi_monitor_composite_pal_nc:
973#ifdef CONFIG_VIDEO_COMPOSITE
974 sunxi_composer_mode_set(mode, address);
975 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
976 sunxi_tvencoder_mode_set();
977 sunxi_composer_enable();
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200978 lcdc_enable(lcdc, sunxi_display.depth);
Jernej Skrabecc1080622017-05-10 18:46:28 +0200979 tvencoder_enable(tve);
Hans de Goede39920c82015-08-03 19:20:26 +0200980#endif
981 break;
Hans de Goede0e045212014-12-21 14:49:34 +0100982 }
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200983}
984
Hans de Goede1c092202014-12-21 14:37:45 +0100985static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
986{
987 switch (monitor) {
Hans de Goede39920c82015-08-03 19:20:26 +0200988 case sunxi_monitor_dvi: return "dvi";
989 case sunxi_monitor_hdmi: return "hdmi";
990 case sunxi_monitor_lcd: return "lcd";
991 case sunxi_monitor_vga: return "vga";
992 case sunxi_monitor_composite_pal: return "composite-pal";
993 case sunxi_monitor_composite_ntsc: return "composite-ntsc";
994 case sunxi_monitor_composite_pal_m: return "composite-pal-m";
995 case sunxi_monitor_composite_pal_nc: return "composite-pal-nc";
Bin Menga0c91fe2020-04-06 06:06:58 -0700996 case sunxi_monitor_none: /* fall through */
997 default: return "none";
Hans de Goede1c092202014-12-21 14:37:45 +0100998 }
Hans de Goede1c092202014-12-21 14:37:45 +0100999}
1000
Hans de Goede5633a292015-02-02 17:13:29 +01001001ulong board_get_usable_ram_top(ulong total_size)
1002{
1003 return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
1004}
1005
Hans de Goedebf689342015-08-03 23:01:38 +02001006static bool sunxi_has_hdmi(void)
1007{
1008#ifdef CONFIG_VIDEO_HDMI
1009 return true;
1010#else
1011 return false;
1012#endif
1013}
1014
1015static bool sunxi_has_lcd(void)
1016{
1017 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
1018
1019 return lcd_mode[0] != 0;
1020}
1021
1022static bool sunxi_has_vga(void)
1023{
1024#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_VGA_VIA_LCD
1025 return true;
1026#else
1027 return false;
1028#endif
1029}
1030
Hans de Goede39920c82015-08-03 19:20:26 +02001031static bool sunxi_has_composite(void)
1032{
1033#ifdef CONFIG_VIDEO_COMPOSITE
1034 return true;
1035#else
1036 return false;
1037#endif
1038}
1039
Hans de Goedebf689342015-08-03 23:01:38 +02001040static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
1041{
1042 if (allow_hdmi && sunxi_has_hdmi())
1043 return sunxi_monitor_dvi;
1044 else if (sunxi_has_lcd())
1045 return sunxi_monitor_lcd;
1046 else if (sunxi_has_vga())
1047 return sunxi_monitor_vga;
Hans de Goede39920c82015-08-03 19:20:26 +02001048 else if (sunxi_has_composite())
1049 return sunxi_monitor_composite_pal;
Hans de Goedebf689342015-08-03 23:01:38 +02001050 else
1051 return sunxi_monitor_none;
1052}
1053
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001054void *video_hw_init(void)
1055{
1056 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
Hans de Goede5f339932014-12-19 14:03:40 +01001057 const struct ctfb_res_modes *mode;
Hans de Goede2dae8002014-12-21 16:28:32 +01001058 struct ctfb_res_modes custom;
Hans de Goede5f339932014-12-19 14:03:40 +01001059 const char *options;
Hans de Goede2fbf0912014-12-23 23:04:35 +01001060#ifdef CONFIG_VIDEO_HDMI
Priit Laes0220f8c2018-12-19 15:06:09 +02001061 int hpd, hpd_delay, edid;
1062 bool hdmi_present;
Hans de Goede2fbf0912014-12-23 23:04:35 +01001063#endif
Hans de Goede58332f82015-08-05 00:06:47 +02001064 int i, overscan_offset, overscan_x, overscan_y;
1065 unsigned int fb_dma_addr;
Hans de Goede1c092202014-12-21 14:37:45 +01001066 char mon[16];
Hans de Goede2dae8002014-12-21 16:28:32 +01001067 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001068
1069 memset(&sunxi_display, 0, sizeof(struct sunxi_display));
1070
Hans de Goede2dae8002014-12-21 16:28:32 +01001071 video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
1072 &sunxi_display.depth, &options);
Hans de Goede2fbf0912014-12-23 23:04:35 +01001073#ifdef CONFIG_VIDEO_HDMI
Hans de Goede518cef22014-12-19 15:13:57 +01001074 hpd = video_get_option_int(options, "hpd", 1);
Hans de Goede7fad8a92014-12-28 09:13:21 +01001075 hpd_delay = video_get_option_int(options, "hpd_delay", 500);
Hans de Goede75481602014-12-19 16:05:12 +01001076 edid = video_get_option_int(options, "edid", 1);
Hans de Goede2fbf0912014-12-23 23:04:35 +01001077#endif
Hans de Goede58332f82015-08-05 00:06:47 +02001078 overscan_x = video_get_option_int(options, "overscan_x", -1);
1079 overscan_y = video_get_option_int(options, "overscan_y", -1);
Hans de Goedebf689342015-08-03 23:01:38 +02001080 sunxi_display.monitor = sunxi_get_default_mon(true);
Hans de Goede1c092202014-12-21 14:37:45 +01001081 video_get_option_string(options, "monitor", mon, sizeof(mon),
1082 sunxi_get_mon_desc(sunxi_display.monitor));
1083 for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
1084 if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
1085 sunxi_display.monitor = i;
1086 break;
1087 }
1088 }
1089 if (i > SUNXI_MONITOR_LAST)
1090 printf("Unknown monitor: '%s', falling back to '%s'\n",
1091 mon, sunxi_get_mon_desc(sunxi_display.monitor));
Hans de Goede5f339932014-12-19 14:03:40 +01001092
Hans de Goede49d27032014-12-25 13:52:04 +01001093#ifdef CONFIG_VIDEO_HDMI
1094 /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
1095 if (sunxi_display.monitor == sunxi_monitor_dvi ||
1096 sunxi_display.monitor == sunxi_monitor_hdmi) {
Hans de Goede0e045212014-12-21 14:49:34 +01001097 /* Always call hdp_detect, as it also enables clocks, etc. */
Priit Laes0220f8c2018-12-19 15:06:09 +02001098 hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
1099 if (hdmi_present && edid) {
Hans de Goede0e045212014-12-21 14:49:34 +01001100 printf("HDMI connected: ");
Priit Laes0220f8c2018-12-19 15:06:09 +02001101 if (sunxi_hdmi_edid_get_mode(&custom, true) == 0)
Hans de Goede2dae8002014-12-21 16:28:32 +01001102 mode = &custom;
Priit Laes0220f8c2018-12-19 15:06:09 +02001103 else
1104 hdmi_present = false;
1105 }
1106 /* Fall back to EDID in case HPD failed */
1107 if (edid && !hdmi_present) {
1108 if (sunxi_hdmi_edid_get_mode(&custom, false) == 0) {
1109 mode = &custom;
1110 hdmi_present = true;
1111 }
1112 }
1113 /* Shut down when display was not found */
1114 if ((hpd || edid) && !hdmi_present) {
Hans de Goede49d27032014-12-25 13:52:04 +01001115 sunxi_hdmi_shutdown();
Hans de Goedebf689342015-08-03 23:01:38 +02001116 sunxi_display.monitor = sunxi_get_default_mon(false);
Hans de Goede49d27032014-12-25 13:52:04 +01001117 } /* else continue with hdmi/dvi without a cable connected */
1118 }
1119#endif
Hans de Goede0e045212014-12-21 14:49:34 +01001120
Hans de Goede49d27032014-12-25 13:52:04 +01001121 switch (sunxi_display.monitor) {
1122 case sunxi_monitor_none:
1123 return NULL;
1124 case sunxi_monitor_dvi:
1125 case sunxi_monitor_hdmi:
Hans de Goedebf689342015-08-03 23:01:38 +02001126 if (!sunxi_has_hdmi()) {
1127 printf("HDMI/DVI not supported on this board\n");
1128 sunxi_display.monitor = sunxi_monitor_none;
1129 return NULL;
Hans de Goede2dae8002014-12-21 16:28:32 +01001130 }
Hans de Goedebf689342015-08-03 23:01:38 +02001131 break;
1132 case sunxi_monitor_lcd:
1133 if (!sunxi_has_lcd()) {
1134 printf("LCD not supported on this board\n");
1135 sunxi_display.monitor = sunxi_monitor_none;
1136 return NULL;
1137 }
1138 sunxi_display.depth = video_get_params(&custom, lcd_mode);
1139 mode = &custom;
1140 break;
Hans de Goede0e045212014-12-21 14:49:34 +01001141 case sunxi_monitor_vga:
Hans de Goedebf689342015-08-03 23:01:38 +02001142 if (!sunxi_has_vga()) {
1143 printf("VGA not supported on this board\n");
1144 sunxi_display.monitor = sunxi_monitor_none;
1145 return NULL;
1146 }
Hans de Goedee2bbdfb2014-12-24 12:17:07 +01001147 sunxi_display.depth = 18;
1148 break;
Hans de Goede39920c82015-08-03 19:20:26 +02001149 case sunxi_monitor_composite_pal:
1150 case sunxi_monitor_composite_ntsc:
1151 case sunxi_monitor_composite_pal_m:
1152 case sunxi_monitor_composite_pal_nc:
1153 if (!sunxi_has_composite()) {
1154 printf("Composite video not supported on this board\n");
1155 sunxi_display.monitor = sunxi_monitor_none;
1156 return NULL;
1157 }
1158 if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
1159 sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
1160 mode = &composite_video_modes[0];
1161 else
1162 mode = &composite_video_modes[1];
1163 sunxi_display.depth = 24;
1164 break;
Hans de Goede75481602014-12-19 16:05:12 +01001165 }
1166
Hans de Goede58332f82015-08-05 00:06:47 +02001167 /* Yes these defaults are quite high, overscan on composite sucks... */
1168 if (overscan_x == -1)
1169 overscan_x = sunxi_is_composite() ? 32 : 0;
1170 if (overscan_y == -1)
1171 overscan_y = sunxi_is_composite() ? 20 : 0;
1172
Hans de Goede20779ec2015-02-02 18:00:53 +01001173 sunxi_display.fb_size =
1174 (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
Hans de Goede58332f82015-08-05 00:06:47 +02001175 overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
1176 /* We want to keep the fb_base for simplefb page aligned, where as
1177 * the sunxi dma engines will happily accept an unaligned address. */
1178 if (overscan_offset)
1179 sunxi_display.fb_size += 0x1000;
1180
Hans de Goede20779ec2015-02-02 18:00:53 +01001181 if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
1182 printf("Error need %dkB for fb, but only %dkB is reserved\n",
1183 sunxi_display.fb_size >> 10,
1184 CONFIG_SUNXI_MAX_FB_SIZE >> 10);
1185 return NULL;
1186 }
1187
Hans de Goede58332f82015-08-05 00:06:47 +02001188 printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
1189 mode->xres, mode->yres,
Hans de Goedef6d9d322015-08-02 16:49:29 +02001190 (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
Hans de Goede58332f82015-08-05 00:06:47 +02001191 sunxi_get_mon_desc(sunxi_display.monitor),
1192 overscan_x, overscan_y);
Hans de Goedef6d9d322015-08-02 16:49:29 +02001193
Hans de Goede20779ec2015-02-02 18:00:53 +01001194 gd->fb_base = gd->bd->bi_dram[0].start +
1195 gd->bd->bi_dram[0].size - sunxi_display.fb_size;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001196 sunxi_engines_init();
Hans de Goede58332f82015-08-05 00:06:47 +02001197
Heinrich Schuchardtd06717f2018-03-03 10:30:17 +01001198#ifdef CONFIG_EFI_LOADER
Michael Walle714497e2020-05-17 12:29:19 +02001199 efi_add_memory_map(gd->fb_base, sunxi_display.fb_size,
1200 EFI_RESERVED_MEMORY_TYPE);
Heinrich Schuchardtd06717f2018-03-03 10:30:17 +01001201#endif
1202
Hans de Goede58332f82015-08-05 00:06:47 +02001203 fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
1204 sunxi_display.fb_addr = gd->fb_base;
1205 if (overscan_offset) {
1206 fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
1207 sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
1208 memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
1209 flush_cache(gd->fb_base, sunxi_display.fb_size);
1210 }
1211 sunxi_mode_set(mode, fb_dma_addr);
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001212
1213 /*
1214 * These are the only members of this structure that are used. All the
Hans de Goede58332f82015-08-05 00:06:47 +02001215 * others are driver specific. The pitch is stored in plnSizeX.
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001216 */
Hans de Goede58332f82015-08-05 00:06:47 +02001217 graphic_device->frameAdrs = sunxi_display.fb_addr;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001218 graphic_device->gdfIndex = GDF_32BIT_X888RGB;
1219 graphic_device->gdfBytesPP = 4;
Hans de Goede58332f82015-08-05 00:06:47 +02001220 graphic_device->winSizeX = mode->xres - 2 * overscan_x;
1221 graphic_device->winSizeY = mode->yres - 2 * overscan_y;
1222 graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001223
1224 return graphic_device;
1225}
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001226
1227/*
1228 * Simplefb support.
1229 */
1230#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
1231int sunxi_simplefb_setup(void *blob)
1232{
1233 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
1234 int offset, ret;
Hans de Goede5633a292015-02-02 17:13:29 +01001235 u64 start, size;
Hans de Goede2dae8002014-12-21 16:28:32 +01001236 const char *pipeline = NULL;
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001237
Hans de Goede7cd6f922015-01-19 08:44:07 +01001238#ifdef CONFIG_MACH_SUN4I
1239#define PIPELINE_PREFIX "de_fe0-"
1240#else
1241#define PIPELINE_PREFIX
1242#endif
1243
Hans de Goede2dae8002014-12-21 16:28:32 +01001244 switch (sunxi_display.monitor) {
1245 case sunxi_monitor_none:
1246 return 0;
1247 case sunxi_monitor_dvi:
1248 case sunxi_monitor_hdmi:
Hans de Goede7cd6f922015-01-19 08:44:07 +01001249 pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi";
Hans de Goede2dae8002014-12-21 16:28:32 +01001250 break;
1251 case sunxi_monitor_lcd:
Hans de Goede7cd6f922015-01-19 08:44:07 +01001252 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede2dae8002014-12-21 16:28:32 +01001253 break;
1254 case sunxi_monitor_vga:
Hans de Goeded9786d22014-12-25 13:58:06 +01001255#ifdef CONFIG_VIDEO_VGA
Hans de Goede7cd6f922015-01-19 08:44:07 +01001256 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
Hans de Goeded9786d22014-12-25 13:58:06 +01001257#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goede7cd6f922015-01-19 08:44:07 +01001258 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goeded9786d22014-12-25 13:58:06 +01001259#endif
Hans de Goede2dae8002014-12-21 16:28:32 +01001260 break;
Hans de Goede39920c82015-08-03 19:20:26 +02001261 case sunxi_monitor_composite_pal:
1262 case sunxi_monitor_composite_ntsc:
1263 case sunxi_monitor_composite_pal_m:
1264 case sunxi_monitor_composite_pal_nc:
1265 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
1266 break;
Hans de Goede2dae8002014-12-21 16:28:32 +01001267 }
1268
Icenowy Zhenge5f92462017-10-26 11:14:45 +08001269 offset = sunxi_simplefb_fdt_match(blob, pipeline);
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001270 if (offset < 0) {
1271 eprintf("Cannot setup simplefb: node not found\n");
1272 return 0; /* Keep older kernels working */
1273 }
1274
Hans de Goede5633a292015-02-02 17:13:29 +01001275 /*
1276 * Do not report the framebuffer as free RAM to the OS, note we cannot
1277 * use fdt_add_mem_rsv() here, because then it is still seen as RAM,
1278 * and e.g. Linux refuses to iomap RAM on ARM, see:
1279 * linux/arch/arm/mm/ioremap.c around line 301.
1280 */
1281 start = gd->bd->bi_dram[0].start;
Hans de Goede20779ec2015-02-02 18:00:53 +01001282 size = gd->bd->bi_dram[0].size - sunxi_display.fb_size;
Hans de Goede5633a292015-02-02 17:13:29 +01001283 ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
1284 if (ret) {
1285 eprintf("Cannot setup simplefb: Error reserving memory\n");
1286 return ret;
1287 }
1288
Hans de Goede58332f82015-08-05 00:06:47 +02001289 ret = fdt_setup_simplefb_node(blob, offset, sunxi_display.fb_addr,
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001290 graphic_device->winSizeX, graphic_device->winSizeY,
Hans de Goede58332f82015-08-05 00:06:47 +02001291 graphic_device->plnSizeX, "x8r8g8b8");
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001292 if (ret)
1293 eprintf("Cannot setup simplefb: Error setting properties\n");
1294
1295 return ret;
1296}
1297#endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */