blob: f52aba4d21c098aab5c2c5b43e98583cd36ccec5 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02002/*
3 * Display driver for Allwinner SoCs.
4 *
5 * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
Hans de Goedec06e00e2015-08-03 19:20:26 +02006 * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02007 */
8
9#include <common.h>
Simon Glass63334482019-11-14 12:57:39 -070010#include <cpu_func.h>
Heinrich Schuchardt084d3d92018-03-03 10:30:17 +010011#include <efi_loader.h>
Simon Glass6980b6b2019-11-14 12:57:45 -070012#include <init.h>
Simon Glass495a5dc2019-11-14 12:57:30 -070013#include <time.h>
Simon Glassdbd79542020-05-10 11:40:11 -060014#include <linux/delay.h>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020015
16#include <asm/arch/clock.h>
17#include <asm/arch/display.h>
Hans de Goede7e68a1b2014-12-21 16:28:32 +010018#include <asm/arch/gpio.h>
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +020019#include <asm/arch/lcdc.h>
Hans de Goede663ae652016-08-19 15:25:41 +020020#include <asm/arch/pwm.h>
Jernej Skrabec8531d082017-05-10 18:46:28 +020021#include <asm/arch/tve.h>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020022#include <asm/global_data.h>
Hans de Goede7e68a1b2014-12-21 16:28:32 +010023#include <asm/gpio.h>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020024#include <asm/io.h>
Hans de Goeded9ee84b2015-10-03 15:18:33 +020025#include <axp_pmic.h>
Hans de Goedea5aa95f2014-12-19 16:05:12 +010026#include <errno.h>
Luc Verhaegen4869a8c2014-08-13 07:55:07 +020027#include <fdtdec.h>
28#include <fdt_support.h>
Hans de Goede613dade2015-02-16 17:49:47 +010029#include <i2c.h>
Hans de Goeded955f442015-08-05 00:06:47 +020030#include <malloc.h>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020031#include <video_fb.h>
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +020032#include "../videomodes.h"
33#include "../anx9804.h"
34#include "../hitachi_tx18d42vm_lcd.h"
35#include "../ssd2828.h"
Icenowy Zheng067a6972017-10-26 11:14:45 +080036#include "simplefb_common.h"
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020037
Hans de Goede2d5d3022015-01-22 21:02:42 +010038#ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW
39#define PWM_ON 0
40#define PWM_OFF 1
41#else
42#define PWM_ON 1
43#define PWM_OFF 0
44#endif
45
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020046DECLARE_GLOBAL_DATA_PTR;
47
Hans de Goedea0b1b732014-12-21 14:37:45 +010048enum sunxi_monitor {
49 sunxi_monitor_none,
50 sunxi_monitor_dvi,
51 sunxi_monitor_hdmi,
52 sunxi_monitor_lcd,
53 sunxi_monitor_vga,
Hans de Goedec06e00e2015-08-03 19:20:26 +020054 sunxi_monitor_composite_pal,
55 sunxi_monitor_composite_ntsc,
56 sunxi_monitor_composite_pal_m,
57 sunxi_monitor_composite_pal_nc,
Hans de Goedea0b1b732014-12-21 14:37:45 +010058};
Hans de Goedec06e00e2015-08-03 19:20:26 +020059#define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
Hans de Goedea0b1b732014-12-21 14:37:45 +010060
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020061struct sunxi_display {
62 GraphicDevice graphic_device;
Hans de Goedea0b1b732014-12-21 14:37:45 +010063 enum sunxi_monitor monitor;
Hans de Goede7e68a1b2014-12-21 16:28:32 +010064 unsigned int depth;
Hans de Goeded955f442015-08-05 00:06:47 +020065 unsigned int fb_addr;
Hans de Goede18799252015-02-02 18:00:53 +010066 unsigned int fb_size;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020067} sunxi_display;
68
Hans de Goedec06e00e2015-08-03 19:20:26 +020069const struct ctfb_res_modes composite_video_modes[2] = {
70 /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */
71 { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED },
72 { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED },
73};
74
Hans de Goedee9544592014-12-23 23:04:35 +010075#ifdef CONFIG_VIDEO_HDMI
76
Hans de Goedea5aa95f2014-12-19 16:05:12 +010077/*
78 * Wait up to 200ms for value to be set in given part of reg.
79 */
80static int await_completion(u32 *reg, u32 mask, u32 val)
81{
82 unsigned long tmo = timer_get_us() + 200000;
83
84 while ((readl(reg) & mask) != val) {
85 if (timer_get_us() > tmo) {
86 printf("DDC: timeout reading EDID\n");
87 return -ETIME;
88 }
89 }
90 return 0;
91}
92
Hans de Goede91593712014-12-28 09:13:21 +010093static int sunxi_hdmi_hpd_detect(int hpd_delay)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020094{
95 struct sunxi_ccm_reg * const ccm =
96 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
97 struct sunxi_hdmi_reg * const hdmi =
98 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Hans de Goede91593712014-12-28 09:13:21 +010099 unsigned long tmo = timer_get_us() + hpd_delay * 1000;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200100
101 /* Set pll3 to 300MHz */
102 clock_set_pll3(300000000);
103
104 /* Set hdmi parent to pll3 */
105 clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
106 CCM_HDMI_CTRL_PLL3);
107
108 /* Set ahb gating to pass */
Hans de Goedef07872b2015-04-06 20:33:34 +0200109#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100110 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
111#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200112 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
113
114 /* Clock on */
115 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
116
117 writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl);
118 writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0);
119
Priit Laesc9ecd5c2018-12-19 15:06:08 +0200120 /* Enable PLLs for eventual DDC */
121 writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
122 &hdmi->pad_ctrl1);
123 writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
124 &hdmi->pll_ctrl);
125 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
126
Hans de Goede205a30c2014-12-20 15:15:23 +0100127 while (timer_get_us() < tmo) {
128 if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT)
129 return 1;
130 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200131
Hans de Goede205a30c2014-12-20 15:15:23 +0100132 return 0;
Hans de Goede695bda42014-12-19 15:13:57 +0100133}
134
135static void sunxi_hdmi_shutdown(void)
136{
137 struct sunxi_ccm_reg * const ccm =
138 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
139 struct sunxi_hdmi_reg * const hdmi =
140 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200141
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200142 clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE);
143 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
144 clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
Hans de Goedef07872b2015-04-06 20:33:34 +0200145#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100146 clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
147#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200148 clock_set_pll3(0);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200149}
150
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100151static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
152{
153 struct sunxi_hdmi_reg * const hdmi =
154 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
155
156 setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
157 writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
158 SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
159 SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
160 SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
161#ifndef CONFIG_MACH_SUN6I
162 writel(n, &hdmi->ddc_byte_count);
163 writel(cmnd, &hdmi->ddc_cmnd);
164#else
165 writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
166#endif
167 setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
168
169 return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
170}
171
172static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
173{
174 struct sunxi_hdmi_reg * const hdmi =
175 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
176 int i, n;
177
178 while (count > 0) {
179 if (count > 16)
180 n = 16;
181 else
182 n = count;
183
184 if (sunxi_hdmi_ddc_do_command(
185 SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
186 offset, n))
187 return -ETIME;
188
189 for (i = 0; i < n; i++)
190 *buf++ = readb(&hdmi->ddc_fifo_data);
191
192 offset += n;
193 count -= n;
194 }
195
196 return 0;
197}
198
Hans de Goede45b8f7b2014-12-20 14:01:48 +0100199static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
200{
201 int r, retries = 2;
202
203 do {
204 r = sunxi_hdmi_ddc_read(block * 128, buf, 128);
205 if (r)
206 continue;
207 r = edid_check_checksum(buf);
208 if (r) {
209 printf("EDID block %d: checksum error%s\n",
210 block, retries ? ", retrying" : "");
211 }
212 } while (r && retries--);
213
214 return r;
215}
216
Priit Laesa9d6db72018-12-19 15:06:09 +0200217static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
218 bool verbose_mode)
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100219{
220 struct edid1_info edid1;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100221 struct edid_cea861_info cea681[4];
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100222 struct edid_detailed_timing *t =
223 (struct edid_detailed_timing *)edid1.monitor_details.timing;
224 struct sunxi_hdmi_reg * const hdmi =
225 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
226 struct sunxi_ccm_reg * const ccm =
227 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100228 int i, r, ext_blocks = 0;
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100229
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100230 /* Reset i2c controller */
231 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
232 writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
233 SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
234 SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
235 SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
236 if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
237 return -EIO;
238
239 writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
240#ifndef CONFIG_MACH_SUN6I
241 writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
242 SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
243#endif
244
Hans de Goede45b8f7b2014-12-20 14:01:48 +0100245 r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100246 if (r == 0) {
247 r = edid_check_info(&edid1);
248 if (r) {
Priit Laesa9d6db72018-12-19 15:06:09 +0200249 if (verbose_mode)
250 printf("EDID: invalid EDID data\n");
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100251 r = -EINVAL;
252 }
253 }
254 if (r == 0) {
255 ext_blocks = edid1.extension_flag;
256 if (ext_blocks > 4)
257 ext_blocks = 4;
258 for (i = 0; i < ext_blocks; i++) {
259 if (sunxi_hdmi_edid_get_block(1 + i,
260 (u8 *)&cea681[i]) != 0) {
261 ext_blocks = i;
262 break;
263 }
264 }
265 }
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100266
267 /* Disable DDC engine, no longer needed */
268 clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
269 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
270
271 if (r)
272 return r;
273
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100274 /* We want version 1.3 or 1.2 with detailed timing info */
275 if (edid1.version != 1 || (edid1.revision < 3 &&
276 !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
277 printf("EDID: unsupported version %d.%d\n",
278 edid1.version, edid1.revision);
279 return -EINVAL;
280 }
281
282 /* Take the first usable detailed timing */
283 for (i = 0; i < 4; i++, t++) {
284 r = video_edid_dtd_to_ctfb_res_modes(t, mode);
285 if (r == 0)
286 break;
287 }
288 if (i == 4) {
289 printf("EDID: no usable detailed timing found\n");
290 return -ENOENT;
291 }
292
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100293 /* Check for basic audio support, if found enable hdmi output */
Hans de Goedea0b1b732014-12-21 14:37:45 +0100294 sunxi_display.monitor = sunxi_monitor_dvi;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100295 for (i = 0; i < ext_blocks; i++) {
296 if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
297 cea681[i].revision < 2)
298 continue;
299
300 if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
Hans de Goedea0b1b732014-12-21 14:37:45 +0100301 sunxi_display.monitor = sunxi_monitor_hdmi;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100302 }
303
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100304 return 0;
305}
306
Hans de Goedee9544592014-12-23 23:04:35 +0100307#endif /* CONFIG_VIDEO_HDMI */
308
Hans de Goedec3cc4262015-01-19 08:44:07 +0100309#ifdef CONFIG_MACH_SUN4I
310/*
311 * Testing has shown that on sun4i the display backend engine does not have
312 * deep enough fifo-s causing flickering / tearing in full-hd mode due to
313 * fifo underruns. So on sun4i we use the display frontend engine to do the
314 * dma from memory, as the frontend does have deep enough fifo-s.
315 */
316
317static const u32 sun4i_vert_coef[32] = {
318 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
319 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
320 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
321 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
322 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
323 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
324 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
325 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
326};
327
328static const u32 sun4i_horz_coef[64] = {
329 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
330 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
331 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
332 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
333 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
334 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
335 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
336 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
337 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
338 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
339 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
340 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
341 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
342 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
343 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
344 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
345};
346
347static void sunxi_frontend_init(void)
348{
349 struct sunxi_ccm_reg * const ccm =
350 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
351 struct sunxi_de_fe_reg * const de_fe =
352 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
353 int i;
354
355 /* Clocks on */
356 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0);
357 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0);
358 clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000);
359
360 setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN);
361
362 for (i = 0; i < 32; i++) {
363 writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]);
364 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]);
365 writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]);
366 writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]);
367 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]);
368 writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]);
369 }
370
371 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY);
372}
373
374static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
375 unsigned int address)
376{
377 struct sunxi_de_fe_reg * const de_fe =
378 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
379
380 setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS);
381 writel(CONFIG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr);
382 writel(mode->xres * 4, &de_fe->ch0_stride);
383 writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt);
384 writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt);
385
386 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
387 &de_fe->ch0_insize);
388 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
389 &de_fe->ch0_outsize);
390 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact);
391 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact);
392
393 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
394 &de_fe->ch1_insize);
395 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
396 &de_fe->ch1_outsize);
397 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact);
398 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact);
399
400 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY);
401}
402
403static void sunxi_frontend_enable(void)
404{
405 struct sunxi_de_fe_reg * const de_fe =
406 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
407
408 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START);
409}
410#else
411static void sunxi_frontend_init(void) {}
412static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
413 unsigned int address) {}
414static void sunxi_frontend_enable(void) {}
415#endif
416
Hans de Goedec06e00e2015-08-03 19:20:26 +0200417static bool sunxi_is_composite(void)
418{
419 switch (sunxi_display.monitor) {
420 case sunxi_monitor_none:
421 case sunxi_monitor_dvi:
422 case sunxi_monitor_hdmi:
423 case sunxi_monitor_lcd:
424 case sunxi_monitor_vga:
425 return false;
426 case sunxi_monitor_composite_pal:
427 case sunxi_monitor_composite_ntsc:
428 case sunxi_monitor_composite_pal_m:
429 case sunxi_monitor_composite_pal_nc:
430 return true;
431 }
432
433 return false; /* Never reached */
434}
435
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200436/*
437 * This is the entity that mixes and matches the different layers and inputs.
438 * Allwinner calls it the back-end, but i like composer better.
439 */
440static void sunxi_composer_init(void)
441{
442 struct sunxi_ccm_reg * const ccm =
443 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
444 struct sunxi_de_be_reg * const de_be =
445 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
446 int i;
447
Hans de Goedec3cc4262015-01-19 08:44:07 +0100448 sunxi_frontend_init();
449
Hans de Goedef07872b2015-04-06 20:33:34 +0200450#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100451 /* Reset off */
452 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0);
453#endif
454
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200455 /* Clocks on */
456 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100457#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200458 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100459#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200460 clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000);
461
462 /* Engine bug, clear registers after reset */
463 for (i = 0x0800; i < 0x1000; i += 4)
464 writel(0, SUNXI_DE_BE0_BASE + i);
465
466 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE);
467}
468
Priit Laes82482d62018-10-23 20:20:31 +0300469static const u32 sunxi_rgb2yuv_coef[12] = {
Hans de Goedec06e00e2015-08-03 19:20:26 +0200470 0x00000107, 0x00000204, 0x00000064, 0x00000108,
471 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,
472 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
473};
474
Hans de Goedeccb0ed52014-12-19 13:46:33 +0100475static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200476 unsigned int address)
477{
478 struct sunxi_de_be_reg * const de_be =
479 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
Hans de Goedec06e00e2015-08-03 19:20:26 +0200480 int i;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200481
Hans de Goedec3cc4262015-01-19 08:44:07 +0100482 sunxi_frontend_mode_set(mode, address);
483
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200484 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
485 &de_be->disp_size);
486 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
487 &de_be->layer0_size);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100488#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200489 writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride);
490 writel(address << 3, &de_be->layer0_addr_low32b);
491 writel(address >> 29, &de_be->layer0_addr_high4b);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100492#else
493 writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl);
494#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200495 writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl);
496
497 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE);
Hans de Goede1977bbb2015-08-02 16:49:29 +0200498 if (mode->vmode == FB_VMODE_INTERLACED)
499 setbits_le32(&de_be->mode,
Hans de Goede8a195ca2015-08-06 12:08:33 +0200500#ifndef CONFIG_MACH_SUN5I
Hans de Goede1977bbb2015-08-02 16:49:29 +0200501 SUNXI_DE_BE_MODE_DEFLICKER_ENABLE |
Hans de Goede8a195ca2015-08-06 12:08:33 +0200502#endif
Hans de Goede1977bbb2015-08-02 16:49:29 +0200503 SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200504
505 if (sunxi_is_composite()) {
506 writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
507 &de_be->output_color_ctrl);
508 for (i = 0; i < 12; i++)
509 writel(sunxi_rgb2yuv_coef[i],
510 &de_be->output_color_coef[i]);
511 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200512}
513
Hans de Goede4125f922014-12-21 14:49:34 +0100514static void sunxi_composer_enable(void)
515{
516 struct sunxi_de_be_reg * const de_be =
517 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
518
Hans de Goedec3cc4262015-01-19 08:44:07 +0100519 sunxi_frontend_enable();
520
Hans de Goede4125f922014-12-21 14:49:34 +0100521 setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS);
522 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
523}
524
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200525static void sunxi_lcdc_init(void)
526{
527 struct sunxi_ccm_reg * const ccm =
528 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
529 struct sunxi_lcdc_reg * const lcdc =
530 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
531
532 /* Reset off */
Hans de Goedef07872b2015-04-06 20:33:34 +0200533#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100534 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
535#else
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200536 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST);
Hans de Goedef651e0a2014-11-14 17:42:14 +0100537#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200538
539 /* Clock on */
540 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100541#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Hans de Goede5f67b862015-05-14 18:52:54 +0200542#ifdef CONFIG_SUNXI_GEN_SUN6I
543 setbits_le32(&ccm->ahb_reset2_cfg, 1 << AHB_RESET_OFFSET_LVDS);
544#else
Hans de Goede797a0f52015-01-01 22:04:34 +0100545 setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
546#endif
Hans de Goede5f67b862015-05-14 18:52:54 +0200547#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200548
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200549 lcdc_init(lcdc);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200550}
551
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100552static void sunxi_lcdc_panel_enable(void)
553{
Hans de Goedece9e3322015-02-16 17:26:41 +0100554 int pin, reset_pin;
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100555
556 /*
557 * Start with backlight disabled to avoid the screen flashing to
558 * white while the lcd inits.
559 */
560 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede7783ab22015-04-22 17:45:59 +0200561 if (pin >= 0) {
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100562 gpio_request(pin, "lcd_backlight_enable");
563 gpio_direction_output(pin, 0);
564 }
565
566 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede7783ab22015-04-22 17:45:59 +0200567 if (pin >= 0) {
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100568 gpio_request(pin, "lcd_backlight_pwm");
Hans de Goede2d5d3022015-01-22 21:02:42 +0100569 gpio_direction_output(pin, PWM_OFF);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100570 }
571
Hans de Goedece9e3322015-02-16 17:26:41 +0100572 reset_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_RESET);
Hans de Goede7783ab22015-04-22 17:45:59 +0200573 if (reset_pin >= 0) {
Hans de Goedece9e3322015-02-16 17:26:41 +0100574 gpio_request(reset_pin, "lcd_reset");
575 gpio_direction_output(reset_pin, 0); /* Assert reset */
576 }
577
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100578 /* Give the backlight some time to turn off and power up the panel. */
579 mdelay(40);
580 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
Hans de Goede7783ab22015-04-22 17:45:59 +0200581 if (pin >= 0) {
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100582 gpio_request(pin, "lcd_power");
583 gpio_direction_output(pin, 1);
584 }
Hans de Goedece9e3322015-02-16 17:26:41 +0100585
Hans de Goede7783ab22015-04-22 17:45:59 +0200586 if (reset_pin >= 0)
Hans de Goedece9e3322015-02-16 17:26:41 +0100587 gpio_direction_output(reset_pin, 1); /* De-assert reset */
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100588}
589
590static void sunxi_lcdc_backlight_enable(void)
591{
592 int pin;
593
594 /*
595 * We want to have scanned out at least one frame before enabling the
596 * backlight to avoid the screen flashing to white when we enable it.
597 */
598 mdelay(40);
599
600 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede7783ab22015-04-22 17:45:59 +0200601 if (pin >= 0)
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100602 gpio_direction_output(pin, 1);
603
604 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede663ae652016-08-19 15:25:41 +0200605#ifdef SUNXI_PWM_PIN0
606 if (pin == SUNXI_PWM_PIN0) {
607 writel(SUNXI_PWM_CTRL_POLARITY0(PWM_ON) |
608 SUNXI_PWM_CTRL_ENABLE0 |
609 SUNXI_PWM_CTRL_PRESCALE0(0xf), SUNXI_PWM_CTRL_REG);
610 writel(SUNXI_PWM_PERIOD_80PCT, SUNXI_PWM_CH0_PERIOD);
611 sunxi_gpio_set_cfgpin(pin, SUNXI_PWM_MUX);
612 return;
613 }
614#endif
Hans de Goede7783ab22015-04-22 17:45:59 +0200615 if (pin >= 0)
Hans de Goede2d5d3022015-01-22 21:02:42 +0100616 gpio_direction_output(pin, PWM_ON);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100617}
618
Hans de Goede18366f72015-01-25 15:33:07 +0100619static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
620 bool for_ext_vga_dac)
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100621{
622 struct sunxi_lcdc_reg * const lcdc =
623 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Vasily Khoruzhickf079f932017-10-26 21:51:51 -0700624 struct sunxi_ccm_reg * const ccm =
625 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200626 int clk_div, clk_double, pin;
Jernej Skrabecccfbe5f2017-03-27 19:22:30 +0200627 struct display_timing timing;
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100628
Lawrence Yuf721f962016-03-04 09:08:56 -0800629#if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS
630 for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) {
631#else
Hans de Goede91f1b822015-08-08 16:13:53 +0200632 for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) {
Lawrence Yuf721f962016-03-04 09:08:56 -0800633#endif
Hans de Goede797a0f52015-01-01 22:04:34 +0100634#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Paul Kocialkowskiae358a42015-03-22 18:12:22 +0100635 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100636#endif
637#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Paul Kocialkowskiae358a42015-03-22 18:12:22 +0100638 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LVDS0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100639#endif
Hans de Goede91f1b822015-08-08 16:13:53 +0200640#ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804
641 sunxi_gpio_set_drv(pin, 3);
642#endif
643 }
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100644
Vasily Khoruzhickf079f932017-10-26 21:51:51 -0700645 lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double,
646 sunxi_is_composite());
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100647
Giulio Benetti953f5ed2020-04-08 17:10:12 +0200648 video_ctfb_mode_to_display_timing(mode, &timing);
Jernej Skrabecccfbe5f2017-03-27 19:22:30 +0200649 lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200650 sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100651}
652
Hans de Goedec06e00e2015-08-03 19:20:26 +0200653#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goede4125f922014-12-21 14:49:34 +0100654static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
Hans de Goedec3d15042014-12-27 15:19:23 +0100655 int *clk_div, int *clk_double,
656 bool use_portd_hvsync)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200657{
658 struct sunxi_lcdc_reg * const lcdc =
659 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Vasily Khoruzhickf079f932017-10-26 21:51:51 -0700660 struct sunxi_ccm_reg * const ccm =
661 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Jernej Skrabecccfbe5f2017-03-27 19:22:30 +0200662 struct display_timing timing;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200663
Giulio Benetti953f5ed2020-04-08 17:10:12 +0200664 video_ctfb_mode_to_display_timing(mode, &timing);
Jernej Skrabecccfbe5f2017-03-27 19:22:30 +0200665 lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200666 sunxi_is_composite());
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200667
Hans de Goedec3d15042014-12-27 15:19:23 +0100668 if (use_portd_hvsync) {
Paul Kocialkowskiae358a42015-03-22 18:12:22 +0100669 sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
670 sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0);
Hans de Goedec3d15042014-12-27 15:19:23 +0100671 }
Hans de Goede8a195ca2015-08-06 12:08:33 +0200672
Vasily Khoruzhickf079f932017-10-26 21:51:51 -0700673 lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double,
674 sunxi_is_composite());
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200675}
Hans de Goedec06e00e2015-08-03 19:20:26 +0200676#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
Hans de Goede260f5202014-12-25 13:58:06 +0100677
678#ifdef CONFIG_VIDEO_HDMI
Hans de Goedef651e0a2014-11-14 17:42:14 +0100679
Hans de Goedea2017e82014-12-20 13:38:06 +0100680static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
681{
682 struct sunxi_hdmi_reg * const hdmi =
683 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
684 u8 checksum = 0;
685 u8 avi_info_frame[17] = {
686 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00,
687 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
688 0x00
689 };
690 u8 vendor_info_frame[19] = {
691 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40,
692 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
693 0x00, 0x00, 0x00
694 };
695 int i;
696
697 if (mode->pixclock_khz <= 27000)
698 avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */
699 else
700 avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */
701
702 if (mode->xres * 100 / mode->yres < 156)
703 avi_info_frame[5] |= 0x18; /* 4 : 3 */
704 else
705 avi_info_frame[5] |= 0x28; /* 16 : 9 */
706
707 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
708 checksum += avi_info_frame[i];
709
710 avi_info_frame[3] = 0x100 - checksum;
711
712 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
713 writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]);
714
715 writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0);
716 writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1);
717
718 for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++)
719 writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]);
720
721 writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0);
722 writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1);
723
724 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI);
725}
726
Hans de Goedeccb0ed52014-12-19 13:46:33 +0100727static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
Hans de Goedea0b1b732014-12-21 14:37:45 +0100728 int clk_div, int clk_double)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200729{
730 struct sunxi_hdmi_reg * const hdmi =
731 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
732 int x, y;
733
734 /* Write clear interrupt status bits */
735 writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq);
736
Hans de Goedea0b1b732014-12-21 14:37:45 +0100737 if (sunxi_display.monitor == sunxi_monitor_hdmi)
Hans de Goedea2017e82014-12-20 13:38:06 +0100738 sunxi_hdmi_setup_info_frames(mode);
739
Hans de Goede95576692014-12-20 13:51:16 +0100740 /* Set input sync enable */
741 writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown);
742
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200743 /* Init various registers, select pll3 as clock source */
744 writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity);
745 writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0);
746 writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1);
747 writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl);
748 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
749
750 /* Setup clk div and doubler */
751 clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK,
752 SUNXI_HDMI_PLL_CTRL_DIV(clk_div));
753 if (!clk_double)
754 setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE);
755
756 /* Setup timing registers */
757 writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres),
758 &hdmi->video_size);
759
760 x = mode->hsync_len + mode->left_margin;
761 y = mode->vsync_len + mode->upper_margin;
762 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp);
763
764 x = mode->right_margin;
765 y = mode->lower_margin;
766 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp);
767
768 x = mode->hsync_len;
769 y = mode->vsync_len;
770 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw);
771
772 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
773 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR);
774
775 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
776 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER);
777}
778
Hans de Goede4125f922014-12-21 14:49:34 +0100779static void sunxi_hdmi_enable(void)
780{
781 struct sunxi_hdmi_reg * const hdmi =
782 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
783
784 udelay(100);
785 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE);
786}
787
Hans de Goedee9544592014-12-23 23:04:35 +0100788#endif /* CONFIG_VIDEO_HDMI */
789
Hans de Goedec06e00e2015-08-03 19:20:26 +0200790#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goede260f5202014-12-25 13:58:06 +0100791
Hans de Goedec06e00e2015-08-03 19:20:26 +0200792static void sunxi_tvencoder_mode_set(void)
Hans de Goede260f5202014-12-25 13:58:06 +0100793{
794 struct sunxi_ccm_reg * const ccm =
795 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
796 struct sunxi_tve_reg * const tve =
797 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
798
Hans de Goede8a195ca2015-08-06 12:08:33 +0200799 /* Reset off */
800 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_TVE_RST);
Hans de Goede260f5202014-12-25 13:58:06 +0100801 /* Clock on */
802 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
803
Hans de Goedec06e00e2015-08-03 19:20:26 +0200804 switch (sunxi_display.monitor) {
805 case sunxi_monitor_vga:
Jernej Skrabec8531d082017-05-10 18:46:28 +0200806 tvencoder_mode_set(tve, tve_mode_vga);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200807 break;
808 case sunxi_monitor_composite_pal_nc:
Jernej Skrabec8531d082017-05-10 18:46:28 +0200809 tvencoder_mode_set(tve, tve_mode_composite_pal_nc);
810 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +0200811 case sunxi_monitor_composite_pal:
Jernej Skrabec8531d082017-05-10 18:46:28 +0200812 tvencoder_mode_set(tve, tve_mode_composite_pal);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200813 break;
814 case sunxi_monitor_composite_pal_m:
Jernej Skrabec8531d082017-05-10 18:46:28 +0200815 tvencoder_mode_set(tve, tve_mode_composite_pal_m);
816 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +0200817 case sunxi_monitor_composite_ntsc:
Jernej Skrabec8531d082017-05-10 18:46:28 +0200818 tvencoder_mode_set(tve, tve_mode_composite_ntsc);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200819 break;
820 case sunxi_monitor_none:
821 case sunxi_monitor_dvi:
822 case sunxi_monitor_hdmi:
823 case sunxi_monitor_lcd:
824 break;
825 }
Hans de Goede260f5202014-12-25 13:58:06 +0100826}
827
Hans de Goedec06e00e2015-08-03 19:20:26 +0200828#endif /* CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE */
Hans de Goede260f5202014-12-25 13:58:06 +0100829
Hans de Goede115e4b42014-12-23 18:39:52 +0100830static void sunxi_drc_init(void)
831{
Hans de Goedef07872b2015-04-06 20:33:34 +0200832#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede115e4b42014-12-23 18:39:52 +0100833 struct sunxi_ccm_reg * const ccm =
834 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
835
836 /* On sun6i the drc must be clocked even when in pass-through mode */
Vishnu Patekar3702f142015-03-01 23:47:48 +0530837#ifdef CONFIG_MACH_SUN8I_A33
838 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_SAT);
839#endif
Hans de Goede115e4b42014-12-23 18:39:52 +0100840 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0);
841 clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000);
842#endif
843}
844
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +0800845#ifdef CONFIG_VIDEO_VGA_VIA_LCD
846static void sunxi_vga_external_dac_enable(void)
847{
848 int pin;
849
850 pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
Hans de Goede7783ab22015-04-22 17:45:59 +0200851 if (pin >= 0) {
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +0800852 gpio_request(pin, "vga_enable");
853 gpio_direction_output(pin, 1);
854 }
855}
856#endif /* CONFIG_VIDEO_VGA_VIA_LCD */
857
Siarhei Siamashkac02f0522015-01-19 05:23:33 +0200858#ifdef CONFIG_VIDEO_LCD_SSD2828
859static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
860{
861 struct ssd2828_config cfg = {
862 .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
863 .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
864 .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
865 .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
866 .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
867 .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
868 .ssd2828_color_depth = 24,
869#ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
870 .mipi_dsi_number_of_data_lanes = 4,
871 .mipi_dsi_bitrate_per_data_lane_mbps = 513,
872 .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
873 .mipi_dsi_delay_after_set_display_on_ms = 200
874#else
875#error MIPI LCD panel needs configuration parameters
876#endif
877 };
878
879 if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
880 printf("SSD2828: SPI pins are not properly configured\n");
881 return 1;
882 }
883 if (cfg.reset_pin == -1) {
884 printf("SSD2828: Reset pin is not properly configured\n");
885 return 1;
886 }
887
888 return ssd2828_init(&cfg, mode);
889}
890#endif /* CONFIG_VIDEO_LCD_SSD2828 */
891
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200892static void sunxi_engines_init(void)
893{
894 sunxi_composer_init();
895 sunxi_lcdc_init();
Hans de Goedef651e0a2014-11-14 17:42:14 +0100896 sunxi_drc_init();
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200897}
898
Hans de Goedea0b1b732014-12-21 14:37:45 +0100899static void sunxi_mode_set(const struct ctfb_res_modes *mode,
Hans de Goedea2017e82014-12-20 13:38:06 +0100900 unsigned int address)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200901{
Hans de Goede260f5202014-12-25 13:58:06 +0100902 int __maybe_unused clk_div, clk_double;
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200903 struct sunxi_lcdc_reg * const lcdc =
904 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Jernej Skrabec8531d082017-05-10 18:46:28 +0200905 struct sunxi_tve_reg * __maybe_unused const tve =
906 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
Hans de Goede260f5202014-12-25 13:58:06 +0100907
Hans de Goede4125f922014-12-21 14:49:34 +0100908 switch (sunxi_display.monitor) {
909 case sunxi_monitor_none:
910 break;
911 case sunxi_monitor_dvi:
Hans de Goede260f5202014-12-25 13:58:06 +0100912 case sunxi_monitor_hdmi:
Hans de Goedee9544592014-12-23 23:04:35 +0100913#ifdef CONFIG_VIDEO_HDMI
Hans de Goede4125f922014-12-21 14:49:34 +0100914 sunxi_composer_mode_set(mode, address);
Hans de Goedec3d15042014-12-27 15:19:23 +0100915 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
Hans de Goede4125f922014-12-21 14:49:34 +0100916 sunxi_hdmi_mode_set(mode, clk_div, clk_double);
917 sunxi_composer_enable();
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200918 lcdc_enable(lcdc, sunxi_display.depth);
Hans de Goede4125f922014-12-21 14:49:34 +0100919 sunxi_hdmi_enable();
Hans de Goedee9544592014-12-23 23:04:35 +0100920#endif
Hans de Goede4125f922014-12-21 14:49:34 +0100921 break;
922 case sunxi_monitor_lcd:
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100923 sunxi_lcdc_panel_enable();
Hans de Goede91f1b822015-08-08 16:13:53 +0200924 if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804)) {
925 /*
926 * The anx9804 needs 1.8V from eldo3, we do this here
Hans de Goeded9ee84b2015-10-03 15:18:33 +0200927 * and not via CONFIG_AXP_ELDO3_VOLT from board_init()
Hans de Goede91f1b822015-08-08 16:13:53 +0200928 * to avoid turning this on when using hdmi output.
929 */
Hans de Goeded9ee84b2015-10-03 15:18:33 +0200930 axp_set_eldo(3, 1800);
Hans de Goede91f1b822015-08-08 16:13:53 +0200931 anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4,
932 ANX9804_DATA_RATE_1620M,
933 sunxi_display.depth);
934 }
Hans de Goede743fb9552015-01-20 09:23:36 +0100935 if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
936 mdelay(50); /* Wait for lcd controller power on */
937 hitachi_tx18d42vm_init();
938 }
Hans de Goede613dade2015-02-16 17:49:47 +0100939 if (IS_ENABLED(CONFIG_VIDEO_LCD_TL059WV5C0)) {
940 unsigned int orig_i2c_bus = i2c_get_bus_num();
941 i2c_set_bus_num(CONFIG_VIDEO_LCD_I2C_BUS);
942 i2c_reg_write(0x5c, 0x04, 0x42); /* Turn on the LCD */
943 i2c_set_bus_num(orig_i2c_bus);
944 }
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100945 sunxi_composer_mode_set(mode, address);
Hans de Goede18366f72015-01-25 15:33:07 +0100946 sunxi_lcdc_tcon0_mode_set(mode, false);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100947 sunxi_composer_enable();
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200948 lcdc_enable(lcdc, sunxi_display.depth);
Siarhei Siamashkac02f0522015-01-19 05:23:33 +0200949#ifdef CONFIG_VIDEO_LCD_SSD2828
950 sunxi_ssd2828_init(mode);
951#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100952 sunxi_lcdc_backlight_enable();
Hans de Goede4125f922014-12-21 14:49:34 +0100953 break;
954 case sunxi_monitor_vga:
Hans de Goede260f5202014-12-25 13:58:06 +0100955#ifdef CONFIG_VIDEO_VGA
956 sunxi_composer_mode_set(mode, address);
957 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200958 sunxi_tvencoder_mode_set();
Hans de Goede260f5202014-12-25 13:58:06 +0100959 sunxi_composer_enable();
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200960 lcdc_enable(lcdc, sunxi_display.depth);
Jernej Skrabec8531d082017-05-10 18:46:28 +0200961 tvencoder_enable(tve);
Hans de Goede260f5202014-12-25 13:58:06 +0100962#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goedeac1633c2014-12-24 12:17:07 +0100963 sunxi_composer_mode_set(mode, address);
Hans de Goede18366f72015-01-25 15:33:07 +0100964 sunxi_lcdc_tcon0_mode_set(mode, true);
Hans de Goedeac1633c2014-12-24 12:17:07 +0100965 sunxi_composer_enable();
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200966 lcdc_enable(lcdc, sunxi_display.depth);
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +0800967 sunxi_vga_external_dac_enable();
Hans de Goedeac1633c2014-12-24 12:17:07 +0100968#endif
Hans de Goede4125f922014-12-21 14:49:34 +0100969 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +0200970 case sunxi_monitor_composite_pal:
971 case sunxi_monitor_composite_ntsc:
972 case sunxi_monitor_composite_pal_m:
973 case sunxi_monitor_composite_pal_nc:
974#ifdef CONFIG_VIDEO_COMPOSITE
975 sunxi_composer_mode_set(mode, address);
976 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
977 sunxi_tvencoder_mode_set();
978 sunxi_composer_enable();
Jernej Skrabec2e0a1f32017-03-27 19:22:29 +0200979 lcdc_enable(lcdc, sunxi_display.depth);
Jernej Skrabec8531d082017-05-10 18:46:28 +0200980 tvencoder_enable(tve);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200981#endif
982 break;
Hans de Goede4125f922014-12-21 14:49:34 +0100983 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200984}
985
Hans de Goedea0b1b732014-12-21 14:37:45 +0100986static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
987{
988 switch (monitor) {
Hans de Goedec06e00e2015-08-03 19:20:26 +0200989 case sunxi_monitor_dvi: return "dvi";
990 case sunxi_monitor_hdmi: return "hdmi";
991 case sunxi_monitor_lcd: return "lcd";
992 case sunxi_monitor_vga: return "vga";
993 case sunxi_monitor_composite_pal: return "composite-pal";
994 case sunxi_monitor_composite_ntsc: return "composite-ntsc";
995 case sunxi_monitor_composite_pal_m: return "composite-pal-m";
996 case sunxi_monitor_composite_pal_nc: return "composite-pal-nc";
Bin Meng12de4aa2020-04-06 06:06:58 -0700997 case sunxi_monitor_none: /* fall through */
998 default: return "none";
Hans de Goedea0b1b732014-12-21 14:37:45 +0100999 }
Hans de Goedea0b1b732014-12-21 14:37:45 +01001000}
1001
Hans de Goede6c912862015-02-02 17:13:29 +01001002ulong board_get_usable_ram_top(ulong total_size)
1003{
1004 return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
1005}
1006
Hans de Goedea2bba492015-08-03 23:01:38 +02001007static bool sunxi_has_hdmi(void)
1008{
1009#ifdef CONFIG_VIDEO_HDMI
1010 return true;
1011#else
1012 return false;
1013#endif
1014}
1015
1016static bool sunxi_has_lcd(void)
1017{
1018 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
1019
1020 return lcd_mode[0] != 0;
1021}
1022
1023static bool sunxi_has_vga(void)
1024{
1025#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_VGA_VIA_LCD
1026 return true;
1027#else
1028 return false;
1029#endif
1030}
1031
Hans de Goedec06e00e2015-08-03 19:20:26 +02001032static bool sunxi_has_composite(void)
1033{
1034#ifdef CONFIG_VIDEO_COMPOSITE
1035 return true;
1036#else
1037 return false;
1038#endif
1039}
1040
Hans de Goedea2bba492015-08-03 23:01:38 +02001041static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
1042{
1043 if (allow_hdmi && sunxi_has_hdmi())
1044 return sunxi_monitor_dvi;
1045 else if (sunxi_has_lcd())
1046 return sunxi_monitor_lcd;
1047 else if (sunxi_has_vga())
1048 return sunxi_monitor_vga;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001049 else if (sunxi_has_composite())
1050 return sunxi_monitor_composite_pal;
Hans de Goedea2bba492015-08-03 23:01:38 +02001051 else
1052 return sunxi_monitor_none;
1053}
1054
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001055void *video_hw_init(void)
1056{
1057 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001058 const struct ctfb_res_modes *mode;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001059 struct ctfb_res_modes custom;
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001060 const char *options;
Hans de Goedee9544592014-12-23 23:04:35 +01001061#ifdef CONFIG_VIDEO_HDMI
Priit Laesa9d6db72018-12-19 15:06:09 +02001062 int hpd, hpd_delay, edid;
1063 bool hdmi_present;
Hans de Goedee9544592014-12-23 23:04:35 +01001064#endif
Hans de Goeded955f442015-08-05 00:06:47 +02001065 int i, overscan_offset, overscan_x, overscan_y;
1066 unsigned int fb_dma_addr;
Hans de Goedea0b1b732014-12-21 14:37:45 +01001067 char mon[16];
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001068 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001069
1070 memset(&sunxi_display, 0, sizeof(struct sunxi_display));
1071
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001072 video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
1073 &sunxi_display.depth, &options);
Hans de Goedee9544592014-12-23 23:04:35 +01001074#ifdef CONFIG_VIDEO_HDMI
Hans de Goede695bda42014-12-19 15:13:57 +01001075 hpd = video_get_option_int(options, "hpd", 1);
Hans de Goede91593712014-12-28 09:13:21 +01001076 hpd_delay = video_get_option_int(options, "hpd_delay", 500);
Hans de Goedea5aa95f2014-12-19 16:05:12 +01001077 edid = video_get_option_int(options, "edid", 1);
Hans de Goedee9544592014-12-23 23:04:35 +01001078#endif
Hans de Goeded955f442015-08-05 00:06:47 +02001079 overscan_x = video_get_option_int(options, "overscan_x", -1);
1080 overscan_y = video_get_option_int(options, "overscan_y", -1);
Hans de Goedea2bba492015-08-03 23:01:38 +02001081 sunxi_display.monitor = sunxi_get_default_mon(true);
Hans de Goedea0b1b732014-12-21 14:37:45 +01001082 video_get_option_string(options, "monitor", mon, sizeof(mon),
1083 sunxi_get_mon_desc(sunxi_display.monitor));
1084 for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
1085 if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
1086 sunxi_display.monitor = i;
1087 break;
1088 }
1089 }
1090 if (i > SUNXI_MONITOR_LAST)
1091 printf("Unknown monitor: '%s', falling back to '%s'\n",
1092 mon, sunxi_get_mon_desc(sunxi_display.monitor));
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001093
Hans de Goede7977ec22014-12-25 13:52:04 +01001094#ifdef CONFIG_VIDEO_HDMI
1095 /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
1096 if (sunxi_display.monitor == sunxi_monitor_dvi ||
1097 sunxi_display.monitor == sunxi_monitor_hdmi) {
1098 /* Always call hdp_detect, as it also enables clocks, etc. */
Priit Laesa9d6db72018-12-19 15:06:09 +02001099 hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
1100 if (hdmi_present && edid) {
Hans de Goede7977ec22014-12-25 13:52:04 +01001101 printf("HDMI connected: ");
Priit Laesa9d6db72018-12-19 15:06:09 +02001102 if (sunxi_hdmi_edid_get_mode(&custom, true) == 0)
Hans de Goede7977ec22014-12-25 13:52:04 +01001103 mode = &custom;
Priit Laesa9d6db72018-12-19 15:06:09 +02001104 else
1105 hdmi_present = false;
1106 }
1107 /* Fall back to EDID in case HPD failed */
1108 if (edid && !hdmi_present) {
1109 if (sunxi_hdmi_edid_get_mode(&custom, false) == 0) {
1110 mode = &custom;
1111 hdmi_present = true;
1112 }
1113 }
1114 /* Shut down when display was not found */
1115 if ((hpd || edid) && !hdmi_present) {
Hans de Goede7977ec22014-12-25 13:52:04 +01001116 sunxi_hdmi_shutdown();
Hans de Goedea2bba492015-08-03 23:01:38 +02001117 sunxi_display.monitor = sunxi_get_default_mon(false);
Hans de Goede7977ec22014-12-25 13:52:04 +01001118 } /* else continue with hdmi/dvi without a cable connected */
1119 }
1120#endif
1121
Hans de Goede4125f922014-12-21 14:49:34 +01001122 switch (sunxi_display.monitor) {
1123 case sunxi_monitor_none:
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001124 return NULL;
Hans de Goede4125f922014-12-21 14:49:34 +01001125 case sunxi_monitor_dvi:
1126 case sunxi_monitor_hdmi:
Hans de Goedea2bba492015-08-03 23:01:38 +02001127 if (!sunxi_has_hdmi()) {
1128 printf("HDMI/DVI not supported on this board\n");
1129 sunxi_display.monitor = sunxi_monitor_none;
1130 return NULL;
1131 }
Hans de Goede7977ec22014-12-25 13:52:04 +01001132 break;
Hans de Goede4125f922014-12-21 14:49:34 +01001133 case sunxi_monitor_lcd:
Hans de Goedea2bba492015-08-03 23:01:38 +02001134 if (!sunxi_has_lcd()) {
1135 printf("LCD not supported on this board\n");
1136 sunxi_display.monitor = sunxi_monitor_none;
1137 return NULL;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001138 }
Hans de Goedea2bba492015-08-03 23:01:38 +02001139 sunxi_display.depth = video_get_params(&custom, lcd_mode);
1140 mode = &custom;
1141 break;
Hans de Goede4125f922014-12-21 14:49:34 +01001142 case sunxi_monitor_vga:
Hans de Goedea2bba492015-08-03 23:01:38 +02001143 if (!sunxi_has_vga()) {
1144 printf("VGA not supported on this board\n");
1145 sunxi_display.monitor = sunxi_monitor_none;
1146 return NULL;
1147 }
Hans de Goedeac1633c2014-12-24 12:17:07 +01001148 sunxi_display.depth = 18;
1149 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001150 case sunxi_monitor_composite_pal:
1151 case sunxi_monitor_composite_ntsc:
1152 case sunxi_monitor_composite_pal_m:
1153 case sunxi_monitor_composite_pal_nc:
1154 if (!sunxi_has_composite()) {
1155 printf("Composite video not supported on this board\n");
1156 sunxi_display.monitor = sunxi_monitor_none;
1157 return NULL;
1158 }
1159 if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
1160 sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
1161 mode = &composite_video_modes[0];
1162 else
1163 mode = &composite_video_modes[1];
1164 sunxi_display.depth = 24;
1165 break;
Hans de Goedea5aa95f2014-12-19 16:05:12 +01001166 }
1167
Hans de Goeded955f442015-08-05 00:06:47 +02001168 /* Yes these defaults are quite high, overscan on composite sucks... */
1169 if (overscan_x == -1)
1170 overscan_x = sunxi_is_composite() ? 32 : 0;
1171 if (overscan_y == -1)
1172 overscan_y = sunxi_is_composite() ? 20 : 0;
1173
Hans de Goede18799252015-02-02 18:00:53 +01001174 sunxi_display.fb_size =
1175 (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
Hans de Goeded955f442015-08-05 00:06:47 +02001176 overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
1177 /* We want to keep the fb_base for simplefb page aligned, where as
1178 * the sunxi dma engines will happily accept an unaligned address. */
1179 if (overscan_offset)
1180 sunxi_display.fb_size += 0x1000;
1181
Hans de Goede18799252015-02-02 18:00:53 +01001182 if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
1183 printf("Error need %dkB for fb, but only %dkB is reserved\n",
1184 sunxi_display.fb_size >> 10,
1185 CONFIG_SUNXI_MAX_FB_SIZE >> 10);
1186 return NULL;
1187 }
1188
Hans de Goeded955f442015-08-05 00:06:47 +02001189 printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
1190 mode->xres, mode->yres,
Hans de Goede1977bbb2015-08-02 16:49:29 +02001191 (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
Hans de Goeded955f442015-08-05 00:06:47 +02001192 sunxi_get_mon_desc(sunxi_display.monitor),
1193 overscan_x, overscan_y);
Hans de Goede1977bbb2015-08-02 16:49:29 +02001194
Hans de Goede18799252015-02-02 18:00:53 +01001195 gd->fb_base = gd->bd->bi_dram[0].start +
1196 gd->bd->bi_dram[0].size - sunxi_display.fb_size;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001197 sunxi_engines_init();
Hans de Goeded955f442015-08-05 00:06:47 +02001198
Heinrich Schuchardt084d3d92018-03-03 10:30:17 +01001199#ifdef CONFIG_EFI_LOADER
Michael Walle282d3862020-05-17 12:29:19 +02001200 efi_add_memory_map(gd->fb_base, sunxi_display.fb_size,
1201 EFI_RESERVED_MEMORY_TYPE);
Heinrich Schuchardt084d3d92018-03-03 10:30:17 +01001202#endif
1203
Hans de Goeded955f442015-08-05 00:06:47 +02001204 fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
1205 sunxi_display.fb_addr = gd->fb_base;
1206 if (overscan_offset) {
1207 fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
1208 sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
1209 memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
1210 flush_cache(gd->fb_base, sunxi_display.fb_size);
1211 }
1212 sunxi_mode_set(mode, fb_dma_addr);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001213
1214 /*
1215 * These are the only members of this structure that are used. All the
Hans de Goeded955f442015-08-05 00:06:47 +02001216 * others are driver specific. The pitch is stored in plnSizeX.
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001217 */
Hans de Goeded955f442015-08-05 00:06:47 +02001218 graphic_device->frameAdrs = sunxi_display.fb_addr;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001219 graphic_device->gdfIndex = GDF_32BIT_X888RGB;
1220 graphic_device->gdfBytesPP = 4;
Hans de Goeded955f442015-08-05 00:06:47 +02001221 graphic_device->winSizeX = mode->xres - 2 * overscan_x;
1222 graphic_device->winSizeY = mode->yres - 2 * overscan_y;
1223 graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001224
1225 return graphic_device;
1226}
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001227
1228/*
1229 * Simplefb support.
1230 */
1231#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
1232int sunxi_simplefb_setup(void *blob)
1233{
1234 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
1235 int offset, ret;
Hans de Goede6c912862015-02-02 17:13:29 +01001236 u64 start, size;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001237 const char *pipeline = NULL;
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001238
Hans de Goedec3cc4262015-01-19 08:44:07 +01001239#ifdef CONFIG_MACH_SUN4I
1240#define PIPELINE_PREFIX "de_fe0-"
1241#else
1242#define PIPELINE_PREFIX
1243#endif
1244
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001245 switch (sunxi_display.monitor) {
1246 case sunxi_monitor_none:
1247 return 0;
1248 case sunxi_monitor_dvi:
1249 case sunxi_monitor_hdmi:
Hans de Goedec3cc4262015-01-19 08:44:07 +01001250 pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi";
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001251 break;
1252 case sunxi_monitor_lcd:
Hans de Goedec3cc4262015-01-19 08:44:07 +01001253 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001254 break;
1255 case sunxi_monitor_vga:
Hans de Goede260f5202014-12-25 13:58:06 +01001256#ifdef CONFIG_VIDEO_VGA
Hans de Goedec3cc4262015-01-19 08:44:07 +01001257 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
Hans de Goede260f5202014-12-25 13:58:06 +01001258#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goedec3cc4262015-01-19 08:44:07 +01001259 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede260f5202014-12-25 13:58:06 +01001260#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001261 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001262 case sunxi_monitor_composite_pal:
1263 case sunxi_monitor_composite_ntsc:
1264 case sunxi_monitor_composite_pal_m:
1265 case sunxi_monitor_composite_pal_nc:
1266 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
1267 break;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001268 }
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001269
Icenowy Zheng067a6972017-10-26 11:14:45 +08001270 offset = sunxi_simplefb_fdt_match(blob, pipeline);
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001271 if (offset < 0) {
1272 eprintf("Cannot setup simplefb: node not found\n");
1273 return 0; /* Keep older kernels working */
1274 }
1275
Hans de Goede6c912862015-02-02 17:13:29 +01001276 /*
1277 * Do not report the framebuffer as free RAM to the OS, note we cannot
1278 * use fdt_add_mem_rsv() here, because then it is still seen as RAM,
1279 * and e.g. Linux refuses to iomap RAM on ARM, see:
1280 * linux/arch/arm/mm/ioremap.c around line 301.
1281 */
1282 start = gd->bd->bi_dram[0].start;
Hans de Goede18799252015-02-02 18:00:53 +01001283 size = gd->bd->bi_dram[0].size - sunxi_display.fb_size;
Hans de Goede6c912862015-02-02 17:13:29 +01001284 ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
1285 if (ret) {
1286 eprintf("Cannot setup simplefb: Error reserving memory\n");
1287 return ret;
1288 }
1289
Hans de Goeded955f442015-08-05 00:06:47 +02001290 ret = fdt_setup_simplefb_node(blob, offset, sunxi_display.fb_addr,
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001291 graphic_device->winSizeX, graphic_device->winSizeY,
Hans de Goeded955f442015-08-05 00:06:47 +02001292 graphic_device->plnSizeX, "x8r8g8b8");
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001293 if (ret)
1294 eprintf("Cannot setup simplefb: Error setting properties\n");
1295
1296 return ret;
1297}
1298#endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */