blob: 46436b88c57e15c7e715aa9b6d8a5d416610f1fd [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>
Heinrich Schuchardtd06717f2018-03-03 10:30:17 +010010#include <efi_loader.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020011
12#include <asm/arch/clock.h>
13#include <asm/arch/display.h>
Hans de Goede2dae8002014-12-21 16:28:32 +010014#include <asm/arch/gpio.h>
Jernej Skrabec5e023e72017-03-27 19:22:29 +020015#include <asm/arch/lcdc.h>
Hans de Goede421c98d2016-08-19 15:25:41 +020016#include <asm/arch/pwm.h>
Jernej Skrabecc1080622017-05-10 18:46:28 +020017#include <asm/arch/tve.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020018#include <asm/global_data.h>
Hans de Goede2dae8002014-12-21 16:28:32 +010019#include <asm/gpio.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020020#include <asm/io.h>
Hans de Goede6944aff2015-10-03 15:18:33 +020021#include <axp_pmic.h>
Hans de Goede75481602014-12-19 16:05:12 +010022#include <errno.h>
Luc Verhaegen2d7a0842014-08-13 07:55:07 +020023#include <fdtdec.h>
24#include <fdt_support.h>
Hans de Goedeaad2ac22015-02-16 17:49:47 +010025#include <i2c.h>
Hans de Goede58332f82015-08-05 00:06:47 +020026#include <malloc.h>
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020027#include <video_fb.h>
Jernej Skrabec5e023e72017-03-27 19:22:29 +020028#include "../videomodes.h"
29#include "../anx9804.h"
30#include "../hitachi_tx18d42vm_lcd.h"
31#include "../ssd2828.h"
Icenowy Zhenge5f92462017-10-26 11:14:45 +080032#include "simplefb_common.h"
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020033
Hans de Goedea7403ae2015-01-22 21:02:42 +010034#ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW
35#define PWM_ON 0
36#define PWM_OFF 1
37#else
38#define PWM_ON 1
39#define PWM_OFF 0
40#endif
41
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020042DECLARE_GLOBAL_DATA_PTR;
43
Hans de Goede1c092202014-12-21 14:37:45 +010044enum sunxi_monitor {
45 sunxi_monitor_none,
46 sunxi_monitor_dvi,
47 sunxi_monitor_hdmi,
48 sunxi_monitor_lcd,
49 sunxi_monitor_vga,
Hans de Goede39920c82015-08-03 19:20:26 +020050 sunxi_monitor_composite_pal,
51 sunxi_monitor_composite_ntsc,
52 sunxi_monitor_composite_pal_m,
53 sunxi_monitor_composite_pal_nc,
Hans de Goede1c092202014-12-21 14:37:45 +010054};
Hans de Goede39920c82015-08-03 19:20:26 +020055#define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
Hans de Goede1c092202014-12-21 14:37:45 +010056
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020057struct sunxi_display {
58 GraphicDevice graphic_device;
Hans de Goede1c092202014-12-21 14:37:45 +010059 enum sunxi_monitor monitor;
Hans de Goede2dae8002014-12-21 16:28:32 +010060 unsigned int depth;
Hans de Goede58332f82015-08-05 00:06:47 +020061 unsigned int fb_addr;
Hans de Goede20779ec2015-02-02 18:00:53 +010062 unsigned int fb_size;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020063} sunxi_display;
64
Hans de Goede39920c82015-08-03 19:20:26 +020065const struct ctfb_res_modes composite_video_modes[2] = {
66 /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */
67 { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED },
68 { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED },
69};
70
Hans de Goede2fbf0912014-12-23 23:04:35 +010071#ifdef CONFIG_VIDEO_HDMI
72
Hans de Goede75481602014-12-19 16:05:12 +010073/*
74 * Wait up to 200ms for value to be set in given part of reg.
75 */
76static int await_completion(u32 *reg, u32 mask, u32 val)
77{
78 unsigned long tmo = timer_get_us() + 200000;
79
80 while ((readl(reg) & mask) != val) {
81 if (timer_get_us() > tmo) {
82 printf("DDC: timeout reading EDID\n");
83 return -ETIME;
84 }
85 }
86 return 0;
87}
88
Hans de Goede7fad8a92014-12-28 09:13:21 +010089static int sunxi_hdmi_hpd_detect(int hpd_delay)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020090{
91 struct sunxi_ccm_reg * const ccm =
92 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
93 struct sunxi_hdmi_reg * const hdmi =
94 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Hans de Goede7fad8a92014-12-28 09:13:21 +010095 unsigned long tmo = timer_get_us() + hpd_delay * 1000;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +020096
97 /* Set pll3 to 300MHz */
98 clock_set_pll3(300000000);
99
100 /* Set hdmi parent to pll3 */
101 clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
102 CCM_HDMI_CTRL_PLL3);
103
104 /* Set ahb gating to pass */
Hans de Goede44d8ae52015-04-06 20:33:34 +0200105#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100106 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
107#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200108 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
109
110 /* Clock on */
111 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
112
113 writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl);
114 writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0);
115
Priit Laes361604d2018-12-19 15:06:08 +0200116 /* Enable PLLs for eventual DDC */
117 writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
118 &hdmi->pad_ctrl1);
119 writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
120 &hdmi->pll_ctrl);
121 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
122
Hans de Goede40f1b872014-12-20 15:15:23 +0100123 while (timer_get_us() < tmo) {
124 if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT)
125 return 1;
126 }
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200127
Hans de Goede40f1b872014-12-20 15:15:23 +0100128 return 0;
Hans de Goede518cef22014-12-19 15:13:57 +0100129}
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200130
Hans de Goede518cef22014-12-19 15:13:57 +0100131static void sunxi_hdmi_shutdown(void)
132{
133 struct sunxi_ccm_reg * const ccm =
134 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
135 struct sunxi_hdmi_reg * const hdmi =
136 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
137
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200138 clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE);
139 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
140 clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
Hans de Goede44d8ae52015-04-06 20:33:34 +0200141#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100142 clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
143#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200144 clock_set_pll3(0);
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200145}
146
Hans de Goede75481602014-12-19 16:05:12 +0100147static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
148{
149 struct sunxi_hdmi_reg * const hdmi =
150 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
151
152 setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
153 writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
154 SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
155 SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
156 SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
157#ifndef CONFIG_MACH_SUN6I
158 writel(n, &hdmi->ddc_byte_count);
159 writel(cmnd, &hdmi->ddc_cmnd);
160#else
161 writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
162#endif
163 setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
164
165 return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
166}
167
168static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
169{
170 struct sunxi_hdmi_reg * const hdmi =
171 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
172 int i, n;
173
174 while (count > 0) {
175 if (count > 16)
176 n = 16;
177 else
178 n = count;
179
180 if (sunxi_hdmi_ddc_do_command(
181 SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
182 offset, n))
183 return -ETIME;
184
185 for (i = 0; i < n; i++)
186 *buf++ = readb(&hdmi->ddc_fifo_data);
187
188 offset += n;
189 count -= n;
190 }
191
192 return 0;
193}
194
Hans de Goede63c5fbd2014-12-20 14:01:48 +0100195static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
196{
197 int r, retries = 2;
198
199 do {
200 r = sunxi_hdmi_ddc_read(block * 128, buf, 128);
201 if (r)
202 continue;
203 r = edid_check_checksum(buf);
204 if (r) {
205 printf("EDID block %d: checksum error%s\n",
206 block, retries ? ", retrying" : "");
207 }
208 } while (r && retries--);
209
210 return r;
211}
212
Priit Laes0220f8c2018-12-19 15:06:09 +0200213static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode,
214 bool verbose_mode)
Hans de Goede75481602014-12-19 16:05:12 +0100215{
216 struct edid1_info edid1;
Hans de Goedef3000682014-12-20 14:31:45 +0100217 struct edid_cea861_info cea681[4];
Hans de Goede75481602014-12-19 16:05:12 +0100218 struct edid_detailed_timing *t =
219 (struct edid_detailed_timing *)edid1.monitor_details.timing;
220 struct sunxi_hdmi_reg * const hdmi =
221 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
222 struct sunxi_ccm_reg * const ccm =
223 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Hans de Goedef3000682014-12-20 14:31:45 +0100224 int i, r, ext_blocks = 0;
Hans de Goede75481602014-12-19 16:05:12 +0100225
Hans de Goede75481602014-12-19 16:05:12 +0100226 /* Reset i2c controller */
227 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
228 writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
229 SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
230 SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
231 SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
232 if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
233 return -EIO;
234
235 writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
236#ifndef CONFIG_MACH_SUN6I
237 writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
238 SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
239#endif
240
Hans de Goede63c5fbd2014-12-20 14:01:48 +0100241 r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
Hans de Goedef3000682014-12-20 14:31:45 +0100242 if (r == 0) {
243 r = edid_check_info(&edid1);
244 if (r) {
Priit Laes0220f8c2018-12-19 15:06:09 +0200245 if (verbose_mode)
246 printf("EDID: invalid EDID data\n");
Hans de Goedef3000682014-12-20 14:31:45 +0100247 r = -EINVAL;
248 }
249 }
250 if (r == 0) {
251 ext_blocks = edid1.extension_flag;
252 if (ext_blocks > 4)
253 ext_blocks = 4;
254 for (i = 0; i < ext_blocks; i++) {
255 if (sunxi_hdmi_edid_get_block(1 + i,
256 (u8 *)&cea681[i]) != 0) {
257 ext_blocks = i;
258 break;
259 }
260 }
261 }
Hans de Goede75481602014-12-19 16:05:12 +0100262
263 /* Disable DDC engine, no longer needed */
264 clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
265 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
266
267 if (r)
268 return r;
269
Hans de Goede75481602014-12-19 16:05:12 +0100270 /* We want version 1.3 or 1.2 with detailed timing info */
271 if (edid1.version != 1 || (edid1.revision < 3 &&
272 !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
273 printf("EDID: unsupported version %d.%d\n",
274 edid1.version, edid1.revision);
275 return -EINVAL;
276 }
277
278 /* Take the first usable detailed timing */
279 for (i = 0; i < 4; i++, t++) {
280 r = video_edid_dtd_to_ctfb_res_modes(t, mode);
281 if (r == 0)
282 break;
283 }
284 if (i == 4) {
285 printf("EDID: no usable detailed timing found\n");
286 return -ENOENT;
287 }
288
Hans de Goedef3000682014-12-20 14:31:45 +0100289 /* Check for basic audio support, if found enable hdmi output */
Hans de Goede1c092202014-12-21 14:37:45 +0100290 sunxi_display.monitor = sunxi_monitor_dvi;
Hans de Goedef3000682014-12-20 14:31:45 +0100291 for (i = 0; i < ext_blocks; i++) {
292 if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
293 cea681[i].revision < 2)
294 continue;
295
296 if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
Hans de Goede1c092202014-12-21 14:37:45 +0100297 sunxi_display.monitor = sunxi_monitor_hdmi;
Hans de Goedef3000682014-12-20 14:31:45 +0100298 }
299
Hans de Goede75481602014-12-19 16:05:12 +0100300 return 0;
301}
302
Hans de Goede2fbf0912014-12-23 23:04:35 +0100303#endif /* CONFIG_VIDEO_HDMI */
304
Hans de Goede7cd6f922015-01-19 08:44:07 +0100305#ifdef CONFIG_MACH_SUN4I
306/*
307 * Testing has shown that on sun4i the display backend engine does not have
308 * deep enough fifo-s causing flickering / tearing in full-hd mode due to
309 * fifo underruns. So on sun4i we use the display frontend engine to do the
310 * dma from memory, as the frontend does have deep enough fifo-s.
311 */
312
313static const u32 sun4i_vert_coef[32] = {
314 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
315 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
316 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
317 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
318 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
319 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
320 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
321 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
322};
323
324static const u32 sun4i_horz_coef[64] = {
325 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
326 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
327 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
328 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
329 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
330 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
331 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
332 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
333 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
334 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
335 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
336 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
337 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
338 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
339 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
340 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
341};
342
343static void sunxi_frontend_init(void)
344{
345 struct sunxi_ccm_reg * const ccm =
346 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
347 struct sunxi_de_fe_reg * const de_fe =
348 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
349 int i;
350
351 /* Clocks on */
352 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0);
353 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0);
354 clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000);
355
356 setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN);
357
358 for (i = 0; i < 32; i++) {
359 writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]);
360 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]);
361 writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]);
362 writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]);
363 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]);
364 writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]);
365 }
366
367 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY);
368}
369
370static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
371 unsigned int address)
372{
373 struct sunxi_de_fe_reg * const de_fe =
374 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
375
376 setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS);
377 writel(CONFIG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr);
378 writel(mode->xres * 4, &de_fe->ch0_stride);
379 writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt);
380 writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt);
381
382 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
383 &de_fe->ch0_insize);
384 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
385 &de_fe->ch0_outsize);
386 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact);
387 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact);
388
389 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
390 &de_fe->ch1_insize);
391 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
392 &de_fe->ch1_outsize);
393 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact);
394 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact);
395
396 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY);
397}
398
399static void sunxi_frontend_enable(void)
400{
401 struct sunxi_de_fe_reg * const de_fe =
402 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
403
404 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START);
405}
406#else
407static void sunxi_frontend_init(void) {}
408static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
409 unsigned int address) {}
410static void sunxi_frontend_enable(void) {}
411#endif
412
Hans de Goede39920c82015-08-03 19:20:26 +0200413static bool sunxi_is_composite(void)
414{
415 switch (sunxi_display.monitor) {
416 case sunxi_monitor_none:
417 case sunxi_monitor_dvi:
418 case sunxi_monitor_hdmi:
419 case sunxi_monitor_lcd:
420 case sunxi_monitor_vga:
421 return false;
422 case sunxi_monitor_composite_pal:
423 case sunxi_monitor_composite_ntsc:
424 case sunxi_monitor_composite_pal_m:
425 case sunxi_monitor_composite_pal_nc:
426 return true;
427 }
428
429 return false; /* Never reached */
430}
431
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200432/*
433 * This is the entity that mixes and matches the different layers and inputs.
434 * Allwinner calls it the back-end, but i like composer better.
435 */
436static void sunxi_composer_init(void)
437{
438 struct sunxi_ccm_reg * const ccm =
439 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
440 struct sunxi_de_be_reg * const de_be =
441 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
442 int i;
443
Hans de Goede7cd6f922015-01-19 08:44:07 +0100444 sunxi_frontend_init();
445
Hans de Goede44d8ae52015-04-06 20:33:34 +0200446#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100447 /* Reset off */
448 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0);
449#endif
450
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200451 /* Clocks on */
452 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100453#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200454 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100455#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200456 clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000);
457
458 /* Engine bug, clear registers after reset */
459 for (i = 0x0800; i < 0x1000; i += 4)
460 writel(0, SUNXI_DE_BE0_BASE + i);
461
462 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE);
463}
464
Priit Laes3d99a0b2018-10-23 20:20:31 +0300465static const u32 sunxi_rgb2yuv_coef[12] = {
Hans de Goede39920c82015-08-03 19:20:26 +0200466 0x00000107, 0x00000204, 0x00000064, 0x00000108,
467 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,
468 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
469};
470
Hans de Goedebe8ec632014-12-19 13:46:33 +0100471static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200472 unsigned int address)
473{
474 struct sunxi_de_be_reg * const de_be =
475 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
Hans de Goede39920c82015-08-03 19:20:26 +0200476 int i;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200477
Hans de Goede7cd6f922015-01-19 08:44:07 +0100478 sunxi_frontend_mode_set(mode, address);
479
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200480 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
481 &de_be->disp_size);
482 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
483 &de_be->layer0_size);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100484#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200485 writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride);
486 writel(address << 3, &de_be->layer0_addr_low32b);
487 writel(address >> 29, &de_be->layer0_addr_high4b);
Hans de Goede7cd6f922015-01-19 08:44:07 +0100488#else
489 writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl);
490#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200491 writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl);
492
493 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE);
Hans de Goedef6d9d322015-08-02 16:49:29 +0200494 if (mode->vmode == FB_VMODE_INTERLACED)
495 setbits_le32(&de_be->mode,
Hans de Goeded8d07992015-08-06 12:08:33 +0200496#ifndef CONFIG_MACH_SUN5I
Hans de Goedef6d9d322015-08-02 16:49:29 +0200497 SUNXI_DE_BE_MODE_DEFLICKER_ENABLE |
Hans de Goeded8d07992015-08-06 12:08:33 +0200498#endif
Hans de Goedef6d9d322015-08-02 16:49:29 +0200499 SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
Hans de Goede39920c82015-08-03 19:20:26 +0200500
501 if (sunxi_is_composite()) {
502 writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
503 &de_be->output_color_ctrl);
504 for (i = 0; i < 12; i++)
505 writel(sunxi_rgb2yuv_coef[i],
506 &de_be->output_color_coef[i]);
507 }
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200508}
509
Hans de Goede0e045212014-12-21 14:49:34 +0100510static void sunxi_composer_enable(void)
511{
512 struct sunxi_de_be_reg * const de_be =
513 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
514
Hans de Goede7cd6f922015-01-19 08:44:07 +0100515 sunxi_frontend_enable();
516
Hans de Goede0e045212014-12-21 14:49:34 +0100517 setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS);
518 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
519}
520
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200521static void sunxi_lcdc_init(void)
522{
523 struct sunxi_ccm_reg * const ccm =
524 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
525 struct sunxi_lcdc_reg * const lcdc =
526 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
527
528 /* Reset off */
Hans de Goede44d8ae52015-04-06 20:33:34 +0200529#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede211717a2014-11-14 17:42:14 +0100530 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
531#else
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200532 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST);
Hans de Goede211717a2014-11-14 17:42:14 +0100533#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200534
535 /* Clock on */
536 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
Hans de Goede213480e2015-01-01 22:04:34 +0100537#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Hans de Goede83edb2a2015-05-14 18:52:54 +0200538#ifdef CONFIG_SUNXI_GEN_SUN6I
539 setbits_le32(&ccm->ahb_reset2_cfg, 1 << AHB_RESET_OFFSET_LVDS);
540#else
Hans de Goede213480e2015-01-01 22:04:34 +0100541 setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
542#endif
Hans de Goede83edb2a2015-05-14 18:52:54 +0200543#endif
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200544
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200545 lcdc_init(lcdc);
Hans de Goede0e045212014-12-21 14:49:34 +0100546}
547
Hans de Goede2dae8002014-12-21 16:28:32 +0100548static void sunxi_lcdc_panel_enable(void)
549{
Hans de Goede242e3d82015-02-16 17:26:41 +0100550 int pin, reset_pin;
Hans de Goede2dae8002014-12-21 16:28:32 +0100551
552 /*
553 * Start with backlight disabled to avoid the screen flashing to
554 * white while the lcd inits.
555 */
556 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede15728192015-04-22 17:45:59 +0200557 if (pin >= 0) {
Hans de Goede2dae8002014-12-21 16:28:32 +0100558 gpio_request(pin, "lcd_backlight_enable");
559 gpio_direction_output(pin, 0);
560 }
561
562 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede15728192015-04-22 17:45:59 +0200563 if (pin >= 0) {
Hans de Goede2dae8002014-12-21 16:28:32 +0100564 gpio_request(pin, "lcd_backlight_pwm");
Hans de Goedea7403ae2015-01-22 21:02:42 +0100565 gpio_direction_output(pin, PWM_OFF);
Hans de Goede2dae8002014-12-21 16:28:32 +0100566 }
567
Hans de Goede242e3d82015-02-16 17:26:41 +0100568 reset_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_RESET);
Hans de Goede15728192015-04-22 17:45:59 +0200569 if (reset_pin >= 0) {
Hans de Goede242e3d82015-02-16 17:26:41 +0100570 gpio_request(reset_pin, "lcd_reset");
571 gpio_direction_output(reset_pin, 0); /* Assert reset */
572 }
573
Hans de Goede2dae8002014-12-21 16:28:32 +0100574 /* Give the backlight some time to turn off and power up the panel. */
575 mdelay(40);
576 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
Hans de Goede15728192015-04-22 17:45:59 +0200577 if (pin >= 0) {
Hans de Goede2dae8002014-12-21 16:28:32 +0100578 gpio_request(pin, "lcd_power");
579 gpio_direction_output(pin, 1);
580 }
Hans de Goede242e3d82015-02-16 17:26:41 +0100581
Hans de Goede15728192015-04-22 17:45:59 +0200582 if (reset_pin >= 0)
Hans de Goede242e3d82015-02-16 17:26:41 +0100583 gpio_direction_output(reset_pin, 1); /* De-assert reset */
Hans de Goede2dae8002014-12-21 16:28:32 +0100584}
585
586static void sunxi_lcdc_backlight_enable(void)
587{
588 int pin;
589
590 /*
591 * We want to have scanned out at least one frame before enabling the
592 * backlight to avoid the screen flashing to white when we enable it.
593 */
594 mdelay(40);
595
596 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede15728192015-04-22 17:45:59 +0200597 if (pin >= 0)
Hans de Goede2dae8002014-12-21 16:28:32 +0100598 gpio_direction_output(pin, 1);
599
600 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede421c98d2016-08-19 15:25:41 +0200601#ifdef SUNXI_PWM_PIN0
602 if (pin == SUNXI_PWM_PIN0) {
603 writel(SUNXI_PWM_CTRL_POLARITY0(PWM_ON) |
604 SUNXI_PWM_CTRL_ENABLE0 |
605 SUNXI_PWM_CTRL_PRESCALE0(0xf), SUNXI_PWM_CTRL_REG);
606 writel(SUNXI_PWM_PERIOD_80PCT, SUNXI_PWM_CH0_PERIOD);
607 sunxi_gpio_set_cfgpin(pin, SUNXI_PWM_MUX);
608 return;
609 }
610#endif
Hans de Goede15728192015-04-22 17:45:59 +0200611 if (pin >= 0)
Hans de Goedea7403ae2015-01-22 21:02:42 +0100612 gpio_direction_output(pin, PWM_ON);
Hans de Goede2dae8002014-12-21 16:28:32 +0100613}
614
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200615static void sunxi_ctfb_mode_to_display_timing(const struct ctfb_res_modes *mode,
616 struct display_timing *timing)
617{
618 timing->pixelclock.typ = mode->pixclock_khz * 1000;
619
620 timing->hactive.typ = mode->xres;
621 timing->hfront_porch.typ = mode->right_margin;
622 timing->hback_porch.typ = mode->left_margin;
623 timing->hsync_len.typ = mode->hsync_len;
624
625 timing->vactive.typ = mode->yres;
626 timing->vfront_porch.typ = mode->lower_margin;
627 timing->vback_porch.typ = mode->upper_margin;
628 timing->vsync_len.typ = mode->vsync_len;
629
Giulio Benetti3900b452018-01-16 17:43:48 +0100630 timing->flags = 0;
631
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200632 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
633 timing->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
634 else
635 timing->flags |= DISPLAY_FLAGS_HSYNC_LOW;
636 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
637 timing->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
638 else
639 timing->flags |= DISPLAY_FLAGS_VSYNC_LOW;
640 if (mode->vmode == FB_VMODE_INTERLACED)
641 timing->flags |= DISPLAY_FLAGS_INTERLACED;
642}
643
Hans de Goedefb75d972015-01-25 15:33:07 +0100644static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
645 bool for_ext_vga_dac)
Hans de Goede2dae8002014-12-21 16:28:32 +0100646{
647 struct sunxi_lcdc_reg * const lcdc =
648 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700649 struct sunxi_ccm_reg * const ccm =
650 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200651 int clk_div, clk_double, pin;
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200652 struct display_timing timing;
Hans de Goede2dae8002014-12-21 16:28:32 +0100653
Lawrence Yucf6eca72016-03-04 09:08:56 -0800654#if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS
655 for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) {
656#else
Hans de Goedec1cfd512015-08-08 16:13:53 +0200657 for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) {
Lawrence Yucf6eca72016-03-04 09:08:56 -0800658#endif
Hans de Goede213480e2015-01-01 22:04:34 +0100659#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Paul Kocialkowski487b3272015-03-22 18:12:22 +0100660 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0);
Hans de Goede213480e2015-01-01 22:04:34 +0100661#endif
662#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Paul Kocialkowski487b3272015-03-22 18:12:22 +0100663 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LVDS0);
Hans de Goede213480e2015-01-01 22:04:34 +0100664#endif
Hans de Goedec1cfd512015-08-08 16:13:53 +0200665#ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804
666 sunxi_gpio_set_drv(pin, 3);
667#endif
668 }
Hans de Goede2dae8002014-12-21 16:28:32 +0100669
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700670 lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double,
671 sunxi_is_composite());
Hans de Goede2dae8002014-12-21 16:28:32 +0100672
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200673 sunxi_ctfb_mode_to_display_timing(mode, &timing);
674 lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200675 sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
Hans de Goede2dae8002014-12-21 16:28:32 +0100676}
677
Hans de Goede39920c82015-08-03 19:20:26 +0200678#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goede0e045212014-12-21 14:49:34 +0100679static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
Hans de Goede3ffbe472014-12-27 15:19:23 +0100680 int *clk_div, int *clk_double,
681 bool use_portd_hvsync)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200682{
683 struct sunxi_lcdc_reg * const lcdc =
684 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700685 struct sunxi_ccm_reg * const ccm =
686 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200687 struct display_timing timing;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200688
Jernej Skrabec30ca2022017-03-27 19:22:30 +0200689 sunxi_ctfb_mode_to_display_timing(mode, &timing);
690 lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200691 sunxi_is_composite());
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200692
Hans de Goede3ffbe472014-12-27 15:19:23 +0100693 if (use_portd_hvsync) {
Paul Kocialkowski487b3272015-03-22 18:12:22 +0100694 sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
695 sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0);
Hans de Goede3ffbe472014-12-27 15:19:23 +0100696 }
Hans de Goeded8d07992015-08-06 12:08:33 +0200697
Vasily Khoruzhick79f285d2017-10-26 21:51:51 -0700698 lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double,
699 sunxi_is_composite());
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200700}
Hans de Goede39920c82015-08-03 19:20:26 +0200701#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
Hans de Goeded9786d22014-12-25 13:58:06 +0100702
703#ifdef CONFIG_VIDEO_HDMI
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200704
Hans de Goede5ee0bea2014-12-20 13:38:06 +0100705static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
706{
707 struct sunxi_hdmi_reg * const hdmi =
708 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
709 u8 checksum = 0;
710 u8 avi_info_frame[17] = {
711 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00,
712 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
713 0x00
714 };
715 u8 vendor_info_frame[19] = {
716 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40,
717 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
718 0x00, 0x00, 0x00
719 };
720 int i;
721
722 if (mode->pixclock_khz <= 27000)
723 avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */
724 else
725 avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */
726
727 if (mode->xres * 100 / mode->yres < 156)
728 avi_info_frame[5] |= 0x18; /* 4 : 3 */
729 else
730 avi_info_frame[5] |= 0x28; /* 16 : 9 */
731
732 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
733 checksum += avi_info_frame[i];
734
735 avi_info_frame[3] = 0x100 - checksum;
736
737 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
738 writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]);
739
740 writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0);
741 writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1);
742
743 for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++)
744 writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]);
745
746 writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0);
747 writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1);
748
749 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI);
750}
751
Hans de Goedebe8ec632014-12-19 13:46:33 +0100752static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
Hans de Goede1c092202014-12-21 14:37:45 +0100753 int clk_div, int clk_double)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200754{
755 struct sunxi_hdmi_reg * const hdmi =
756 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
757 int x, y;
758
759 /* Write clear interrupt status bits */
760 writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq);
761
Hans de Goede1c092202014-12-21 14:37:45 +0100762 if (sunxi_display.monitor == sunxi_monitor_hdmi)
Hans de Goede5ee0bea2014-12-20 13:38:06 +0100763 sunxi_hdmi_setup_info_frames(mode);
764
Hans de Goede876aaaf2014-12-20 13:51:16 +0100765 /* Set input sync enable */
766 writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown);
767
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200768 /* Init various registers, select pll3 as clock source */
769 writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity);
770 writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0);
771 writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1);
772 writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl);
773 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
774
775 /* Setup clk div and doubler */
776 clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK,
777 SUNXI_HDMI_PLL_CTRL_DIV(clk_div));
778 if (!clk_double)
779 setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE);
780
781 /* Setup timing registers */
782 writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres),
783 &hdmi->video_size);
784
785 x = mode->hsync_len + mode->left_margin;
786 y = mode->vsync_len + mode->upper_margin;
787 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp);
788
789 x = mode->right_margin;
790 y = mode->lower_margin;
791 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp);
792
793 x = mode->hsync_len;
794 y = mode->vsync_len;
795 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw);
796
797 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
798 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR);
799
800 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
801 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER);
802}
803
Hans de Goede0e045212014-12-21 14:49:34 +0100804static void sunxi_hdmi_enable(void)
805{
806 struct sunxi_hdmi_reg * const hdmi =
807 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
808
809 udelay(100);
810 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE);
811}
812
Hans de Goede2fbf0912014-12-23 23:04:35 +0100813#endif /* CONFIG_VIDEO_HDMI */
814
Hans de Goede39920c82015-08-03 19:20:26 +0200815#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goeded9786d22014-12-25 13:58:06 +0100816
Hans de Goede39920c82015-08-03 19:20:26 +0200817static void sunxi_tvencoder_mode_set(void)
Hans de Goeded9786d22014-12-25 13:58:06 +0100818{
819 struct sunxi_ccm_reg * const ccm =
820 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
821 struct sunxi_tve_reg * const tve =
822 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
823
Hans de Goeded8d07992015-08-06 12:08:33 +0200824 /* Reset off */
825 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_TVE_RST);
Hans de Goeded9786d22014-12-25 13:58:06 +0100826 /* Clock on */
827 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
828
Hans de Goede39920c82015-08-03 19:20:26 +0200829 switch (sunxi_display.monitor) {
830 case sunxi_monitor_vga:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200831 tvencoder_mode_set(tve, tve_mode_vga);
Hans de Goede39920c82015-08-03 19:20:26 +0200832 break;
833 case sunxi_monitor_composite_pal_nc:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200834 tvencoder_mode_set(tve, tve_mode_composite_pal_nc);
835 break;
Hans de Goede39920c82015-08-03 19:20:26 +0200836 case sunxi_monitor_composite_pal:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200837 tvencoder_mode_set(tve, tve_mode_composite_pal);
Hans de Goede39920c82015-08-03 19:20:26 +0200838 break;
839 case sunxi_monitor_composite_pal_m:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200840 tvencoder_mode_set(tve, tve_mode_composite_pal_m);
841 break;
Hans de Goede39920c82015-08-03 19:20:26 +0200842 case sunxi_monitor_composite_ntsc:
Jernej Skrabecc1080622017-05-10 18:46:28 +0200843 tvencoder_mode_set(tve, tve_mode_composite_ntsc);
Hans de Goede39920c82015-08-03 19:20:26 +0200844 break;
845 case sunxi_monitor_none:
846 case sunxi_monitor_dvi:
847 case sunxi_monitor_hdmi:
848 case sunxi_monitor_lcd:
849 break;
850 }
Hans de Goeded9786d22014-12-25 13:58:06 +0100851}
852
Hans de Goede39920c82015-08-03 19:20:26 +0200853#endif /* CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE */
Hans de Goeded9786d22014-12-25 13:58:06 +0100854
Hans de Goedee8400792014-12-23 18:39:52 +0100855static void sunxi_drc_init(void)
856{
Hans de Goede44d8ae52015-04-06 20:33:34 +0200857#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedee8400792014-12-23 18:39:52 +0100858 struct sunxi_ccm_reg * const ccm =
859 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
860
861 /* On sun6i the drc must be clocked even when in pass-through mode */
Vishnu Patekar8c3dacf2015-03-01 23:47:48 +0530862#ifdef CONFIG_MACH_SUN8I_A33
863 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_SAT);
864#endif
Hans de Goedee8400792014-12-23 18:39:52 +0100865 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0);
866 clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000);
867#endif
868}
869
Chen-Yu Tsai507e27d2015-01-12 18:02:11 +0800870#ifdef CONFIG_VIDEO_VGA_VIA_LCD
871static void sunxi_vga_external_dac_enable(void)
872{
873 int pin;
874
875 pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
Hans de Goede15728192015-04-22 17:45:59 +0200876 if (pin >= 0) {
Chen-Yu Tsai507e27d2015-01-12 18:02:11 +0800877 gpio_request(pin, "vga_enable");
878 gpio_direction_output(pin, 1);
879 }
880}
881#endif /* CONFIG_VIDEO_VGA_VIA_LCD */
882
Siarhei Siamashka97ece832015-01-19 05:23:33 +0200883#ifdef CONFIG_VIDEO_LCD_SSD2828
884static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
885{
886 struct ssd2828_config cfg = {
887 .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
888 .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
889 .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
890 .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
891 .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
892 .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
893 .ssd2828_color_depth = 24,
894#ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
895 .mipi_dsi_number_of_data_lanes = 4,
896 .mipi_dsi_bitrate_per_data_lane_mbps = 513,
897 .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
898 .mipi_dsi_delay_after_set_display_on_ms = 200
899#else
900#error MIPI LCD panel needs configuration parameters
901#endif
902 };
903
904 if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
905 printf("SSD2828: SPI pins are not properly configured\n");
906 return 1;
907 }
908 if (cfg.reset_pin == -1) {
909 printf("SSD2828: Reset pin is not properly configured\n");
910 return 1;
911 }
912
913 return ssd2828_init(&cfg, mode);
914}
915#endif /* CONFIG_VIDEO_LCD_SSD2828 */
916
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200917static void sunxi_engines_init(void)
918{
919 sunxi_composer_init();
920 sunxi_lcdc_init();
Hans de Goede211717a2014-11-14 17:42:14 +0100921 sunxi_drc_init();
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200922}
923
Hans de Goede1c092202014-12-21 14:37:45 +0100924static void sunxi_mode_set(const struct ctfb_res_modes *mode,
Hans de Goede5ee0bea2014-12-20 13:38:06 +0100925 unsigned int address)
Luc Verhaegen7f2c5212014-08-13 07:55:06 +0200926{
Hans de Goeded9786d22014-12-25 13:58:06 +0100927 int __maybe_unused clk_div, clk_double;
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200928 struct sunxi_lcdc_reg * const lcdc =
929 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Jernej Skrabecc1080622017-05-10 18:46:28 +0200930 struct sunxi_tve_reg * __maybe_unused const tve =
931 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
Hans de Goeded9786d22014-12-25 13:58:06 +0100932
Hans de Goede0e045212014-12-21 14:49:34 +0100933 switch (sunxi_display.monitor) {
934 case sunxi_monitor_none:
935 break;
936 case sunxi_monitor_dvi:
Hans de Goeded9786d22014-12-25 13:58:06 +0100937 case sunxi_monitor_hdmi:
Hans de Goede2fbf0912014-12-23 23:04:35 +0100938#ifdef CONFIG_VIDEO_HDMI
Hans de Goede0e045212014-12-21 14:49:34 +0100939 sunxi_composer_mode_set(mode, address);
Hans de Goede3ffbe472014-12-27 15:19:23 +0100940 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
Hans de Goede0e045212014-12-21 14:49:34 +0100941 sunxi_hdmi_mode_set(mode, clk_div, clk_double);
942 sunxi_composer_enable();
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200943 lcdc_enable(lcdc, sunxi_display.depth);
Hans de Goede0e045212014-12-21 14:49:34 +0100944 sunxi_hdmi_enable();
Hans de Goede2fbf0912014-12-23 23:04:35 +0100945#endif
Hans de Goede0e045212014-12-21 14:49:34 +0100946 break;
947 case sunxi_monitor_lcd:
Hans de Goede2dae8002014-12-21 16:28:32 +0100948 sunxi_lcdc_panel_enable();
Hans de Goedec1cfd512015-08-08 16:13:53 +0200949 if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804)) {
950 /*
951 * The anx9804 needs 1.8V from eldo3, we do this here
Hans de Goede6944aff2015-10-03 15:18:33 +0200952 * and not via CONFIG_AXP_ELDO3_VOLT from board_init()
Hans de Goedec1cfd512015-08-08 16:13:53 +0200953 * to avoid turning this on when using hdmi output.
954 */
Hans de Goede6944aff2015-10-03 15:18:33 +0200955 axp_set_eldo(3, 1800);
Hans de Goedec1cfd512015-08-08 16:13:53 +0200956 anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4,
957 ANX9804_DATA_RATE_1620M,
958 sunxi_display.depth);
959 }
Hans de Goede27515b22015-01-20 09:23:36 +0100960 if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
961 mdelay(50); /* Wait for lcd controller power on */
962 hitachi_tx18d42vm_init();
963 }
Hans de Goedeaad2ac22015-02-16 17:49:47 +0100964 if (IS_ENABLED(CONFIG_VIDEO_LCD_TL059WV5C0)) {
965 unsigned int orig_i2c_bus = i2c_get_bus_num();
966 i2c_set_bus_num(CONFIG_VIDEO_LCD_I2C_BUS);
967 i2c_reg_write(0x5c, 0x04, 0x42); /* Turn on the LCD */
968 i2c_set_bus_num(orig_i2c_bus);
969 }
Hans de Goede2dae8002014-12-21 16:28:32 +0100970 sunxi_composer_mode_set(mode, address);
Hans de Goedefb75d972015-01-25 15:33:07 +0100971 sunxi_lcdc_tcon0_mode_set(mode, false);
Hans de Goede2dae8002014-12-21 16:28:32 +0100972 sunxi_composer_enable();
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200973 lcdc_enable(lcdc, sunxi_display.depth);
Siarhei Siamashka97ece832015-01-19 05:23:33 +0200974#ifdef CONFIG_VIDEO_LCD_SSD2828
975 sunxi_ssd2828_init(mode);
976#endif
Hans de Goede2dae8002014-12-21 16:28:32 +0100977 sunxi_lcdc_backlight_enable();
Hans de Goede0e045212014-12-21 14:49:34 +0100978 break;
979 case sunxi_monitor_vga:
Hans de Goeded9786d22014-12-25 13:58:06 +0100980#ifdef CONFIG_VIDEO_VGA
981 sunxi_composer_mode_set(mode, address);
982 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
Hans de Goede39920c82015-08-03 19:20:26 +0200983 sunxi_tvencoder_mode_set();
Hans de Goeded9786d22014-12-25 13:58:06 +0100984 sunxi_composer_enable();
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200985 lcdc_enable(lcdc, sunxi_display.depth);
Jernej Skrabecc1080622017-05-10 18:46:28 +0200986 tvencoder_enable(tve);
Hans de Goeded9786d22014-12-25 13:58:06 +0100987#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goedee2bbdfb2014-12-24 12:17:07 +0100988 sunxi_composer_mode_set(mode, address);
Hans de Goedefb75d972015-01-25 15:33:07 +0100989 sunxi_lcdc_tcon0_mode_set(mode, true);
Hans de Goedee2bbdfb2014-12-24 12:17:07 +0100990 sunxi_composer_enable();
Jernej Skrabec5e023e72017-03-27 19:22:29 +0200991 lcdc_enable(lcdc, sunxi_display.depth);
Chen-Yu Tsai507e27d2015-01-12 18:02:11 +0800992 sunxi_vga_external_dac_enable();
Hans de Goedee2bbdfb2014-12-24 12:17:07 +0100993#endif
Hans de Goede0e045212014-12-21 14:49:34 +0100994 break;
Hans de Goede39920c82015-08-03 19:20:26 +0200995 case sunxi_monitor_composite_pal:
996 case sunxi_monitor_composite_ntsc:
997 case sunxi_monitor_composite_pal_m:
998 case sunxi_monitor_composite_pal_nc:
999#ifdef CONFIG_VIDEO_COMPOSITE
1000 sunxi_composer_mode_set(mode, address);
1001 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
1002 sunxi_tvencoder_mode_set();
1003 sunxi_composer_enable();
Jernej Skrabec5e023e72017-03-27 19:22:29 +02001004 lcdc_enable(lcdc, sunxi_display.depth);
Jernej Skrabecc1080622017-05-10 18:46:28 +02001005 tvencoder_enable(tve);
Hans de Goede39920c82015-08-03 19:20:26 +02001006#endif
1007 break;
Hans de Goede0e045212014-12-21 14:49:34 +01001008 }
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001009}
1010
Hans de Goede1c092202014-12-21 14:37:45 +01001011static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
1012{
1013 switch (monitor) {
Hans de Goede39920c82015-08-03 19:20:26 +02001014 case sunxi_monitor_none: return "none";
1015 case sunxi_monitor_dvi: return "dvi";
1016 case sunxi_monitor_hdmi: return "hdmi";
1017 case sunxi_monitor_lcd: return "lcd";
1018 case sunxi_monitor_vga: return "vga";
1019 case sunxi_monitor_composite_pal: return "composite-pal";
1020 case sunxi_monitor_composite_ntsc: return "composite-ntsc";
1021 case sunxi_monitor_composite_pal_m: return "composite-pal-m";
1022 case sunxi_monitor_composite_pal_nc: return "composite-pal-nc";
Hans de Goede1c092202014-12-21 14:37:45 +01001023 }
1024 return NULL; /* never reached */
1025}
1026
Hans de Goede5633a292015-02-02 17:13:29 +01001027ulong board_get_usable_ram_top(ulong total_size)
1028{
1029 return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
1030}
1031
Hans de Goedebf689342015-08-03 23:01:38 +02001032static bool sunxi_has_hdmi(void)
1033{
1034#ifdef CONFIG_VIDEO_HDMI
1035 return true;
1036#else
1037 return false;
1038#endif
1039}
1040
1041static bool sunxi_has_lcd(void)
1042{
1043 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
1044
1045 return lcd_mode[0] != 0;
1046}
1047
1048static bool sunxi_has_vga(void)
1049{
1050#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_VGA_VIA_LCD
1051 return true;
1052#else
1053 return false;
1054#endif
1055}
1056
Hans de Goede39920c82015-08-03 19:20:26 +02001057static bool sunxi_has_composite(void)
1058{
1059#ifdef CONFIG_VIDEO_COMPOSITE
1060 return true;
1061#else
1062 return false;
1063#endif
1064}
1065
Hans de Goedebf689342015-08-03 23:01:38 +02001066static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
1067{
1068 if (allow_hdmi && sunxi_has_hdmi())
1069 return sunxi_monitor_dvi;
1070 else if (sunxi_has_lcd())
1071 return sunxi_monitor_lcd;
1072 else if (sunxi_has_vga())
1073 return sunxi_monitor_vga;
Hans de Goede39920c82015-08-03 19:20:26 +02001074 else if (sunxi_has_composite())
1075 return sunxi_monitor_composite_pal;
Hans de Goedebf689342015-08-03 23:01:38 +02001076 else
1077 return sunxi_monitor_none;
1078}
1079
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001080void *video_hw_init(void)
1081{
1082 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
Hans de Goede5f339932014-12-19 14:03:40 +01001083 const struct ctfb_res_modes *mode;
Hans de Goede2dae8002014-12-21 16:28:32 +01001084 struct ctfb_res_modes custom;
Hans de Goede5f339932014-12-19 14:03:40 +01001085 const char *options;
Hans de Goede2fbf0912014-12-23 23:04:35 +01001086#ifdef CONFIG_VIDEO_HDMI
Priit Laes0220f8c2018-12-19 15:06:09 +02001087 int hpd, hpd_delay, edid;
1088 bool hdmi_present;
Hans de Goede2fbf0912014-12-23 23:04:35 +01001089#endif
Hans de Goede58332f82015-08-05 00:06:47 +02001090 int i, overscan_offset, overscan_x, overscan_y;
1091 unsigned int fb_dma_addr;
Hans de Goede1c092202014-12-21 14:37:45 +01001092 char mon[16];
Hans de Goede2dae8002014-12-21 16:28:32 +01001093 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001094
1095 memset(&sunxi_display, 0, sizeof(struct sunxi_display));
1096
Hans de Goede2dae8002014-12-21 16:28:32 +01001097 video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
1098 &sunxi_display.depth, &options);
Hans de Goede2fbf0912014-12-23 23:04:35 +01001099#ifdef CONFIG_VIDEO_HDMI
Hans de Goede518cef22014-12-19 15:13:57 +01001100 hpd = video_get_option_int(options, "hpd", 1);
Hans de Goede7fad8a92014-12-28 09:13:21 +01001101 hpd_delay = video_get_option_int(options, "hpd_delay", 500);
Hans de Goede75481602014-12-19 16:05:12 +01001102 edid = video_get_option_int(options, "edid", 1);
Hans de Goede2fbf0912014-12-23 23:04:35 +01001103#endif
Hans de Goede58332f82015-08-05 00:06:47 +02001104 overscan_x = video_get_option_int(options, "overscan_x", -1);
1105 overscan_y = video_get_option_int(options, "overscan_y", -1);
Hans de Goedebf689342015-08-03 23:01:38 +02001106 sunxi_display.monitor = sunxi_get_default_mon(true);
Hans de Goede1c092202014-12-21 14:37:45 +01001107 video_get_option_string(options, "monitor", mon, sizeof(mon),
1108 sunxi_get_mon_desc(sunxi_display.monitor));
1109 for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
1110 if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
1111 sunxi_display.monitor = i;
1112 break;
1113 }
1114 }
1115 if (i > SUNXI_MONITOR_LAST)
1116 printf("Unknown monitor: '%s', falling back to '%s'\n",
1117 mon, sunxi_get_mon_desc(sunxi_display.monitor));
Hans de Goede5f339932014-12-19 14:03:40 +01001118
Hans de Goede49d27032014-12-25 13:52:04 +01001119#ifdef CONFIG_VIDEO_HDMI
1120 /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
1121 if (sunxi_display.monitor == sunxi_monitor_dvi ||
1122 sunxi_display.monitor == sunxi_monitor_hdmi) {
Hans de Goede0e045212014-12-21 14:49:34 +01001123 /* Always call hdp_detect, as it also enables clocks, etc. */
Priit Laes0220f8c2018-12-19 15:06:09 +02001124 hdmi_present = (sunxi_hdmi_hpd_detect(hpd_delay) == 1);
1125 if (hdmi_present && edid) {
Hans de Goede0e045212014-12-21 14:49:34 +01001126 printf("HDMI connected: ");
Priit Laes0220f8c2018-12-19 15:06:09 +02001127 if (sunxi_hdmi_edid_get_mode(&custom, true) == 0)
Hans de Goede2dae8002014-12-21 16:28:32 +01001128 mode = &custom;
Priit Laes0220f8c2018-12-19 15:06:09 +02001129 else
1130 hdmi_present = false;
1131 }
1132 /* Fall back to EDID in case HPD failed */
1133 if (edid && !hdmi_present) {
1134 if (sunxi_hdmi_edid_get_mode(&custom, false) == 0) {
1135 mode = &custom;
1136 hdmi_present = true;
1137 }
1138 }
1139 /* Shut down when display was not found */
1140 if ((hpd || edid) && !hdmi_present) {
Hans de Goede49d27032014-12-25 13:52:04 +01001141 sunxi_hdmi_shutdown();
Hans de Goedebf689342015-08-03 23:01:38 +02001142 sunxi_display.monitor = sunxi_get_default_mon(false);
Hans de Goede49d27032014-12-25 13:52:04 +01001143 } /* else continue with hdmi/dvi without a cable connected */
1144 }
1145#endif
Hans de Goede0e045212014-12-21 14:49:34 +01001146
Hans de Goede49d27032014-12-25 13:52:04 +01001147 switch (sunxi_display.monitor) {
1148 case sunxi_monitor_none:
1149 return NULL;
1150 case sunxi_monitor_dvi:
1151 case sunxi_monitor_hdmi:
Hans de Goedebf689342015-08-03 23:01:38 +02001152 if (!sunxi_has_hdmi()) {
1153 printf("HDMI/DVI not supported on this board\n");
1154 sunxi_display.monitor = sunxi_monitor_none;
1155 return NULL;
Hans de Goede2dae8002014-12-21 16:28:32 +01001156 }
Hans de Goedebf689342015-08-03 23:01:38 +02001157 break;
1158 case sunxi_monitor_lcd:
1159 if (!sunxi_has_lcd()) {
1160 printf("LCD not supported on this board\n");
1161 sunxi_display.monitor = sunxi_monitor_none;
1162 return NULL;
1163 }
1164 sunxi_display.depth = video_get_params(&custom, lcd_mode);
1165 mode = &custom;
1166 break;
Hans de Goede0e045212014-12-21 14:49:34 +01001167 case sunxi_monitor_vga:
Hans de Goedebf689342015-08-03 23:01:38 +02001168 if (!sunxi_has_vga()) {
1169 printf("VGA not supported on this board\n");
1170 sunxi_display.monitor = sunxi_monitor_none;
1171 return NULL;
1172 }
Hans de Goedee2bbdfb2014-12-24 12:17:07 +01001173 sunxi_display.depth = 18;
1174 break;
Hans de Goede39920c82015-08-03 19:20:26 +02001175 case sunxi_monitor_composite_pal:
1176 case sunxi_monitor_composite_ntsc:
1177 case sunxi_monitor_composite_pal_m:
1178 case sunxi_monitor_composite_pal_nc:
1179 if (!sunxi_has_composite()) {
1180 printf("Composite video not supported on this board\n");
1181 sunxi_display.monitor = sunxi_monitor_none;
1182 return NULL;
1183 }
1184 if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
1185 sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
1186 mode = &composite_video_modes[0];
1187 else
1188 mode = &composite_video_modes[1];
1189 sunxi_display.depth = 24;
1190 break;
Hans de Goede75481602014-12-19 16:05:12 +01001191 }
1192
Hans de Goede58332f82015-08-05 00:06:47 +02001193 /* Yes these defaults are quite high, overscan on composite sucks... */
1194 if (overscan_x == -1)
1195 overscan_x = sunxi_is_composite() ? 32 : 0;
1196 if (overscan_y == -1)
1197 overscan_y = sunxi_is_composite() ? 20 : 0;
1198
Hans de Goede20779ec2015-02-02 18:00:53 +01001199 sunxi_display.fb_size =
1200 (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
Hans de Goede58332f82015-08-05 00:06:47 +02001201 overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
1202 /* We want to keep the fb_base for simplefb page aligned, where as
1203 * the sunxi dma engines will happily accept an unaligned address. */
1204 if (overscan_offset)
1205 sunxi_display.fb_size += 0x1000;
1206
Hans de Goede20779ec2015-02-02 18:00:53 +01001207 if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
1208 printf("Error need %dkB for fb, but only %dkB is reserved\n",
1209 sunxi_display.fb_size >> 10,
1210 CONFIG_SUNXI_MAX_FB_SIZE >> 10);
1211 return NULL;
1212 }
1213
Hans de Goede58332f82015-08-05 00:06:47 +02001214 printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
1215 mode->xres, mode->yres,
Hans de Goedef6d9d322015-08-02 16:49:29 +02001216 (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
Hans de Goede58332f82015-08-05 00:06:47 +02001217 sunxi_get_mon_desc(sunxi_display.monitor),
1218 overscan_x, overscan_y);
Hans de Goedef6d9d322015-08-02 16:49:29 +02001219
Hans de Goede20779ec2015-02-02 18:00:53 +01001220 gd->fb_base = gd->bd->bi_dram[0].start +
1221 gd->bd->bi_dram[0].size - sunxi_display.fb_size;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001222 sunxi_engines_init();
Hans de Goede58332f82015-08-05 00:06:47 +02001223
Heinrich Schuchardtd06717f2018-03-03 10:30:17 +01001224#ifdef CONFIG_EFI_LOADER
1225 efi_add_memory_map(gd->fb_base,
1226 ALIGN(sunxi_display.fb_size, EFI_PAGE_SIZE) >>
1227 EFI_PAGE_SHIFT,
1228 EFI_RESERVED_MEMORY_TYPE, false);
1229#endif
1230
Hans de Goede58332f82015-08-05 00:06:47 +02001231 fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
1232 sunxi_display.fb_addr = gd->fb_base;
1233 if (overscan_offset) {
1234 fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
1235 sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
1236 memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
1237 flush_cache(gd->fb_base, sunxi_display.fb_size);
1238 }
1239 sunxi_mode_set(mode, fb_dma_addr);
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001240
1241 /*
1242 * These are the only members of this structure that are used. All the
Hans de Goede58332f82015-08-05 00:06:47 +02001243 * others are driver specific. The pitch is stored in plnSizeX.
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001244 */
Hans de Goede58332f82015-08-05 00:06:47 +02001245 graphic_device->frameAdrs = sunxi_display.fb_addr;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001246 graphic_device->gdfIndex = GDF_32BIT_X888RGB;
1247 graphic_device->gdfBytesPP = 4;
Hans de Goede58332f82015-08-05 00:06:47 +02001248 graphic_device->winSizeX = mode->xres - 2 * overscan_x;
1249 graphic_device->winSizeY = mode->yres - 2 * overscan_y;
1250 graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
Luc Verhaegen7f2c5212014-08-13 07:55:06 +02001251
1252 return graphic_device;
1253}
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001254
1255/*
1256 * Simplefb support.
1257 */
1258#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
1259int sunxi_simplefb_setup(void *blob)
1260{
1261 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
1262 int offset, ret;
Hans de Goede5633a292015-02-02 17:13:29 +01001263 u64 start, size;
Hans de Goede2dae8002014-12-21 16:28:32 +01001264 const char *pipeline = NULL;
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001265
Hans de Goede7cd6f922015-01-19 08:44:07 +01001266#ifdef CONFIG_MACH_SUN4I
1267#define PIPELINE_PREFIX "de_fe0-"
1268#else
1269#define PIPELINE_PREFIX
1270#endif
1271
Hans de Goede2dae8002014-12-21 16:28:32 +01001272 switch (sunxi_display.monitor) {
1273 case sunxi_monitor_none:
1274 return 0;
1275 case sunxi_monitor_dvi:
1276 case sunxi_monitor_hdmi:
Hans de Goede7cd6f922015-01-19 08:44:07 +01001277 pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi";
Hans de Goede2dae8002014-12-21 16:28:32 +01001278 break;
1279 case sunxi_monitor_lcd:
Hans de Goede7cd6f922015-01-19 08:44:07 +01001280 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede2dae8002014-12-21 16:28:32 +01001281 break;
1282 case sunxi_monitor_vga:
Hans de Goeded9786d22014-12-25 13:58:06 +01001283#ifdef CONFIG_VIDEO_VGA
Hans de Goede7cd6f922015-01-19 08:44:07 +01001284 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
Hans de Goeded9786d22014-12-25 13:58:06 +01001285#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goede7cd6f922015-01-19 08:44:07 +01001286 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goeded9786d22014-12-25 13:58:06 +01001287#endif
Hans de Goede2dae8002014-12-21 16:28:32 +01001288 break;
Hans de Goede39920c82015-08-03 19:20:26 +02001289 case sunxi_monitor_composite_pal:
1290 case sunxi_monitor_composite_ntsc:
1291 case sunxi_monitor_composite_pal_m:
1292 case sunxi_monitor_composite_pal_nc:
1293 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
1294 break;
Hans de Goede2dae8002014-12-21 16:28:32 +01001295 }
1296
Icenowy Zhenge5f92462017-10-26 11:14:45 +08001297 offset = sunxi_simplefb_fdt_match(blob, pipeline);
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001298 if (offset < 0) {
1299 eprintf("Cannot setup simplefb: node not found\n");
1300 return 0; /* Keep older kernels working */
1301 }
1302
Hans de Goede5633a292015-02-02 17:13:29 +01001303 /*
1304 * Do not report the framebuffer as free RAM to the OS, note we cannot
1305 * use fdt_add_mem_rsv() here, because then it is still seen as RAM,
1306 * and e.g. Linux refuses to iomap RAM on ARM, see:
1307 * linux/arch/arm/mm/ioremap.c around line 301.
1308 */
1309 start = gd->bd->bi_dram[0].start;
Hans de Goede20779ec2015-02-02 18:00:53 +01001310 size = gd->bd->bi_dram[0].size - sunxi_display.fb_size;
Hans de Goede5633a292015-02-02 17:13:29 +01001311 ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
1312 if (ret) {
1313 eprintf("Cannot setup simplefb: Error reserving memory\n");
1314 return ret;
1315 }
1316
Hans de Goede58332f82015-08-05 00:06:47 +02001317 ret = fdt_setup_simplefb_node(blob, offset, sunxi_display.fb_addr,
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001318 graphic_device->winSizeX, graphic_device->winSizeY,
Hans de Goede58332f82015-08-05 00:06:47 +02001319 graphic_device->plnSizeX, "x8r8g8b8");
Luc Verhaegen2d7a0842014-08-13 07:55:07 +02001320 if (ret)
1321 eprintf("Cannot setup simplefb: Error setting properties\n");
1322
1323 return ret;
1324}
1325#endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */