blob: f5deca35abc5bff90b9a11c3bdccd83ac3e91f04 [file] [log] [blame]
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001/*
2 * Display driver for Allwinner SoCs.
3 *
4 * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
Hans de Goedec06e00e2015-08-03 19:20:26 +02005 * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02006 *
7 * SPDX-License-Identifier: GPL-2.0+
8 */
9
10#include <common.h>
11
12#include <asm/arch/clock.h>
13#include <asm/arch/display.h>
Hans de Goede7e68a1b2014-12-21 16:28:32 +010014#include <asm/arch/gpio.h>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020015#include <asm/global_data.h>
Hans de Goede7e68a1b2014-12-21 16:28:32 +010016#include <asm/gpio.h>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020017#include <asm/io.h>
Hans de Goedea5aa95f2014-12-19 16:05:12 +010018#include <errno.h>
Luc Verhaegen4869a8c2014-08-13 07:55:07 +020019#include <fdtdec.h>
20#include <fdt_support.h>
Hans de Goede613dade2015-02-16 17:49:47 +010021#include <i2c.h>
Hans de Goeded955f442015-08-05 00:06:47 +020022#include <malloc.h>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020023#include <video_fb.h>
Hans de Goedeccb0ed52014-12-19 13:46:33 +010024#include "videomodes.h"
Hans de Goede743fb9552015-01-20 09:23:36 +010025#include "hitachi_tx18d42vm_lcd.h"
Siarhei Siamashkac02f0522015-01-19 05:23:33 +020026#include "ssd2828.h"
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020027
Hans de Goede2d5d3022015-01-22 21:02:42 +010028#ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW
29#define PWM_ON 0
30#define PWM_OFF 1
31#else
32#define PWM_ON 1
33#define PWM_OFF 0
34#endif
35
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020036DECLARE_GLOBAL_DATA_PTR;
37
Hans de Goedea0b1b732014-12-21 14:37:45 +010038enum sunxi_monitor {
39 sunxi_monitor_none,
40 sunxi_monitor_dvi,
41 sunxi_monitor_hdmi,
42 sunxi_monitor_lcd,
43 sunxi_monitor_vga,
Hans de Goedec06e00e2015-08-03 19:20:26 +020044 sunxi_monitor_composite_pal,
45 sunxi_monitor_composite_ntsc,
46 sunxi_monitor_composite_pal_m,
47 sunxi_monitor_composite_pal_nc,
Hans de Goedea0b1b732014-12-21 14:37:45 +010048};
Hans de Goedec06e00e2015-08-03 19:20:26 +020049#define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
Hans de Goedea0b1b732014-12-21 14:37:45 +010050
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020051struct sunxi_display {
52 GraphicDevice graphic_device;
Hans de Goedea0b1b732014-12-21 14:37:45 +010053 enum sunxi_monitor monitor;
Hans de Goede7e68a1b2014-12-21 16:28:32 +010054 unsigned int depth;
Hans de Goeded955f442015-08-05 00:06:47 +020055 unsigned int fb_addr;
Hans de Goede18799252015-02-02 18:00:53 +010056 unsigned int fb_size;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020057} sunxi_display;
58
Hans de Goedec06e00e2015-08-03 19:20:26 +020059const struct ctfb_res_modes composite_video_modes[2] = {
60 /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */
61 { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED },
62 { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED },
63};
64
Hans de Goedee9544592014-12-23 23:04:35 +010065#ifdef CONFIG_VIDEO_HDMI
66
Hans de Goedea5aa95f2014-12-19 16:05:12 +010067/*
68 * Wait up to 200ms for value to be set in given part of reg.
69 */
70static int await_completion(u32 *reg, u32 mask, u32 val)
71{
72 unsigned long tmo = timer_get_us() + 200000;
73
74 while ((readl(reg) & mask) != val) {
75 if (timer_get_us() > tmo) {
76 printf("DDC: timeout reading EDID\n");
77 return -ETIME;
78 }
79 }
80 return 0;
81}
82
Hans de Goede91593712014-12-28 09:13:21 +010083static int sunxi_hdmi_hpd_detect(int hpd_delay)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020084{
85 struct sunxi_ccm_reg * const ccm =
86 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
87 struct sunxi_hdmi_reg * const hdmi =
88 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Hans de Goede91593712014-12-28 09:13:21 +010089 unsigned long tmo = timer_get_us() + hpd_delay * 1000;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020090
91 /* Set pll3 to 300MHz */
92 clock_set_pll3(300000000);
93
94 /* Set hdmi parent to pll3 */
95 clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
96 CCM_HDMI_CTRL_PLL3);
97
98 /* Set ahb gating to pass */
Hans de Goedef07872b2015-04-06 20:33:34 +020099#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100100 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
101#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200102 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
103
104 /* Clock on */
105 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
106
107 writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl);
108 writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0);
109
Hans de Goede205a30c2014-12-20 15:15:23 +0100110 while (timer_get_us() < tmo) {
111 if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT)
112 return 1;
113 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200114
Hans de Goede205a30c2014-12-20 15:15:23 +0100115 return 0;
Hans de Goede695bda42014-12-19 15:13:57 +0100116}
117
118static void sunxi_hdmi_shutdown(void)
119{
120 struct sunxi_ccm_reg * const ccm =
121 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
122 struct sunxi_hdmi_reg * const hdmi =
123 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200124
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200125 clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE);
126 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
127 clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
Hans de Goedef07872b2015-04-06 20:33:34 +0200128#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100129 clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
130#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200131 clock_set_pll3(0);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200132}
133
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100134static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
135{
136 struct sunxi_hdmi_reg * const hdmi =
137 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
138
139 setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
140 writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
141 SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
142 SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
143 SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
144#ifndef CONFIG_MACH_SUN6I
145 writel(n, &hdmi->ddc_byte_count);
146 writel(cmnd, &hdmi->ddc_cmnd);
147#else
148 writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
149#endif
150 setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
151
152 return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
153}
154
155static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
156{
157 struct sunxi_hdmi_reg * const hdmi =
158 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
159 int i, n;
160
161 while (count > 0) {
162 if (count > 16)
163 n = 16;
164 else
165 n = count;
166
167 if (sunxi_hdmi_ddc_do_command(
168 SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
169 offset, n))
170 return -ETIME;
171
172 for (i = 0; i < n; i++)
173 *buf++ = readb(&hdmi->ddc_fifo_data);
174
175 offset += n;
176 count -= n;
177 }
178
179 return 0;
180}
181
Hans de Goede45b8f7b2014-12-20 14:01:48 +0100182static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
183{
184 int r, retries = 2;
185
186 do {
187 r = sunxi_hdmi_ddc_read(block * 128, buf, 128);
188 if (r)
189 continue;
190 r = edid_check_checksum(buf);
191 if (r) {
192 printf("EDID block %d: checksum error%s\n",
193 block, retries ? ", retrying" : "");
194 }
195 } while (r && retries--);
196
197 return r;
198}
199
Hans de Goedea0b1b732014-12-21 14:37:45 +0100200static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode)
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100201{
202 struct edid1_info edid1;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100203 struct edid_cea861_info cea681[4];
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100204 struct edid_detailed_timing *t =
205 (struct edid_detailed_timing *)edid1.monitor_details.timing;
206 struct sunxi_hdmi_reg * const hdmi =
207 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
208 struct sunxi_ccm_reg * const ccm =
209 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100210 int i, r, ext_blocks = 0;
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100211
212 /* SUNXI_HDMI_CTRL_ENABLE & PAD_CTRL0 are already set by hpd_detect */
213 writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
214 &hdmi->pad_ctrl1);
215 writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
216 &hdmi->pll_ctrl);
217 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
218
219 /* Reset i2c controller */
220 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
221 writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
222 SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
223 SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
224 SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
225 if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
226 return -EIO;
227
228 writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
229#ifndef CONFIG_MACH_SUN6I
230 writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
231 SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
232#endif
233
Hans de Goede45b8f7b2014-12-20 14:01:48 +0100234 r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100235 if (r == 0) {
236 r = edid_check_info(&edid1);
237 if (r) {
238 printf("EDID: invalid EDID data\n");
239 r = -EINVAL;
240 }
241 }
242 if (r == 0) {
243 ext_blocks = edid1.extension_flag;
244 if (ext_blocks > 4)
245 ext_blocks = 4;
246 for (i = 0; i < ext_blocks; i++) {
247 if (sunxi_hdmi_edid_get_block(1 + i,
248 (u8 *)&cea681[i]) != 0) {
249 ext_blocks = i;
250 break;
251 }
252 }
253 }
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100254
255 /* Disable DDC engine, no longer needed */
256 clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
257 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
258
259 if (r)
260 return r;
261
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100262 /* We want version 1.3 or 1.2 with detailed timing info */
263 if (edid1.version != 1 || (edid1.revision < 3 &&
264 !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
265 printf("EDID: unsupported version %d.%d\n",
266 edid1.version, edid1.revision);
267 return -EINVAL;
268 }
269
270 /* Take the first usable detailed timing */
271 for (i = 0; i < 4; i++, t++) {
272 r = video_edid_dtd_to_ctfb_res_modes(t, mode);
273 if (r == 0)
274 break;
275 }
276 if (i == 4) {
277 printf("EDID: no usable detailed timing found\n");
278 return -ENOENT;
279 }
280
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100281 /* Check for basic audio support, if found enable hdmi output */
Hans de Goedea0b1b732014-12-21 14:37:45 +0100282 sunxi_display.monitor = sunxi_monitor_dvi;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100283 for (i = 0; i < ext_blocks; i++) {
284 if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
285 cea681[i].revision < 2)
286 continue;
287
288 if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
Hans de Goedea0b1b732014-12-21 14:37:45 +0100289 sunxi_display.monitor = sunxi_monitor_hdmi;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100290 }
291
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100292 return 0;
293}
294
Hans de Goedee9544592014-12-23 23:04:35 +0100295#endif /* CONFIG_VIDEO_HDMI */
296
Hans de Goedec3cc4262015-01-19 08:44:07 +0100297#ifdef CONFIG_MACH_SUN4I
298/*
299 * Testing has shown that on sun4i the display backend engine does not have
300 * deep enough fifo-s causing flickering / tearing in full-hd mode due to
301 * fifo underruns. So on sun4i we use the display frontend engine to do the
302 * dma from memory, as the frontend does have deep enough fifo-s.
303 */
304
305static const u32 sun4i_vert_coef[32] = {
306 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
307 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
308 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
309 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
310 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
311 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
312 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
313 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
314};
315
316static const u32 sun4i_horz_coef[64] = {
317 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
318 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
319 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
320 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
321 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
322 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
323 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
324 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
325 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
326 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
327 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
328 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
329 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
330 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
331 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
332 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
333};
334
335static void sunxi_frontend_init(void)
336{
337 struct sunxi_ccm_reg * const ccm =
338 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
339 struct sunxi_de_fe_reg * const de_fe =
340 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
341 int i;
342
343 /* Clocks on */
344 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0);
345 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0);
346 clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000);
347
348 setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN);
349
350 for (i = 0; i < 32; i++) {
351 writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]);
352 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]);
353 writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]);
354 writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]);
355 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]);
356 writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]);
357 }
358
359 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY);
360}
361
362static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
363 unsigned int address)
364{
365 struct sunxi_de_fe_reg * const de_fe =
366 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
367
368 setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS);
369 writel(CONFIG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr);
370 writel(mode->xres * 4, &de_fe->ch0_stride);
371 writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt);
372 writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt);
373
374 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
375 &de_fe->ch0_insize);
376 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
377 &de_fe->ch0_outsize);
378 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact);
379 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact);
380
381 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
382 &de_fe->ch1_insize);
383 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
384 &de_fe->ch1_outsize);
385 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact);
386 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact);
387
388 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY);
389}
390
391static void sunxi_frontend_enable(void)
392{
393 struct sunxi_de_fe_reg * const de_fe =
394 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
395
396 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START);
397}
398#else
399static void sunxi_frontend_init(void) {}
400static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
401 unsigned int address) {}
402static void sunxi_frontend_enable(void) {}
403#endif
404
Hans de Goedec06e00e2015-08-03 19:20:26 +0200405static bool sunxi_is_composite(void)
406{
407 switch (sunxi_display.monitor) {
408 case sunxi_monitor_none:
409 case sunxi_monitor_dvi:
410 case sunxi_monitor_hdmi:
411 case sunxi_monitor_lcd:
412 case sunxi_monitor_vga:
413 return false;
414 case sunxi_monitor_composite_pal:
415 case sunxi_monitor_composite_ntsc:
416 case sunxi_monitor_composite_pal_m:
417 case sunxi_monitor_composite_pal_nc:
418 return true;
419 }
420
421 return false; /* Never reached */
422}
423
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200424/*
425 * This is the entity that mixes and matches the different layers and inputs.
426 * Allwinner calls it the back-end, but i like composer better.
427 */
428static void sunxi_composer_init(void)
429{
430 struct sunxi_ccm_reg * const ccm =
431 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
432 struct sunxi_de_be_reg * const de_be =
433 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
434 int i;
435
Hans de Goedec3cc4262015-01-19 08:44:07 +0100436 sunxi_frontend_init();
437
Hans de Goedef07872b2015-04-06 20:33:34 +0200438#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100439 /* Reset off */
440 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0);
441#endif
442
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200443 /* Clocks on */
444 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100445#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200446 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100447#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200448 clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000);
449
450 /* Engine bug, clear registers after reset */
451 for (i = 0x0800; i < 0x1000; i += 4)
452 writel(0, SUNXI_DE_BE0_BASE + i);
453
454 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE);
455}
456
Hans de Goedec06e00e2015-08-03 19:20:26 +0200457static u32 sunxi_rgb2yuv_coef[12] = {
458 0x00000107, 0x00000204, 0x00000064, 0x00000108,
459 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,
460 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
461};
462
Hans de Goedeccb0ed52014-12-19 13:46:33 +0100463static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200464 unsigned int address)
465{
466 struct sunxi_de_be_reg * const de_be =
467 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
Hans de Goedec06e00e2015-08-03 19:20:26 +0200468 int i;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200469
Hans de Goedec3cc4262015-01-19 08:44:07 +0100470 sunxi_frontend_mode_set(mode, address);
471
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200472 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
473 &de_be->disp_size);
474 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
475 &de_be->layer0_size);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100476#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200477 writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride);
478 writel(address << 3, &de_be->layer0_addr_low32b);
479 writel(address >> 29, &de_be->layer0_addr_high4b);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100480#else
481 writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl);
482#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200483 writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl);
484
485 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE);
Hans de Goede1977bbb2015-08-02 16:49:29 +0200486 if (mode->vmode == FB_VMODE_INTERLACED)
487 setbits_le32(&de_be->mode,
Hans de Goede8a195ca2015-08-06 12:08:33 +0200488#ifndef CONFIG_MACH_SUN5I
Hans de Goede1977bbb2015-08-02 16:49:29 +0200489 SUNXI_DE_BE_MODE_DEFLICKER_ENABLE |
Hans de Goede8a195ca2015-08-06 12:08:33 +0200490#endif
Hans de Goede1977bbb2015-08-02 16:49:29 +0200491 SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200492
493 if (sunxi_is_composite()) {
494 writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
495 &de_be->output_color_ctrl);
496 for (i = 0; i < 12; i++)
497 writel(sunxi_rgb2yuv_coef[i],
498 &de_be->output_color_coef[i]);
499 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200500}
501
Hans de Goede4125f922014-12-21 14:49:34 +0100502static void sunxi_composer_enable(void)
503{
504 struct sunxi_de_be_reg * const de_be =
505 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
506
Hans de Goedec3cc4262015-01-19 08:44:07 +0100507 sunxi_frontend_enable();
508
Hans de Goede4125f922014-12-21 14:49:34 +0100509 setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS);
510 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
511}
512
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200513/*
514 * LCDC, what allwinner calls a CRTC, so timing controller and serializer.
515 */
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100516static void sunxi_lcdc_pll_set(int tcon, int dotclock,
517 int *clk_div, int *clk_double)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200518{
519 struct sunxi_ccm_reg * const ccm =
520 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100521 int value, n, m, min_m, max_m, diff;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200522 int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF;
523 int best_double = 0;
524
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100525 if (tcon == 0) {
Hans de Goede797a0f52015-01-01 22:04:34 +0100526#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100527 min_m = 6;
528 max_m = 127;
Hans de Goede797a0f52015-01-01 22:04:34 +0100529#endif
530#ifdef CONFIG_VIDEO_LCD_IF_LVDS
531 min_m = max_m = 7;
532#endif
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100533 } else {
534 min_m = 1;
535 max_m = 15;
536 }
537
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200538 /*
539 * Find the lowest divider resulting in a matching clock, if there
540 * is no match, pick the closest lower clock, as monitors tend to
541 * not sync to higher frequencies.
542 */
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100543 for (m = min_m; m <= max_m; m++) {
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200544 n = (m * dotclock) / 3000;
545
546 if ((n >= 9) && (n <= 127)) {
547 value = (3000 * n) / m;
548 diff = dotclock - value;
549 if (diff < best_diff) {
550 best_diff = diff;
551 best_m = m;
552 best_n = n;
553 best_double = 0;
554 }
555 }
556
557 /* These are just duplicates */
558 if (!(m & 1))
559 continue;
560
561 n = (m * dotclock) / 6000;
562 if ((n >= 9) && (n <= 127)) {
563 value = (6000 * n) / m;
564 diff = dotclock - value;
565 if (diff < best_diff) {
566 best_diff = diff;
567 best_m = m;
568 best_n = n;
569 best_double = 1;
570 }
571 }
572 }
573
574 debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n",
575 dotclock, (best_double + 1) * 3000 * best_n / best_m,
576 best_double + 1, best_n, best_m);
577
578 clock_set_pll3(best_n * 3000000);
579
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100580 if (tcon == 0) {
581 writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST |
582 (best_double ? CCM_LCD_CH0_CTRL_PLL3_2X :
583 CCM_LCD_CH0_CTRL_PLL3),
584 &ccm->lcd0_ch0_clk_cfg);
585 } else {
586 writel(CCM_LCD_CH1_CTRL_GATE |
587 (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X :
588 CCM_LCD_CH1_CTRL_PLL3) |
589 CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg);
Hans de Goedec06e00e2015-08-03 19:20:26 +0200590 if (sunxi_is_composite())
591 setbits_le32(&ccm->lcd0_ch1_clk_cfg,
592 CCM_LCD_CH1_CTRL_HALF_SCLK1);
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100593 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200594
595 *clk_div = best_m;
596 *clk_double = best_double;
597}
598
599static void sunxi_lcdc_init(void)
600{
601 struct sunxi_ccm_reg * const ccm =
602 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
603 struct sunxi_lcdc_reg * const lcdc =
604 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
605
606 /* Reset off */
Hans de Goedef07872b2015-04-06 20:33:34 +0200607#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100608 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
609#else
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200610 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST);
Hans de Goedef651e0a2014-11-14 17:42:14 +0100611#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200612
613 /* Clock on */
614 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100615#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Hans de Goede5f67b862015-05-14 18:52:54 +0200616#ifdef CONFIG_SUNXI_GEN_SUN6I
617 setbits_le32(&ccm->ahb_reset2_cfg, 1 << AHB_RESET_OFFSET_LVDS);
618#else
Hans de Goede797a0f52015-01-01 22:04:34 +0100619 setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
620#endif
Hans de Goede5f67b862015-05-14 18:52:54 +0200621#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200622
623 /* Init lcdc */
624 writel(0, &lcdc->ctrl); /* Disable tcon */
625 writel(0, &lcdc->int0); /* Disable all interrupts */
626
627 /* Disable tcon0 dot clock */
628 clrbits_le32(&lcdc->tcon0_dclk, SUNXI_LCDC_TCON0_DCLK_ENABLE);
629
630 /* Set all io lines to tristate */
631 writel(0xffffffff, &lcdc->tcon0_io_tristate);
632 writel(0xffffffff, &lcdc->tcon1_io_tristate);
633}
634
Hans de Goede4125f922014-12-21 14:49:34 +0100635static void sunxi_lcdc_enable(void)
636{
637 struct sunxi_lcdc_reg * const lcdc =
638 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
639
640 setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
Hans de Goede797a0f52015-01-01 22:04:34 +0100641#ifdef CONFIG_VIDEO_LCD_IF_LVDS
642 setbits_le32(&lcdc->tcon0_lvds_intf, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE);
643 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0);
Hans de Goede5f67b862015-05-14 18:52:54 +0200644#ifdef CONFIG_SUNXI_GEN_SUN6I
645 udelay(2); /* delay at least 1200 ns */
646 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_EN_MB);
647 udelay(2); /* delay at least 1200 ns */
648 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVC);
649 if (sunxi_display.depth == 18)
650 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0x7));
651 else
652 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0xf));
653#else
Hans de Goede797a0f52015-01-01 22:04:34 +0100654 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
655 udelay(2); /* delay at least 1200 ns */
656 setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT1);
657 udelay(1); /* delay at least 120 ns */
658 setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT2);
659 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
660#endif
Hans de Goede5f67b862015-05-14 18:52:54 +0200661#endif
Hans de Goede4125f922014-12-21 14:49:34 +0100662}
663
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100664static void sunxi_lcdc_panel_enable(void)
665{
Hans de Goedece9e3322015-02-16 17:26:41 +0100666 int pin, reset_pin;
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100667
668 /*
669 * Start with backlight disabled to avoid the screen flashing to
670 * white while the lcd inits.
671 */
672 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede7783ab22015-04-22 17:45:59 +0200673 if (pin >= 0) {
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100674 gpio_request(pin, "lcd_backlight_enable");
675 gpio_direction_output(pin, 0);
676 }
677
678 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede7783ab22015-04-22 17:45:59 +0200679 if (pin >= 0) {
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100680 gpio_request(pin, "lcd_backlight_pwm");
Hans de Goede2d5d3022015-01-22 21:02:42 +0100681 gpio_direction_output(pin, PWM_OFF);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100682 }
683
Hans de Goedece9e3322015-02-16 17:26:41 +0100684 reset_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_RESET);
Hans de Goede7783ab22015-04-22 17:45:59 +0200685 if (reset_pin >= 0) {
Hans de Goedece9e3322015-02-16 17:26:41 +0100686 gpio_request(reset_pin, "lcd_reset");
687 gpio_direction_output(reset_pin, 0); /* Assert reset */
688 }
689
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100690 /* Give the backlight some time to turn off and power up the panel. */
691 mdelay(40);
692 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
Hans de Goede7783ab22015-04-22 17:45:59 +0200693 if (pin >= 0) {
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100694 gpio_request(pin, "lcd_power");
695 gpio_direction_output(pin, 1);
696 }
Hans de Goedece9e3322015-02-16 17:26:41 +0100697
Hans de Goede7783ab22015-04-22 17:45:59 +0200698 if (reset_pin >= 0)
Hans de Goedece9e3322015-02-16 17:26:41 +0100699 gpio_direction_output(reset_pin, 1); /* De-assert reset */
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100700}
701
702static void sunxi_lcdc_backlight_enable(void)
703{
704 int pin;
705
706 /*
707 * We want to have scanned out at least one frame before enabling the
708 * backlight to avoid the screen flashing to white when we enable it.
709 */
710 mdelay(40);
711
712 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
Hans de Goede7783ab22015-04-22 17:45:59 +0200713 if (pin >= 0)
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100714 gpio_direction_output(pin, 1);
715
716 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
Hans de Goede7783ab22015-04-22 17:45:59 +0200717 if (pin >= 0)
Hans de Goede2d5d3022015-01-22 21:02:42 +0100718 gpio_direction_output(pin, PWM_ON);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100719}
720
Hans de Goede3cbcb272015-08-02 17:38:43 +0200721static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode, int tcon)
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100722{
723 int delay;
724
Hans de Goede3cbcb272015-08-02 17:38:43 +0200725 delay = mode->lower_margin + mode->vsync_len + mode->upper_margin;
Hans de Goede1977bbb2015-08-02 16:49:29 +0200726 if (mode->vmode == FB_VMODE_INTERLACED)
727 delay /= 2;
Hans de Goede3cbcb272015-08-02 17:38:43 +0200728 if (tcon == 1)
729 delay -= 2;
730
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100731 return (delay > 30) ? 30 : delay;
732}
733
Hans de Goede18366f72015-01-25 15:33:07 +0100734static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
735 bool for_ext_vga_dac)
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100736{
737 struct sunxi_lcdc_reg * const lcdc =
738 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
739 int bp, clk_delay, clk_div, clk_double, pin, total, val;
740
741 for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++)
Hans de Goede797a0f52015-01-01 22:04:34 +0100742#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Paul Kocialkowskiae358a42015-03-22 18:12:22 +0100743 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100744#endif
745#ifdef CONFIG_VIDEO_LCD_IF_LVDS
Paul Kocialkowskiae358a42015-03-22 18:12:22 +0100746 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LVDS0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100747#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100748
749 sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
750
751 /* Use tcon0 */
752 clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
753 SUNXI_LCDC_CTRL_IO_MAP_TCON0);
754
Hans de Goede3cbcb272015-08-02 17:38:43 +0200755 clk_delay = sunxi_lcdc_get_clk_delay(mode, 0);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100756 writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
757 SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
758
759 writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
760 SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
761
762 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
763 &lcdc->tcon0_timing_active);
764
765 bp = mode->hsync_len + mode->left_margin;
766 total = mode->xres + mode->right_margin + bp;
767 writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
768 SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
769
770 bp = mode->vsync_len + mode->upper_margin;
771 total = mode->yres + mode->lower_margin + bp;
772 writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
773 SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
774
Hans de Goede797a0f52015-01-01 22:04:34 +0100775#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100776 writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
777 &lcdc->tcon0_timing_sync);
778
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100779 writel(0, &lcdc->tcon0_hv_intf);
780 writel(0, &lcdc->tcon0_cpu_intf);
Hans de Goede797a0f52015-01-01 22:04:34 +0100781#endif
782#ifdef CONFIG_VIDEO_LCD_IF_LVDS
783 val = (sunxi_display.depth == 18) ? 1 : 0;
Hans de Goede5f67b862015-05-14 18:52:54 +0200784 writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val) |
785 SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0, &lcdc->tcon0_lvds_intf);
Hans de Goede797a0f52015-01-01 22:04:34 +0100786#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100787
788 if (sunxi_display.depth == 18 || sunxi_display.depth == 16) {
789 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
790 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]);
791 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]);
792 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]);
793 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]);
794 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]);
795 writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]);
796 writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]);
797 writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]);
798 writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]);
799 writel(((sunxi_display.depth == 18) ?
800 SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 :
801 SUNXI_LCDC_TCON0_FRM_CTRL_RGB565),
802 &lcdc->tcon0_frm_ctrl);
803 }
804
Hans de Goede481b6642015-01-13 13:21:46 +0100805 val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(CONFIG_VIDEO_LCD_DCLK_PHASE);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100806 if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
807 val |= SUNXI_LCDC_TCON_HSYNC_MASK;
808 if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
809 val |= SUNXI_LCDC_TCON_VSYNC_MASK;
Hans de Goede18366f72015-01-25 15:33:07 +0100810
811#ifdef CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH
812 if (for_ext_vga_dac)
813 val = 0;
814#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100815 writel(val, &lcdc->tcon0_io_polarity);
816
817 writel(0, &lcdc->tcon0_io_tristate);
818}
819
Hans de Goedec06e00e2015-08-03 19:20:26 +0200820#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goede4125f922014-12-21 14:49:34 +0100821static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
Hans de Goedec3d15042014-12-27 15:19:23 +0100822 int *clk_div, int *clk_double,
823 bool use_portd_hvsync)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200824{
825 struct sunxi_lcdc_reg * const lcdc =
826 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Hans de Goede1977bbb2015-08-02 16:49:29 +0200827 int bp, clk_delay, total, val, yres;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200828
829 /* Use tcon1 */
830 clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
831 SUNXI_LCDC_CTRL_IO_MAP_TCON1);
832
Hans de Goede3cbcb272015-08-02 17:38:43 +0200833 clk_delay = sunxi_lcdc_get_clk_delay(mode, 1);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200834 writel(SUNXI_LCDC_TCON1_CTRL_ENABLE |
Hans de Goede1977bbb2015-08-02 16:49:29 +0200835 ((mode->vmode == FB_VMODE_INTERLACED) ?
836 SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE : 0) |
Hans de Goedeac5d43d2014-12-24 19:50:11 +0100837 SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200838
Hans de Goede1977bbb2015-08-02 16:49:29 +0200839 yres = mode->yres;
840 if (mode->vmode == FB_VMODE_INTERLACED)
841 yres /= 2;
842 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200843 &lcdc->tcon1_timing_source);
Hans de Goede1977bbb2015-08-02 16:49:29 +0200844 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200845 &lcdc->tcon1_timing_scale);
Hans de Goede1977bbb2015-08-02 16:49:29 +0200846 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200847 &lcdc->tcon1_timing_out);
848
849 bp = mode->hsync_len + mode->left_margin;
850 total = mode->xres + mode->right_margin + bp;
851 writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) |
852 SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h);
853
854 bp = mode->vsync_len + mode->upper_margin;
855 total = mode->yres + mode->lower_margin + bp;
Hans de Goede1977bbb2015-08-02 16:49:29 +0200856 if (mode->vmode == FB_VMODE_NONINTERLACED)
857 total *= 2;
858 writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) |
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200859 SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v);
860
861 writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
862 &lcdc->tcon1_timing_sync);
863
Hans de Goedec3d15042014-12-27 15:19:23 +0100864 if (use_portd_hvsync) {
Paul Kocialkowskiae358a42015-03-22 18:12:22 +0100865 sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
866 sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0);
Hans de Goedec3d15042014-12-27 15:19:23 +0100867
868 val = 0;
869 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
870 val |= SUNXI_LCDC_TCON_HSYNC_MASK;
871 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
872 val |= SUNXI_LCDC_TCON_VSYNC_MASK;
873 writel(val, &lcdc->tcon1_io_polarity);
874
875 clrbits_le32(&lcdc->tcon1_io_tristate,
876 SUNXI_LCDC_TCON_VSYNC_MASK |
877 SUNXI_LCDC_TCON_HSYNC_MASK);
878 }
Hans de Goede8a195ca2015-08-06 12:08:33 +0200879
880#ifdef CONFIG_MACH_SUN5I
881 if (sunxi_is_composite())
882 clrsetbits_le32(&lcdc->mux_ctrl, SUNXI_LCDC_MUX_CTRL_SRC0_MASK,
883 SUNXI_LCDC_MUX_CTRL_SRC0(1));
884#endif
885
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100886 sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200887}
Hans de Goedec06e00e2015-08-03 19:20:26 +0200888#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
Hans de Goede260f5202014-12-25 13:58:06 +0100889
890#ifdef CONFIG_VIDEO_HDMI
Hans de Goedef651e0a2014-11-14 17:42:14 +0100891
Hans de Goedea2017e82014-12-20 13:38:06 +0100892static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
893{
894 struct sunxi_hdmi_reg * const hdmi =
895 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
896 u8 checksum = 0;
897 u8 avi_info_frame[17] = {
898 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00,
899 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
900 0x00
901 };
902 u8 vendor_info_frame[19] = {
903 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40,
904 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
905 0x00, 0x00, 0x00
906 };
907 int i;
908
909 if (mode->pixclock_khz <= 27000)
910 avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */
911 else
912 avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */
913
914 if (mode->xres * 100 / mode->yres < 156)
915 avi_info_frame[5] |= 0x18; /* 4 : 3 */
916 else
917 avi_info_frame[5] |= 0x28; /* 16 : 9 */
918
919 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
920 checksum += avi_info_frame[i];
921
922 avi_info_frame[3] = 0x100 - checksum;
923
924 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
925 writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]);
926
927 writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0);
928 writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1);
929
930 for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++)
931 writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]);
932
933 writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0);
934 writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1);
935
936 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI);
937}
938
Hans de Goedeccb0ed52014-12-19 13:46:33 +0100939static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
Hans de Goedea0b1b732014-12-21 14:37:45 +0100940 int clk_div, int clk_double)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200941{
942 struct sunxi_hdmi_reg * const hdmi =
943 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
944 int x, y;
945
946 /* Write clear interrupt status bits */
947 writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq);
948
Hans de Goedea0b1b732014-12-21 14:37:45 +0100949 if (sunxi_display.monitor == sunxi_monitor_hdmi)
Hans de Goedea2017e82014-12-20 13:38:06 +0100950 sunxi_hdmi_setup_info_frames(mode);
951
Hans de Goede95576692014-12-20 13:51:16 +0100952 /* Set input sync enable */
953 writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown);
954
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200955 /* Init various registers, select pll3 as clock source */
956 writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity);
957 writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0);
958 writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1);
959 writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl);
960 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
961
962 /* Setup clk div and doubler */
963 clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK,
964 SUNXI_HDMI_PLL_CTRL_DIV(clk_div));
965 if (!clk_double)
966 setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE);
967
968 /* Setup timing registers */
969 writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres),
970 &hdmi->video_size);
971
972 x = mode->hsync_len + mode->left_margin;
973 y = mode->vsync_len + mode->upper_margin;
974 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp);
975
976 x = mode->right_margin;
977 y = mode->lower_margin;
978 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp);
979
980 x = mode->hsync_len;
981 y = mode->vsync_len;
982 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw);
983
984 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
985 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR);
986
987 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
988 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER);
989}
990
Hans de Goede4125f922014-12-21 14:49:34 +0100991static void sunxi_hdmi_enable(void)
992{
993 struct sunxi_hdmi_reg * const hdmi =
994 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
995
996 udelay(100);
997 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE);
998}
999
Hans de Goedee9544592014-12-23 23:04:35 +01001000#endif /* CONFIG_VIDEO_HDMI */
1001
Hans de Goedec06e00e2015-08-03 19:20:26 +02001002#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
Hans de Goede260f5202014-12-25 13:58:06 +01001003
Hans de Goedec06e00e2015-08-03 19:20:26 +02001004static void sunxi_tvencoder_mode_set(void)
Hans de Goede260f5202014-12-25 13:58:06 +01001005{
1006 struct sunxi_ccm_reg * const ccm =
1007 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
1008 struct sunxi_tve_reg * const tve =
1009 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
1010
Hans de Goede8a195ca2015-08-06 12:08:33 +02001011 /* Reset off */
1012 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_TVE_RST);
Hans de Goede260f5202014-12-25 13:58:06 +01001013 /* Clock on */
1014 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
1015
Hans de Goedec06e00e2015-08-03 19:20:26 +02001016 switch (sunxi_display.monitor) {
1017 case sunxi_monitor_vga:
1018 writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
1019 SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
1020 SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl);
1021 writel(SUNXI_TVE_CFG0_VGA, &tve->cfg0);
1022 writel(SUNXI_TVE_DAC_CFG0_VGA, &tve->dac_cfg0);
1023 writel(SUNXI_TVE_UNKNOWN1_VGA, &tve->unknown1);
1024 break;
1025 case sunxi_monitor_composite_pal_nc:
1026 writel(SUNXI_TVE_CHROMA_FREQ_PAL_NC, &tve->chroma_freq);
1027 /* Fall through */
1028 case sunxi_monitor_composite_pal:
1029 writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
1030 SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
1031 SUNXI_TVE_GCTRL_DAC_INPUT(2, 3) |
1032 SUNXI_TVE_GCTRL_DAC_INPUT(3, 4), &tve->gctrl);
1033 writel(SUNXI_TVE_CFG0_PAL, &tve->cfg0);
1034 writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0);
1035 writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter);
1036 writel(SUNXI_TVE_PORCH_NUM_PAL, &tve->porch_num);
1037 writel(SUNXI_TVE_LINE_NUM_PAL, &tve->line_num);
1038 writel(SUNXI_TVE_BLANK_BLACK_LEVEL_PAL, &tve->blank_black_level);
1039 writel(SUNXI_TVE_UNKNOWN1_COMPOSITE, &tve->unknown1);
1040 writel(SUNXI_TVE_CBR_LEVEL_PAL, &tve->cbr_level);
1041 writel(SUNXI_TVE_BURST_WIDTH_COMPOSITE, &tve->burst_width);
1042 writel(SUNXI_TVE_UNKNOWN2_PAL, &tve->unknown2);
1043 writel(SUNXI_TVE_ACTIVE_NUM_COMPOSITE, &tve->active_num);
1044 writel(SUNXI_TVE_CHROMA_BW_GAIN_COMP, &tve->chroma_bw_gain);
1045 writel(SUNXI_TVE_NOTCH_WIDTH_COMPOSITE, &tve->notch_width);
1046 writel(SUNXI_TVE_RESYNC_NUM_PAL, &tve->resync_num);
1047 writel(SUNXI_TVE_SLAVE_PARA_COMPOSITE, &tve->slave_para);
1048 break;
1049 case sunxi_monitor_composite_pal_m:
1050 writel(SUNXI_TVE_CHROMA_FREQ_PAL_M, &tve->chroma_freq);
1051 writel(SUNXI_TVE_COLOR_BURST_PAL_M, &tve->color_burst);
1052 /* Fall through */
1053 case sunxi_monitor_composite_ntsc:
1054 writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
1055 SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
1056 SUNXI_TVE_GCTRL_DAC_INPUT(2, 3) |
1057 SUNXI_TVE_GCTRL_DAC_INPUT(3, 4), &tve->gctrl);
1058 writel(SUNXI_TVE_CFG0_NTSC, &tve->cfg0);
1059 writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0);
1060 writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter);
1061 writel(SUNXI_TVE_PORCH_NUM_NTSC, &tve->porch_num);
1062 writel(SUNXI_TVE_LINE_NUM_NTSC, &tve->line_num);
1063 writel(SUNXI_TVE_BLANK_BLACK_LEVEL_NTSC, &tve->blank_black_level);
1064 writel(SUNXI_TVE_UNKNOWN1_COMPOSITE, &tve->unknown1);
1065 writel(SUNXI_TVE_CBR_LEVEL_NTSC, &tve->cbr_level);
1066 writel(SUNXI_TVE_BURST_PHASE_NTSC, &tve->burst_phase);
1067 writel(SUNXI_TVE_BURST_WIDTH_COMPOSITE, &tve->burst_width);
1068 writel(SUNXI_TVE_UNKNOWN2_NTSC, &tve->unknown2);
1069 writel(SUNXI_TVE_SYNC_VBI_LEVEL_NTSC, &tve->sync_vbi_level);
1070 writel(SUNXI_TVE_ACTIVE_NUM_COMPOSITE, &tve->active_num);
1071 writel(SUNXI_TVE_CHROMA_BW_GAIN_COMP, &tve->chroma_bw_gain);
1072 writel(SUNXI_TVE_NOTCH_WIDTH_COMPOSITE, &tve->notch_width);
1073 writel(SUNXI_TVE_RESYNC_NUM_NTSC, &tve->resync_num);
1074 writel(SUNXI_TVE_SLAVE_PARA_COMPOSITE, &tve->slave_para);
1075 break;
1076 case sunxi_monitor_none:
1077 case sunxi_monitor_dvi:
1078 case sunxi_monitor_hdmi:
1079 case sunxi_monitor_lcd:
1080 break;
1081 }
Hans de Goede260f5202014-12-25 13:58:06 +01001082}
1083
Hans de Goedec06e00e2015-08-03 19:20:26 +02001084static void sunxi_tvencoder_enable(void)
Hans de Goede260f5202014-12-25 13:58:06 +01001085{
1086 struct sunxi_tve_reg * const tve =
1087 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
1088
1089 setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE);
1090}
1091
Hans de Goedec06e00e2015-08-03 19:20:26 +02001092#endif /* CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE */
Hans de Goede260f5202014-12-25 13:58:06 +01001093
Hans de Goede115e4b42014-12-23 18:39:52 +01001094static void sunxi_drc_init(void)
1095{
Hans de Goedef07872b2015-04-06 20:33:34 +02001096#ifdef CONFIG_SUNXI_GEN_SUN6I
Hans de Goede115e4b42014-12-23 18:39:52 +01001097 struct sunxi_ccm_reg * const ccm =
1098 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
1099
1100 /* On sun6i the drc must be clocked even when in pass-through mode */
Vishnu Patekar3702f142015-03-01 23:47:48 +05301101#ifdef CONFIG_MACH_SUN8I_A33
1102 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_SAT);
1103#endif
Hans de Goede115e4b42014-12-23 18:39:52 +01001104 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0);
1105 clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000);
1106#endif
1107}
1108
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +08001109#ifdef CONFIG_VIDEO_VGA_VIA_LCD
1110static void sunxi_vga_external_dac_enable(void)
1111{
1112 int pin;
1113
1114 pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
Hans de Goede7783ab22015-04-22 17:45:59 +02001115 if (pin >= 0) {
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +08001116 gpio_request(pin, "vga_enable");
1117 gpio_direction_output(pin, 1);
1118 }
1119}
1120#endif /* CONFIG_VIDEO_VGA_VIA_LCD */
1121
Siarhei Siamashkac02f0522015-01-19 05:23:33 +02001122#ifdef CONFIG_VIDEO_LCD_SSD2828
1123static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
1124{
1125 struct ssd2828_config cfg = {
1126 .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
1127 .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
1128 .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
1129 .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
1130 .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
1131 .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
1132 .ssd2828_color_depth = 24,
1133#ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
1134 .mipi_dsi_number_of_data_lanes = 4,
1135 .mipi_dsi_bitrate_per_data_lane_mbps = 513,
1136 .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
1137 .mipi_dsi_delay_after_set_display_on_ms = 200
1138#else
1139#error MIPI LCD panel needs configuration parameters
1140#endif
1141 };
1142
1143 if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
1144 printf("SSD2828: SPI pins are not properly configured\n");
1145 return 1;
1146 }
1147 if (cfg.reset_pin == -1) {
1148 printf("SSD2828: Reset pin is not properly configured\n");
1149 return 1;
1150 }
1151
1152 return ssd2828_init(&cfg, mode);
1153}
1154#endif /* CONFIG_VIDEO_LCD_SSD2828 */
1155
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001156static void sunxi_engines_init(void)
1157{
1158 sunxi_composer_init();
1159 sunxi_lcdc_init();
Hans de Goedef651e0a2014-11-14 17:42:14 +01001160 sunxi_drc_init();
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001161}
1162
Hans de Goedea0b1b732014-12-21 14:37:45 +01001163static void sunxi_mode_set(const struct ctfb_res_modes *mode,
Hans de Goedea2017e82014-12-20 13:38:06 +01001164 unsigned int address)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001165{
Hans de Goede260f5202014-12-25 13:58:06 +01001166 int __maybe_unused clk_div, clk_double;
1167
Hans de Goede4125f922014-12-21 14:49:34 +01001168 switch (sunxi_display.monitor) {
1169 case sunxi_monitor_none:
1170 break;
1171 case sunxi_monitor_dvi:
Hans de Goede260f5202014-12-25 13:58:06 +01001172 case sunxi_monitor_hdmi:
Hans de Goedee9544592014-12-23 23:04:35 +01001173#ifdef CONFIG_VIDEO_HDMI
Hans de Goede4125f922014-12-21 14:49:34 +01001174 sunxi_composer_mode_set(mode, address);
Hans de Goedec3d15042014-12-27 15:19:23 +01001175 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
Hans de Goede4125f922014-12-21 14:49:34 +01001176 sunxi_hdmi_mode_set(mode, clk_div, clk_double);
1177 sunxi_composer_enable();
1178 sunxi_lcdc_enable();
1179 sunxi_hdmi_enable();
Hans de Goedee9544592014-12-23 23:04:35 +01001180#endif
Hans de Goede4125f922014-12-21 14:49:34 +01001181 break;
1182 case sunxi_monitor_lcd:
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001183 sunxi_lcdc_panel_enable();
Hans de Goede743fb9552015-01-20 09:23:36 +01001184 if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
1185 mdelay(50); /* Wait for lcd controller power on */
1186 hitachi_tx18d42vm_init();
1187 }
Hans de Goede613dade2015-02-16 17:49:47 +01001188 if (IS_ENABLED(CONFIG_VIDEO_LCD_TL059WV5C0)) {
1189 unsigned int orig_i2c_bus = i2c_get_bus_num();
1190 i2c_set_bus_num(CONFIG_VIDEO_LCD_I2C_BUS);
1191 i2c_reg_write(0x5c, 0x04, 0x42); /* Turn on the LCD */
1192 i2c_set_bus_num(orig_i2c_bus);
1193 }
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001194 sunxi_composer_mode_set(mode, address);
Hans de Goede18366f72015-01-25 15:33:07 +01001195 sunxi_lcdc_tcon0_mode_set(mode, false);
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001196 sunxi_composer_enable();
1197 sunxi_lcdc_enable();
Siarhei Siamashkac02f0522015-01-19 05:23:33 +02001198#ifdef CONFIG_VIDEO_LCD_SSD2828
1199 sunxi_ssd2828_init(mode);
1200#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001201 sunxi_lcdc_backlight_enable();
Hans de Goede4125f922014-12-21 14:49:34 +01001202 break;
1203 case sunxi_monitor_vga:
Hans de Goede260f5202014-12-25 13:58:06 +01001204#ifdef CONFIG_VIDEO_VGA
1205 sunxi_composer_mode_set(mode, address);
1206 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
Hans de Goedec06e00e2015-08-03 19:20:26 +02001207 sunxi_tvencoder_mode_set();
Hans de Goede260f5202014-12-25 13:58:06 +01001208 sunxi_composer_enable();
1209 sunxi_lcdc_enable();
Hans de Goedec06e00e2015-08-03 19:20:26 +02001210 sunxi_tvencoder_enable();
Hans de Goede260f5202014-12-25 13:58:06 +01001211#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goedeac1633c2014-12-24 12:17:07 +01001212 sunxi_composer_mode_set(mode, address);
Hans de Goede18366f72015-01-25 15:33:07 +01001213 sunxi_lcdc_tcon0_mode_set(mode, true);
Hans de Goedeac1633c2014-12-24 12:17:07 +01001214 sunxi_composer_enable();
1215 sunxi_lcdc_enable();
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +08001216 sunxi_vga_external_dac_enable();
Hans de Goedeac1633c2014-12-24 12:17:07 +01001217#endif
Hans de Goede4125f922014-12-21 14:49:34 +01001218 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001219 case sunxi_monitor_composite_pal:
1220 case sunxi_monitor_composite_ntsc:
1221 case sunxi_monitor_composite_pal_m:
1222 case sunxi_monitor_composite_pal_nc:
1223#ifdef CONFIG_VIDEO_COMPOSITE
1224 sunxi_composer_mode_set(mode, address);
1225 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
1226 sunxi_tvencoder_mode_set();
1227 sunxi_composer_enable();
1228 sunxi_lcdc_enable();
1229 sunxi_tvencoder_enable();
1230#endif
1231 break;
Hans de Goede4125f922014-12-21 14:49:34 +01001232 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001233}
1234
Hans de Goedea0b1b732014-12-21 14:37:45 +01001235static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
1236{
1237 switch (monitor) {
Hans de Goedec06e00e2015-08-03 19:20:26 +02001238 case sunxi_monitor_none: return "none";
1239 case sunxi_monitor_dvi: return "dvi";
1240 case sunxi_monitor_hdmi: return "hdmi";
1241 case sunxi_monitor_lcd: return "lcd";
1242 case sunxi_monitor_vga: return "vga";
1243 case sunxi_monitor_composite_pal: return "composite-pal";
1244 case sunxi_monitor_composite_ntsc: return "composite-ntsc";
1245 case sunxi_monitor_composite_pal_m: return "composite-pal-m";
1246 case sunxi_monitor_composite_pal_nc: return "composite-pal-nc";
Hans de Goedea0b1b732014-12-21 14:37:45 +01001247 }
1248 return NULL; /* never reached */
1249}
1250
Hans de Goede6c912862015-02-02 17:13:29 +01001251ulong board_get_usable_ram_top(ulong total_size)
1252{
1253 return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
1254}
1255
Hans de Goedea2bba492015-08-03 23:01:38 +02001256static bool sunxi_has_hdmi(void)
1257{
1258#ifdef CONFIG_VIDEO_HDMI
1259 return true;
1260#else
1261 return false;
1262#endif
1263}
1264
1265static bool sunxi_has_lcd(void)
1266{
1267 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
1268
1269 return lcd_mode[0] != 0;
1270}
1271
1272static bool sunxi_has_vga(void)
1273{
1274#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_VGA_VIA_LCD
1275 return true;
1276#else
1277 return false;
1278#endif
1279}
1280
Hans de Goedec06e00e2015-08-03 19:20:26 +02001281static bool sunxi_has_composite(void)
1282{
1283#ifdef CONFIG_VIDEO_COMPOSITE
1284 return true;
1285#else
1286 return false;
1287#endif
1288}
1289
Hans de Goedea2bba492015-08-03 23:01:38 +02001290static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
1291{
1292 if (allow_hdmi && sunxi_has_hdmi())
1293 return sunxi_monitor_dvi;
1294 else if (sunxi_has_lcd())
1295 return sunxi_monitor_lcd;
1296 else if (sunxi_has_vga())
1297 return sunxi_monitor_vga;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001298 else if (sunxi_has_composite())
1299 return sunxi_monitor_composite_pal;
Hans de Goedea2bba492015-08-03 23:01:38 +02001300 else
1301 return sunxi_monitor_none;
1302}
1303
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001304void *video_hw_init(void)
1305{
1306 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001307 const struct ctfb_res_modes *mode;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001308 struct ctfb_res_modes custom;
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001309 const char *options;
Hans de Goedee9544592014-12-23 23:04:35 +01001310#ifdef CONFIG_VIDEO_HDMI
Hans de Goede91593712014-12-28 09:13:21 +01001311 int ret, hpd, hpd_delay, edid;
Hans de Goedee9544592014-12-23 23:04:35 +01001312#endif
Hans de Goeded955f442015-08-05 00:06:47 +02001313 int i, overscan_offset, overscan_x, overscan_y;
1314 unsigned int fb_dma_addr;
Hans de Goedea0b1b732014-12-21 14:37:45 +01001315 char mon[16];
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001316 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001317
1318 memset(&sunxi_display, 0, sizeof(struct sunxi_display));
1319
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001320 video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
1321 &sunxi_display.depth, &options);
Hans de Goedee9544592014-12-23 23:04:35 +01001322#ifdef CONFIG_VIDEO_HDMI
Hans de Goede695bda42014-12-19 15:13:57 +01001323 hpd = video_get_option_int(options, "hpd", 1);
Hans de Goede91593712014-12-28 09:13:21 +01001324 hpd_delay = video_get_option_int(options, "hpd_delay", 500);
Hans de Goedea5aa95f2014-12-19 16:05:12 +01001325 edid = video_get_option_int(options, "edid", 1);
Hans de Goedee9544592014-12-23 23:04:35 +01001326#endif
Hans de Goeded955f442015-08-05 00:06:47 +02001327 overscan_x = video_get_option_int(options, "overscan_x", -1);
1328 overscan_y = video_get_option_int(options, "overscan_y", -1);
Hans de Goedea2bba492015-08-03 23:01:38 +02001329 sunxi_display.monitor = sunxi_get_default_mon(true);
Hans de Goedea0b1b732014-12-21 14:37:45 +01001330 video_get_option_string(options, "monitor", mon, sizeof(mon),
1331 sunxi_get_mon_desc(sunxi_display.monitor));
1332 for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
1333 if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
1334 sunxi_display.monitor = i;
1335 break;
1336 }
1337 }
1338 if (i > SUNXI_MONITOR_LAST)
1339 printf("Unknown monitor: '%s', falling back to '%s'\n",
1340 mon, sunxi_get_mon_desc(sunxi_display.monitor));
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001341
Hans de Goede7977ec22014-12-25 13:52:04 +01001342#ifdef CONFIG_VIDEO_HDMI
1343 /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
1344 if (sunxi_display.monitor == sunxi_monitor_dvi ||
1345 sunxi_display.monitor == sunxi_monitor_hdmi) {
1346 /* Always call hdp_detect, as it also enables clocks, etc. */
Hans de Goede91593712014-12-28 09:13:21 +01001347 ret = sunxi_hdmi_hpd_detect(hpd_delay);
Hans de Goede7977ec22014-12-25 13:52:04 +01001348 if (ret) {
1349 printf("HDMI connected: ");
1350 if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0)
1351 mode = &custom;
1352 } else if (hpd) {
1353 sunxi_hdmi_shutdown();
Hans de Goedea2bba492015-08-03 23:01:38 +02001354 sunxi_display.monitor = sunxi_get_default_mon(false);
Hans de Goede7977ec22014-12-25 13:52:04 +01001355 } /* else continue with hdmi/dvi without a cable connected */
1356 }
1357#endif
1358
Hans de Goede4125f922014-12-21 14:49:34 +01001359 switch (sunxi_display.monitor) {
1360 case sunxi_monitor_none:
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001361 return NULL;
Hans de Goede4125f922014-12-21 14:49:34 +01001362 case sunxi_monitor_dvi:
1363 case sunxi_monitor_hdmi:
Hans de Goedea2bba492015-08-03 23:01:38 +02001364 if (!sunxi_has_hdmi()) {
1365 printf("HDMI/DVI not supported on this board\n");
1366 sunxi_display.monitor = sunxi_monitor_none;
1367 return NULL;
1368 }
Hans de Goede7977ec22014-12-25 13:52:04 +01001369 break;
Hans de Goede4125f922014-12-21 14:49:34 +01001370 case sunxi_monitor_lcd:
Hans de Goedea2bba492015-08-03 23:01:38 +02001371 if (!sunxi_has_lcd()) {
1372 printf("LCD not supported on this board\n");
1373 sunxi_display.monitor = sunxi_monitor_none;
1374 return NULL;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001375 }
Hans de Goedea2bba492015-08-03 23:01:38 +02001376 sunxi_display.depth = video_get_params(&custom, lcd_mode);
1377 mode = &custom;
1378 break;
Hans de Goede4125f922014-12-21 14:49:34 +01001379 case sunxi_monitor_vga:
Hans de Goedea2bba492015-08-03 23:01:38 +02001380 if (!sunxi_has_vga()) {
1381 printf("VGA not supported on this board\n");
1382 sunxi_display.monitor = sunxi_monitor_none;
1383 return NULL;
1384 }
Hans de Goedeac1633c2014-12-24 12:17:07 +01001385 sunxi_display.depth = 18;
1386 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001387 case sunxi_monitor_composite_pal:
1388 case sunxi_monitor_composite_ntsc:
1389 case sunxi_monitor_composite_pal_m:
1390 case sunxi_monitor_composite_pal_nc:
1391 if (!sunxi_has_composite()) {
1392 printf("Composite video not supported on this board\n");
1393 sunxi_display.monitor = sunxi_monitor_none;
1394 return NULL;
1395 }
1396 if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
1397 sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
1398 mode = &composite_video_modes[0];
1399 else
1400 mode = &composite_video_modes[1];
1401 sunxi_display.depth = 24;
1402 break;
Hans de Goedea5aa95f2014-12-19 16:05:12 +01001403 }
1404
Hans de Goeded955f442015-08-05 00:06:47 +02001405 /* Yes these defaults are quite high, overscan on composite sucks... */
1406 if (overscan_x == -1)
1407 overscan_x = sunxi_is_composite() ? 32 : 0;
1408 if (overscan_y == -1)
1409 overscan_y = sunxi_is_composite() ? 20 : 0;
1410
Hans de Goede18799252015-02-02 18:00:53 +01001411 sunxi_display.fb_size =
1412 (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
Hans de Goeded955f442015-08-05 00:06:47 +02001413 overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
1414 /* We want to keep the fb_base for simplefb page aligned, where as
1415 * the sunxi dma engines will happily accept an unaligned address. */
1416 if (overscan_offset)
1417 sunxi_display.fb_size += 0x1000;
1418
Hans de Goede18799252015-02-02 18:00:53 +01001419 if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
1420 printf("Error need %dkB for fb, but only %dkB is reserved\n",
1421 sunxi_display.fb_size >> 10,
1422 CONFIG_SUNXI_MAX_FB_SIZE >> 10);
1423 return NULL;
1424 }
1425
Hans de Goeded955f442015-08-05 00:06:47 +02001426 printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
1427 mode->xres, mode->yres,
Hans de Goede1977bbb2015-08-02 16:49:29 +02001428 (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
Hans de Goeded955f442015-08-05 00:06:47 +02001429 sunxi_get_mon_desc(sunxi_display.monitor),
1430 overscan_x, overscan_y);
Hans de Goede1977bbb2015-08-02 16:49:29 +02001431
Hans de Goede18799252015-02-02 18:00:53 +01001432 gd->fb_base = gd->bd->bi_dram[0].start +
1433 gd->bd->bi_dram[0].size - sunxi_display.fb_size;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001434 sunxi_engines_init();
Hans de Goeded955f442015-08-05 00:06:47 +02001435
1436 fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
1437 sunxi_display.fb_addr = gd->fb_base;
1438 if (overscan_offset) {
1439 fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
1440 sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
1441 memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
1442 flush_cache(gd->fb_base, sunxi_display.fb_size);
1443 }
1444 sunxi_mode_set(mode, fb_dma_addr);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001445
1446 /*
1447 * These are the only members of this structure that are used. All the
Hans de Goeded955f442015-08-05 00:06:47 +02001448 * others are driver specific. The pitch is stored in plnSizeX.
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001449 */
Hans de Goeded955f442015-08-05 00:06:47 +02001450 graphic_device->frameAdrs = sunxi_display.fb_addr;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001451 graphic_device->gdfIndex = GDF_32BIT_X888RGB;
1452 graphic_device->gdfBytesPP = 4;
Hans de Goeded955f442015-08-05 00:06:47 +02001453 graphic_device->winSizeX = mode->xres - 2 * overscan_x;
1454 graphic_device->winSizeY = mode->yres - 2 * overscan_y;
1455 graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001456
1457 return graphic_device;
1458}
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001459
1460/*
1461 * Simplefb support.
1462 */
1463#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
1464int sunxi_simplefb_setup(void *blob)
1465{
1466 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
1467 int offset, ret;
Hans de Goede6c912862015-02-02 17:13:29 +01001468 u64 start, size;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001469 const char *pipeline = NULL;
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001470
Hans de Goedec3cc4262015-01-19 08:44:07 +01001471#ifdef CONFIG_MACH_SUN4I
1472#define PIPELINE_PREFIX "de_fe0-"
1473#else
1474#define PIPELINE_PREFIX
1475#endif
1476
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001477 switch (sunxi_display.monitor) {
1478 case sunxi_monitor_none:
1479 return 0;
1480 case sunxi_monitor_dvi:
1481 case sunxi_monitor_hdmi:
Hans de Goedec3cc4262015-01-19 08:44:07 +01001482 pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi";
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001483 break;
1484 case sunxi_monitor_lcd:
Hans de Goedec3cc4262015-01-19 08:44:07 +01001485 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001486 break;
1487 case sunxi_monitor_vga:
Hans de Goede260f5202014-12-25 13:58:06 +01001488#ifdef CONFIG_VIDEO_VGA
Hans de Goedec3cc4262015-01-19 08:44:07 +01001489 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
Hans de Goede260f5202014-12-25 13:58:06 +01001490#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goedec3cc4262015-01-19 08:44:07 +01001491 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede260f5202014-12-25 13:58:06 +01001492#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001493 break;
Hans de Goedec06e00e2015-08-03 19:20:26 +02001494 case sunxi_monitor_composite_pal:
1495 case sunxi_monitor_composite_ntsc:
1496 case sunxi_monitor_composite_pal_m:
1497 case sunxi_monitor_composite_pal_nc:
1498 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
1499 break;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001500 }
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001501
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001502 /* Find a prefilled simpefb node, matching out pipeline config */
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001503 offset = fdt_node_offset_by_compatible(blob, -1,
1504 "allwinner,simple-framebuffer");
1505 while (offset >= 0) {
1506 ret = fdt_find_string(blob, offset, "allwinner,pipeline",
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001507 pipeline);
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001508 if (ret == 0)
1509 break;
1510 offset = fdt_node_offset_by_compatible(blob, offset,
1511 "allwinner,simple-framebuffer");
1512 }
1513 if (offset < 0) {
1514 eprintf("Cannot setup simplefb: node not found\n");
1515 return 0; /* Keep older kernels working */
1516 }
1517
Hans de Goede6c912862015-02-02 17:13:29 +01001518 /*
1519 * Do not report the framebuffer as free RAM to the OS, note we cannot
1520 * use fdt_add_mem_rsv() here, because then it is still seen as RAM,
1521 * and e.g. Linux refuses to iomap RAM on ARM, see:
1522 * linux/arch/arm/mm/ioremap.c around line 301.
1523 */
1524 start = gd->bd->bi_dram[0].start;
Hans de Goede18799252015-02-02 18:00:53 +01001525 size = gd->bd->bi_dram[0].size - sunxi_display.fb_size;
Hans de Goede6c912862015-02-02 17:13:29 +01001526 ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
1527 if (ret) {
1528 eprintf("Cannot setup simplefb: Error reserving memory\n");
1529 return ret;
1530 }
1531
Hans de Goeded955f442015-08-05 00:06:47 +02001532 ret = fdt_setup_simplefb_node(blob, offset, sunxi_display.fb_addr,
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001533 graphic_device->winSizeX, graphic_device->winSizeY,
Hans de Goeded955f442015-08-05 00:06:47 +02001534 graphic_device->plnSizeX, "x8r8g8b8");
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001535 if (ret)
1536 eprintf("Cannot setup simplefb: Error setting properties\n");
1537
1538 return ret;
1539}
1540#endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */