blob: 5077111daf90e44f4eb8be3519636cd94f3851a9 [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>
5 * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
6 *
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>
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020021#include <video_fb.h>
Hans de Goedeccb0ed52014-12-19 13:46:33 +010022#include "videomodes.h"
Hans de Goede743fb9552015-01-20 09:23:36 +010023#include "hitachi_tx18d42vm_lcd.h"
Siarhei Siamashkac02f0522015-01-19 05:23:33 +020024#include "ssd2828.h"
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020025
26DECLARE_GLOBAL_DATA_PTR;
27
Hans de Goedea0b1b732014-12-21 14:37:45 +010028enum sunxi_monitor {
29 sunxi_monitor_none,
30 sunxi_monitor_dvi,
31 sunxi_monitor_hdmi,
32 sunxi_monitor_lcd,
33 sunxi_monitor_vga,
34};
35#define SUNXI_MONITOR_LAST sunxi_monitor_vga
36
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020037struct sunxi_display {
38 GraphicDevice graphic_device;
Hans de Goedea0b1b732014-12-21 14:37:45 +010039 enum sunxi_monitor monitor;
Hans de Goede7e68a1b2014-12-21 16:28:32 +010040 unsigned int depth;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020041} sunxi_display;
42
Hans de Goedee9544592014-12-23 23:04:35 +010043#ifdef CONFIG_VIDEO_HDMI
44
Hans de Goedea5aa95f2014-12-19 16:05:12 +010045/*
46 * Wait up to 200ms for value to be set in given part of reg.
47 */
48static int await_completion(u32 *reg, u32 mask, u32 val)
49{
50 unsigned long tmo = timer_get_us() + 200000;
51
52 while ((readl(reg) & mask) != val) {
53 if (timer_get_us() > tmo) {
54 printf("DDC: timeout reading EDID\n");
55 return -ETIME;
56 }
57 }
58 return 0;
59}
60
Hans de Goede91593712014-12-28 09:13:21 +010061static int sunxi_hdmi_hpd_detect(int hpd_delay)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020062{
63 struct sunxi_ccm_reg * const ccm =
64 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
65 struct sunxi_hdmi_reg * const hdmi =
66 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Hans de Goede91593712014-12-28 09:13:21 +010067 unsigned long tmo = timer_get_us() + hpd_delay * 1000;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020068
69 /* Set pll3 to 300MHz */
70 clock_set_pll3(300000000);
71
72 /* Set hdmi parent to pll3 */
73 clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
74 CCM_HDMI_CTRL_PLL3);
75
76 /* Set ahb gating to pass */
Hans de Goedef651e0a2014-11-14 17:42:14 +010077#ifdef CONFIG_MACH_SUN6I
78 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
79#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020080 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
81
82 /* Clock on */
83 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
84
85 writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl);
86 writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0);
87
Hans de Goede205a30c2014-12-20 15:15:23 +010088 while (timer_get_us() < tmo) {
89 if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT)
90 return 1;
91 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +020092
Hans de Goede205a30c2014-12-20 15:15:23 +010093 return 0;
Hans de Goede695bda42014-12-19 15:13:57 +010094}
95
96static void sunxi_hdmi_shutdown(void)
97{
98 struct sunxi_ccm_reg * const ccm =
99 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
100 struct sunxi_hdmi_reg * const hdmi =
101 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200102
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200103 clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE);
104 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
105 clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
Hans de Goedef651e0a2014-11-14 17:42:14 +0100106#ifdef CONFIG_MACH_SUN6I
107 clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
108#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200109 clock_set_pll3(0);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200110}
111
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100112static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
113{
114 struct sunxi_hdmi_reg * const hdmi =
115 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
116
117 setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
118 writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
119 SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
120 SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
121 SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
122#ifndef CONFIG_MACH_SUN6I
123 writel(n, &hdmi->ddc_byte_count);
124 writel(cmnd, &hdmi->ddc_cmnd);
125#else
126 writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
127#endif
128 setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
129
130 return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
131}
132
133static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
134{
135 struct sunxi_hdmi_reg * const hdmi =
136 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
137 int i, n;
138
139 while (count > 0) {
140 if (count > 16)
141 n = 16;
142 else
143 n = count;
144
145 if (sunxi_hdmi_ddc_do_command(
146 SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
147 offset, n))
148 return -ETIME;
149
150 for (i = 0; i < n; i++)
151 *buf++ = readb(&hdmi->ddc_fifo_data);
152
153 offset += n;
154 count -= n;
155 }
156
157 return 0;
158}
159
Hans de Goede45b8f7b2014-12-20 14:01:48 +0100160static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
161{
162 int r, retries = 2;
163
164 do {
165 r = sunxi_hdmi_ddc_read(block * 128, buf, 128);
166 if (r)
167 continue;
168 r = edid_check_checksum(buf);
169 if (r) {
170 printf("EDID block %d: checksum error%s\n",
171 block, retries ? ", retrying" : "");
172 }
173 } while (r && retries--);
174
175 return r;
176}
177
Hans de Goedea0b1b732014-12-21 14:37:45 +0100178static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode)
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100179{
180 struct edid1_info edid1;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100181 struct edid_cea861_info cea681[4];
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100182 struct edid_detailed_timing *t =
183 (struct edid_detailed_timing *)edid1.monitor_details.timing;
184 struct sunxi_hdmi_reg * const hdmi =
185 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
186 struct sunxi_ccm_reg * const ccm =
187 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100188 int i, r, ext_blocks = 0;
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100189
190 /* SUNXI_HDMI_CTRL_ENABLE & PAD_CTRL0 are already set by hpd_detect */
191 writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
192 &hdmi->pad_ctrl1);
193 writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
194 &hdmi->pll_ctrl);
195 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
196
197 /* Reset i2c controller */
198 setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
199 writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
200 SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
201 SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
202 SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
203 if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
204 return -EIO;
205
206 writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
207#ifndef CONFIG_MACH_SUN6I
208 writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
209 SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
210#endif
211
Hans de Goede45b8f7b2014-12-20 14:01:48 +0100212 r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100213 if (r == 0) {
214 r = edid_check_info(&edid1);
215 if (r) {
216 printf("EDID: invalid EDID data\n");
217 r = -EINVAL;
218 }
219 }
220 if (r == 0) {
221 ext_blocks = edid1.extension_flag;
222 if (ext_blocks > 4)
223 ext_blocks = 4;
224 for (i = 0; i < ext_blocks; i++) {
225 if (sunxi_hdmi_edid_get_block(1 + i,
226 (u8 *)&cea681[i]) != 0) {
227 ext_blocks = i;
228 break;
229 }
230 }
231 }
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100232
233 /* Disable DDC engine, no longer needed */
234 clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
235 clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
236
237 if (r)
238 return r;
239
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100240 /* We want version 1.3 or 1.2 with detailed timing info */
241 if (edid1.version != 1 || (edid1.revision < 3 &&
242 !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
243 printf("EDID: unsupported version %d.%d\n",
244 edid1.version, edid1.revision);
245 return -EINVAL;
246 }
247
248 /* Take the first usable detailed timing */
249 for (i = 0; i < 4; i++, t++) {
250 r = video_edid_dtd_to_ctfb_res_modes(t, mode);
251 if (r == 0)
252 break;
253 }
254 if (i == 4) {
255 printf("EDID: no usable detailed timing found\n");
256 return -ENOENT;
257 }
258
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100259 /* Check for basic audio support, if found enable hdmi output */
Hans de Goedea0b1b732014-12-21 14:37:45 +0100260 sunxi_display.monitor = sunxi_monitor_dvi;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100261 for (i = 0; i < ext_blocks; i++) {
262 if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
263 cea681[i].revision < 2)
264 continue;
265
266 if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
Hans de Goedea0b1b732014-12-21 14:37:45 +0100267 sunxi_display.monitor = sunxi_monitor_hdmi;
Hans de Goede1ff6cc42014-12-20 14:31:45 +0100268 }
269
Hans de Goedea5aa95f2014-12-19 16:05:12 +0100270 return 0;
271}
272
Hans de Goedee9544592014-12-23 23:04:35 +0100273#endif /* CONFIG_VIDEO_HDMI */
274
Hans de Goedec3cc4262015-01-19 08:44:07 +0100275#ifdef CONFIG_MACH_SUN4I
276/*
277 * Testing has shown that on sun4i the display backend engine does not have
278 * deep enough fifo-s causing flickering / tearing in full-hd mode due to
279 * fifo underruns. So on sun4i we use the display frontend engine to do the
280 * dma from memory, as the frontend does have deep enough fifo-s.
281 */
282
283static const u32 sun4i_vert_coef[32] = {
284 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
285 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
286 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
287 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
288 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
289 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
290 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
291 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
292};
293
294static const u32 sun4i_horz_coef[64] = {
295 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
296 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
297 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
298 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
299 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
300 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
301 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
302 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
303 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
304 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
305 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
306 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
307 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
308 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
309 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
310 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
311};
312
313static void sunxi_frontend_init(void)
314{
315 struct sunxi_ccm_reg * const ccm =
316 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
317 struct sunxi_de_fe_reg * const de_fe =
318 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
319 int i;
320
321 /* Clocks on */
322 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0);
323 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0);
324 clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000);
325
326 setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN);
327
328 for (i = 0; i < 32; i++) {
329 writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]);
330 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]);
331 writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]);
332 writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]);
333 writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]);
334 writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]);
335 }
336
337 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY);
338}
339
340static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
341 unsigned int address)
342{
343 struct sunxi_de_fe_reg * const de_fe =
344 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
345
346 setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS);
347 writel(CONFIG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr);
348 writel(mode->xres * 4, &de_fe->ch0_stride);
349 writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt);
350 writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt);
351
352 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
353 &de_fe->ch0_insize);
354 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
355 &de_fe->ch0_outsize);
356 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact);
357 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact);
358
359 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
360 &de_fe->ch1_insize);
361 writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
362 &de_fe->ch1_outsize);
363 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact);
364 writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact);
365
366 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY);
367}
368
369static void sunxi_frontend_enable(void)
370{
371 struct sunxi_de_fe_reg * const de_fe =
372 (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
373
374 setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START);
375}
376#else
377static void sunxi_frontend_init(void) {}
378static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
379 unsigned int address) {}
380static void sunxi_frontend_enable(void) {}
381#endif
382
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200383/*
384 * This is the entity that mixes and matches the different layers and inputs.
385 * Allwinner calls it the back-end, but i like composer better.
386 */
387static void sunxi_composer_init(void)
388{
389 struct sunxi_ccm_reg * const ccm =
390 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
391 struct sunxi_de_be_reg * const de_be =
392 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
393 int i;
394
Hans de Goedec3cc4262015-01-19 08:44:07 +0100395 sunxi_frontend_init();
396
Hans de Goedee9544592014-12-23 23:04:35 +0100397#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100398 /* Reset off */
399 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0);
400#endif
401
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200402 /* Clocks on */
403 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100404#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200405 setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100406#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200407 clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000);
408
409 /* Engine bug, clear registers after reset */
410 for (i = 0x0800; i < 0x1000; i += 4)
411 writel(0, SUNXI_DE_BE0_BASE + i);
412
413 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE);
414}
415
Hans de Goedeccb0ed52014-12-19 13:46:33 +0100416static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200417 unsigned int address)
418{
419 struct sunxi_de_be_reg * const de_be =
420 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
421
Hans de Goedec3cc4262015-01-19 08:44:07 +0100422 sunxi_frontend_mode_set(mode, address);
423
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200424 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
425 &de_be->disp_size);
426 writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
427 &de_be->layer0_size);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100428#ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200429 writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride);
430 writel(address << 3, &de_be->layer0_addr_low32b);
431 writel(address >> 29, &de_be->layer0_addr_high4b);
Hans de Goedec3cc4262015-01-19 08:44:07 +0100432#else
433 writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl);
434#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200435 writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl);
436
437 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE);
438}
439
Hans de Goede4125f922014-12-21 14:49:34 +0100440static void sunxi_composer_enable(void)
441{
442 struct sunxi_de_be_reg * const de_be =
443 (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
444
Hans de Goedec3cc4262015-01-19 08:44:07 +0100445 sunxi_frontend_enable();
446
Hans de Goede4125f922014-12-21 14:49:34 +0100447 setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS);
448 setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
449}
450
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200451/*
452 * LCDC, what allwinner calls a CRTC, so timing controller and serializer.
453 */
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100454static void sunxi_lcdc_pll_set(int tcon, int dotclock,
455 int *clk_div, int *clk_double)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200456{
457 struct sunxi_ccm_reg * const ccm =
458 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100459 int value, n, m, min_m, max_m, diff;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200460 int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF;
461 int best_double = 0;
462
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100463 if (tcon == 0) {
Hans de Goede797a0f52015-01-01 22:04:34 +0100464#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100465 min_m = 6;
466 max_m = 127;
Hans de Goede797a0f52015-01-01 22:04:34 +0100467#endif
468#ifdef CONFIG_VIDEO_LCD_IF_LVDS
469 min_m = max_m = 7;
470#endif
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100471 } else {
472 min_m = 1;
473 max_m = 15;
474 }
475
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200476 /*
477 * Find the lowest divider resulting in a matching clock, if there
478 * is no match, pick the closest lower clock, as monitors tend to
479 * not sync to higher frequencies.
480 */
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100481 for (m = min_m; m <= max_m; m++) {
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200482 n = (m * dotclock) / 3000;
483
484 if ((n >= 9) && (n <= 127)) {
485 value = (3000 * n) / m;
486 diff = dotclock - value;
487 if (diff < best_diff) {
488 best_diff = diff;
489 best_m = m;
490 best_n = n;
491 best_double = 0;
492 }
493 }
494
495 /* These are just duplicates */
496 if (!(m & 1))
497 continue;
498
499 n = (m * dotclock) / 6000;
500 if ((n >= 9) && (n <= 127)) {
501 value = (6000 * n) / m;
502 diff = dotclock - value;
503 if (diff < best_diff) {
504 best_diff = diff;
505 best_m = m;
506 best_n = n;
507 best_double = 1;
508 }
509 }
510 }
511
512 debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n",
513 dotclock, (best_double + 1) * 3000 * best_n / best_m,
514 best_double + 1, best_n, best_m);
515
516 clock_set_pll3(best_n * 3000000);
517
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100518 if (tcon == 0) {
519 writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST |
520 (best_double ? CCM_LCD_CH0_CTRL_PLL3_2X :
521 CCM_LCD_CH0_CTRL_PLL3),
522 &ccm->lcd0_ch0_clk_cfg);
523 } else {
524 writel(CCM_LCD_CH1_CTRL_GATE |
525 (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X :
526 CCM_LCD_CH1_CTRL_PLL3) |
527 CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg);
528 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200529
530 *clk_div = best_m;
531 *clk_double = best_double;
532}
533
534static void sunxi_lcdc_init(void)
535{
536 struct sunxi_ccm_reg * const ccm =
537 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
538 struct sunxi_lcdc_reg * const lcdc =
539 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
540
541 /* Reset off */
Hans de Goedee9544592014-12-23 23:04:35 +0100542#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
Hans de Goedef651e0a2014-11-14 17:42:14 +0100543 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
544#else
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200545 setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST);
Hans de Goedef651e0a2014-11-14 17:42:14 +0100546#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200547
548 /* Clock on */
549 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100550#ifdef CONFIG_VIDEO_LCD_IF_LVDS
551 setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
552#endif
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200553
554 /* Init lcdc */
555 writel(0, &lcdc->ctrl); /* Disable tcon */
556 writel(0, &lcdc->int0); /* Disable all interrupts */
557
558 /* Disable tcon0 dot clock */
559 clrbits_le32(&lcdc->tcon0_dclk, SUNXI_LCDC_TCON0_DCLK_ENABLE);
560
561 /* Set all io lines to tristate */
562 writel(0xffffffff, &lcdc->tcon0_io_tristate);
563 writel(0xffffffff, &lcdc->tcon1_io_tristate);
564}
565
Hans de Goede4125f922014-12-21 14:49:34 +0100566static void sunxi_lcdc_enable(void)
567{
568 struct sunxi_lcdc_reg * const lcdc =
569 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
570
571 setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
Hans de Goede797a0f52015-01-01 22:04:34 +0100572#ifdef CONFIG_VIDEO_LCD_IF_LVDS
573 setbits_le32(&lcdc->tcon0_lvds_intf, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE);
574 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0);
575 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
576 udelay(2); /* delay at least 1200 ns */
577 setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT1);
578 udelay(1); /* delay at least 120 ns */
579 setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT2);
580 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
581#endif
Hans de Goede4125f922014-12-21 14:49:34 +0100582}
583
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100584static void sunxi_lcdc_panel_enable(void)
585{
586 int pin;
587
588 /*
589 * Start with backlight disabled to avoid the screen flashing to
590 * white while the lcd inits.
591 */
592 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
593 if (pin != -1) {
594 gpio_request(pin, "lcd_backlight_enable");
595 gpio_direction_output(pin, 0);
596 }
597
598 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
599 if (pin != -1) {
600 gpio_request(pin, "lcd_backlight_pwm");
601 /* backlight pwm is inverted, set to 1 to disable backlight */
602 gpio_direction_output(pin, 1);
603 }
604
605 /* Give the backlight some time to turn off and power up the panel. */
606 mdelay(40);
607 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
608 if (pin != -1) {
609 gpio_request(pin, "lcd_power");
610 gpio_direction_output(pin, 1);
611 }
612}
613
614static void sunxi_lcdc_backlight_enable(void)
615{
616 int pin;
617
618 /*
619 * We want to have scanned out at least one frame before enabling the
620 * backlight to avoid the screen flashing to white when we enable it.
621 */
622 mdelay(40);
623
624 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
625 if (pin != -1)
626 gpio_direction_output(pin, 1);
627
628 pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
629 if (pin != -1) {
630 /* backlight pwm is inverted, set to 0 to enable backlight */
631 gpio_direction_output(pin, 0);
632 }
633}
634
635static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode)
636{
637 int delay;
638
639 delay = mode->lower_margin + mode->vsync_len + mode->upper_margin - 2;
640 return (delay > 30) ? 30 : delay;
641}
642
643static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode)
644{
645 struct sunxi_lcdc_reg * const lcdc =
646 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
647 int bp, clk_delay, clk_div, clk_double, pin, total, val;
648
649 for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++)
Hans de Goede797a0f52015-01-01 22:04:34 +0100650#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100651 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LCD0);
Hans de Goede797a0f52015-01-01 22:04:34 +0100652#endif
653#ifdef CONFIG_VIDEO_LCD_IF_LVDS
654 sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LVDS0);
655#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100656
657 sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
658
659 /* Use tcon0 */
660 clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
661 SUNXI_LCDC_CTRL_IO_MAP_TCON0);
662
663 clk_delay = sunxi_lcdc_get_clk_delay(mode);
664 writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
665 SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
666
667 writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
668 SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
669
670 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
671 &lcdc->tcon0_timing_active);
672
673 bp = mode->hsync_len + mode->left_margin;
674 total = mode->xres + mode->right_margin + bp;
675 writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
676 SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
677
678 bp = mode->vsync_len + mode->upper_margin;
679 total = mode->yres + mode->lower_margin + bp;
680 writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
681 SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
682
Hans de Goede797a0f52015-01-01 22:04:34 +0100683#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100684 writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
685 &lcdc->tcon0_timing_sync);
686
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100687 writel(0, &lcdc->tcon0_hv_intf);
688 writel(0, &lcdc->tcon0_cpu_intf);
Hans de Goede797a0f52015-01-01 22:04:34 +0100689#endif
690#ifdef CONFIG_VIDEO_LCD_IF_LVDS
691 val = (sunxi_display.depth == 18) ? 1 : 0;
692 writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val), &lcdc->tcon0_lvds_intf);
693#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100694
695 if (sunxi_display.depth == 18 || sunxi_display.depth == 16) {
696 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
697 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]);
698 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]);
699 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]);
700 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]);
701 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]);
702 writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]);
703 writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]);
704 writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]);
705 writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]);
706 writel(((sunxi_display.depth == 18) ?
707 SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 :
708 SUNXI_LCDC_TCON0_FRM_CTRL_RGB565),
709 &lcdc->tcon0_frm_ctrl);
710 }
711
Hans de Goede481b6642015-01-13 13:21:46 +0100712 val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(CONFIG_VIDEO_LCD_DCLK_PHASE);
Hans de Goede7e68a1b2014-12-21 16:28:32 +0100713 if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
714 val |= SUNXI_LCDC_TCON_HSYNC_MASK;
715 if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
716 val |= SUNXI_LCDC_TCON_VSYNC_MASK;
717 writel(val, &lcdc->tcon0_io_polarity);
718
719 writel(0, &lcdc->tcon0_io_tristate);
720}
721
Hans de Goede260f5202014-12-25 13:58:06 +0100722#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA
Hans de Goede4125f922014-12-21 14:49:34 +0100723static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
Hans de Goedec3d15042014-12-27 15:19:23 +0100724 int *clk_div, int *clk_double,
725 bool use_portd_hvsync)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200726{
727 struct sunxi_lcdc_reg * const lcdc =
728 (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
Hans de Goedec3d15042014-12-27 15:19:23 +0100729 int bp, clk_delay, total, val;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200730
731 /* Use tcon1 */
732 clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
733 SUNXI_LCDC_CTRL_IO_MAP_TCON1);
734
Hans de Goedeac5d43d2014-12-24 19:50:11 +0100735 clk_delay = sunxi_lcdc_get_clk_delay(mode);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200736 writel(SUNXI_LCDC_TCON1_CTRL_ENABLE |
Hans de Goedeac5d43d2014-12-24 19:50:11 +0100737 SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200738
739 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
740 &lcdc->tcon1_timing_source);
741 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
742 &lcdc->tcon1_timing_scale);
743 writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
744 &lcdc->tcon1_timing_out);
745
746 bp = mode->hsync_len + mode->left_margin;
747 total = mode->xres + mode->right_margin + bp;
748 writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) |
749 SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h);
750
751 bp = mode->vsync_len + mode->upper_margin;
752 total = mode->yres + mode->lower_margin + bp;
753 writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) |
754 SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v);
755
756 writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
757 &lcdc->tcon1_timing_sync);
758
Hans de Goedec3d15042014-12-27 15:19:23 +0100759 if (use_portd_hvsync) {
760 sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD0_LCD0);
761 sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD0_LCD0);
762
763 val = 0;
764 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
765 val |= SUNXI_LCDC_TCON_HSYNC_MASK;
766 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
767 val |= SUNXI_LCDC_TCON_VSYNC_MASK;
768 writel(val, &lcdc->tcon1_io_polarity);
769
770 clrbits_le32(&lcdc->tcon1_io_tristate,
771 SUNXI_LCDC_TCON_VSYNC_MASK |
772 SUNXI_LCDC_TCON_HSYNC_MASK);
773 }
Hans de Goedec5a3b4b2014-12-21 16:27:45 +0100774 sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200775}
Hans de Goede260f5202014-12-25 13:58:06 +0100776#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA */
777
778#ifdef CONFIG_VIDEO_HDMI
Hans de Goedef651e0a2014-11-14 17:42:14 +0100779
Hans de Goedea2017e82014-12-20 13:38:06 +0100780static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
781{
782 struct sunxi_hdmi_reg * const hdmi =
783 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
784 u8 checksum = 0;
785 u8 avi_info_frame[17] = {
786 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00,
787 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
788 0x00
789 };
790 u8 vendor_info_frame[19] = {
791 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40,
792 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
793 0x00, 0x00, 0x00
794 };
795 int i;
796
797 if (mode->pixclock_khz <= 27000)
798 avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */
799 else
800 avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */
801
802 if (mode->xres * 100 / mode->yres < 156)
803 avi_info_frame[5] |= 0x18; /* 4 : 3 */
804 else
805 avi_info_frame[5] |= 0x28; /* 16 : 9 */
806
807 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
808 checksum += avi_info_frame[i];
809
810 avi_info_frame[3] = 0x100 - checksum;
811
812 for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
813 writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]);
814
815 writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0);
816 writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1);
817
818 for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++)
819 writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]);
820
821 writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0);
822 writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1);
823
824 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI);
825}
826
Hans de Goedeccb0ed52014-12-19 13:46:33 +0100827static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
Hans de Goedea0b1b732014-12-21 14:37:45 +0100828 int clk_div, int clk_double)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200829{
830 struct sunxi_hdmi_reg * const hdmi =
831 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
832 int x, y;
833
834 /* Write clear interrupt status bits */
835 writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq);
836
Hans de Goedea0b1b732014-12-21 14:37:45 +0100837 if (sunxi_display.monitor == sunxi_monitor_hdmi)
Hans de Goedea2017e82014-12-20 13:38:06 +0100838 sunxi_hdmi_setup_info_frames(mode);
839
Hans de Goede95576692014-12-20 13:51:16 +0100840 /* Set input sync enable */
841 writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown);
842
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200843 /* Init various registers, select pll3 as clock source */
844 writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity);
845 writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0);
846 writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1);
847 writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl);
848 writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
849
850 /* Setup clk div and doubler */
851 clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK,
852 SUNXI_HDMI_PLL_CTRL_DIV(clk_div));
853 if (!clk_double)
854 setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE);
855
856 /* Setup timing registers */
857 writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres),
858 &hdmi->video_size);
859
860 x = mode->hsync_len + mode->left_margin;
861 y = mode->vsync_len + mode->upper_margin;
862 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp);
863
864 x = mode->right_margin;
865 y = mode->lower_margin;
866 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp);
867
868 x = mode->hsync_len;
869 y = mode->vsync_len;
870 writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw);
871
872 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
873 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR);
874
875 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
876 setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER);
877}
878
Hans de Goede4125f922014-12-21 14:49:34 +0100879static void sunxi_hdmi_enable(void)
880{
881 struct sunxi_hdmi_reg * const hdmi =
882 (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
883
884 udelay(100);
885 setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE);
886}
887
Hans de Goedee9544592014-12-23 23:04:35 +0100888#endif /* CONFIG_VIDEO_HDMI */
889
Hans de Goede260f5202014-12-25 13:58:06 +0100890#ifdef CONFIG_VIDEO_VGA
891
892static void sunxi_vga_mode_set(void)
893{
894 struct sunxi_ccm_reg * const ccm =
895 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
896 struct sunxi_tve_reg * const tve =
897 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
898
899 /* Clock on */
900 setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
901
902 /* Set TVE in VGA mode */
903 writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
904 SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
905 SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl);
906 writel(SUNXI_TVE_GCTRL_CFG0_VGA, &tve->cfg0);
907 writel(SUNXI_TVE_GCTRL_DAC_CFG0_VGA, &tve->dac_cfg0);
908 writel(SUNXI_TVE_GCTRL_UNKNOWN1_VGA, &tve->unknown1);
909}
910
911static void sunxi_vga_enable(void)
912{
913 struct sunxi_tve_reg * const tve =
914 (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
915
916 setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE);
917}
918
919#endif /* CONFIG_VIDEO_VGA */
920
Hans de Goede115e4b42014-12-23 18:39:52 +0100921static void sunxi_drc_init(void)
922{
Hans de Goedee9544592014-12-23 23:04:35 +0100923#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
Hans de Goede115e4b42014-12-23 18:39:52 +0100924 struct sunxi_ccm_reg * const ccm =
925 (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
926
927 /* On sun6i the drc must be clocked even when in pass-through mode */
928 setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0);
929 clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000);
930#endif
931}
932
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +0800933#ifdef CONFIG_VIDEO_VGA_VIA_LCD
934static void sunxi_vga_external_dac_enable(void)
935{
936 int pin;
937
938 pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
939 if (pin != -1) {
940 gpio_request(pin, "vga_enable");
941 gpio_direction_output(pin, 1);
942 }
943}
944#endif /* CONFIG_VIDEO_VGA_VIA_LCD */
945
Siarhei Siamashkac02f0522015-01-19 05:23:33 +0200946#ifdef CONFIG_VIDEO_LCD_SSD2828
947static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
948{
949 struct ssd2828_config cfg = {
950 .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
951 .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
952 .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
953 .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
954 .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
955 .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
956 .ssd2828_color_depth = 24,
957#ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
958 .mipi_dsi_number_of_data_lanes = 4,
959 .mipi_dsi_bitrate_per_data_lane_mbps = 513,
960 .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
961 .mipi_dsi_delay_after_set_display_on_ms = 200
962#else
963#error MIPI LCD panel needs configuration parameters
964#endif
965 };
966
967 if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
968 printf("SSD2828: SPI pins are not properly configured\n");
969 return 1;
970 }
971 if (cfg.reset_pin == -1) {
972 printf("SSD2828: Reset pin is not properly configured\n");
973 return 1;
974 }
975
976 return ssd2828_init(&cfg, mode);
977}
978#endif /* CONFIG_VIDEO_LCD_SSD2828 */
979
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200980static void sunxi_engines_init(void)
981{
982 sunxi_composer_init();
983 sunxi_lcdc_init();
Hans de Goedef651e0a2014-11-14 17:42:14 +0100984 sunxi_drc_init();
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200985}
986
Hans de Goedea0b1b732014-12-21 14:37:45 +0100987static void sunxi_mode_set(const struct ctfb_res_modes *mode,
Hans de Goedea2017e82014-12-20 13:38:06 +0100988 unsigned int address)
Luc Verhaegenb01df1e2014-08-13 07:55:06 +0200989{
Hans de Goede260f5202014-12-25 13:58:06 +0100990 int __maybe_unused clk_div, clk_double;
991
Hans de Goede4125f922014-12-21 14:49:34 +0100992 switch (sunxi_display.monitor) {
993 case sunxi_monitor_none:
994 break;
995 case sunxi_monitor_dvi:
Hans de Goede260f5202014-12-25 13:58:06 +0100996 case sunxi_monitor_hdmi:
Hans de Goedee9544592014-12-23 23:04:35 +0100997#ifdef CONFIG_VIDEO_HDMI
Hans de Goede4125f922014-12-21 14:49:34 +0100998 sunxi_composer_mode_set(mode, address);
Hans de Goedec3d15042014-12-27 15:19:23 +0100999 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
Hans de Goede4125f922014-12-21 14:49:34 +01001000 sunxi_hdmi_mode_set(mode, clk_div, clk_double);
1001 sunxi_composer_enable();
1002 sunxi_lcdc_enable();
1003 sunxi_hdmi_enable();
Hans de Goedee9544592014-12-23 23:04:35 +01001004#endif
Hans de Goede4125f922014-12-21 14:49:34 +01001005 break;
1006 case sunxi_monitor_lcd:
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001007 sunxi_lcdc_panel_enable();
Hans de Goede743fb9552015-01-20 09:23:36 +01001008 if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
1009 mdelay(50); /* Wait for lcd controller power on */
1010 hitachi_tx18d42vm_init();
1011 }
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001012 sunxi_composer_mode_set(mode, address);
1013 sunxi_lcdc_tcon0_mode_set(mode);
1014 sunxi_composer_enable();
1015 sunxi_lcdc_enable();
Siarhei Siamashkac02f0522015-01-19 05:23:33 +02001016#ifdef CONFIG_VIDEO_LCD_SSD2828
1017 sunxi_ssd2828_init(mode);
1018#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001019 sunxi_lcdc_backlight_enable();
Hans de Goede4125f922014-12-21 14:49:34 +01001020 break;
1021 case sunxi_monitor_vga:
Hans de Goede260f5202014-12-25 13:58:06 +01001022#ifdef CONFIG_VIDEO_VGA
1023 sunxi_composer_mode_set(mode, address);
1024 sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
1025 sunxi_vga_mode_set();
1026 sunxi_composer_enable();
1027 sunxi_lcdc_enable();
1028 sunxi_vga_enable();
1029#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goedeac1633c2014-12-24 12:17:07 +01001030 sunxi_composer_mode_set(mode, address);
1031 sunxi_lcdc_tcon0_mode_set(mode);
1032 sunxi_composer_enable();
1033 sunxi_lcdc_enable();
Chen-Yu Tsai9ed19522015-01-12 18:02:11 +08001034 sunxi_vga_external_dac_enable();
Hans de Goedeac1633c2014-12-24 12:17:07 +01001035#endif
Hans de Goede4125f922014-12-21 14:49:34 +01001036 break;
1037 }
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001038}
1039
Hans de Goedea0b1b732014-12-21 14:37:45 +01001040static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
1041{
1042 switch (monitor) {
1043 case sunxi_monitor_none: return "none";
1044 case sunxi_monitor_dvi: return "dvi";
1045 case sunxi_monitor_hdmi: return "hdmi";
1046 case sunxi_monitor_lcd: return "lcd";
1047 case sunxi_monitor_vga: return "vga";
1048 }
1049 return NULL; /* never reached */
1050}
1051
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001052void *video_hw_init(void)
1053{
1054 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001055 const struct ctfb_res_modes *mode;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001056 struct ctfb_res_modes custom;
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001057 const char *options;
Hans de Goedee9544592014-12-23 23:04:35 +01001058#ifdef CONFIG_VIDEO_HDMI
Hans de Goede91593712014-12-28 09:13:21 +01001059 int ret, hpd, hpd_delay, edid;
Hans de Goedee9544592014-12-23 23:04:35 +01001060#endif
Hans de Goedea0b1b732014-12-21 14:37:45 +01001061 char mon[16];
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001062 char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
Hans de Goedee9544592014-12-23 23:04:35 +01001063 int i;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001064
1065 memset(&sunxi_display, 0, sizeof(struct sunxi_display));
1066
1067 printf("Reserved %dkB of RAM for Framebuffer.\n",
1068 CONFIG_SUNXI_FB_SIZE >> 10);
1069 gd->fb_base = gd->ram_top;
1070
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001071 video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
1072 &sunxi_display.depth, &options);
Hans de Goedee9544592014-12-23 23:04:35 +01001073#ifdef CONFIG_VIDEO_HDMI
Hans de Goede695bda42014-12-19 15:13:57 +01001074 hpd = video_get_option_int(options, "hpd", 1);
Hans de Goede91593712014-12-28 09:13:21 +01001075 hpd_delay = video_get_option_int(options, "hpd_delay", 500);
Hans de Goedea5aa95f2014-12-19 16:05:12 +01001076 edid = video_get_option_int(options, "edid", 1);
Hans de Goedea0b1b732014-12-21 14:37:45 +01001077 sunxi_display.monitor = sunxi_monitor_dvi;
Hans de Goedeac1633c2014-12-24 12:17:07 +01001078#elif defined CONFIG_VIDEO_VGA_VIA_LCD
1079 sunxi_display.monitor = sunxi_monitor_vga;
Hans de Goedee9544592014-12-23 23:04:35 +01001080#else
1081 sunxi_display.monitor = sunxi_monitor_lcd;
1082#endif
Hans de Goedea0b1b732014-12-21 14:37:45 +01001083 video_get_option_string(options, "monitor", mon, sizeof(mon),
1084 sunxi_get_mon_desc(sunxi_display.monitor));
1085 for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
1086 if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
1087 sunxi_display.monitor = i;
1088 break;
1089 }
1090 }
1091 if (i > SUNXI_MONITOR_LAST)
1092 printf("Unknown monitor: '%s', falling back to '%s'\n",
1093 mon, sunxi_get_mon_desc(sunxi_display.monitor));
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001094
Hans de Goede7977ec22014-12-25 13:52:04 +01001095#ifdef CONFIG_VIDEO_HDMI
1096 /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
1097 if (sunxi_display.monitor == sunxi_monitor_dvi ||
1098 sunxi_display.monitor == sunxi_monitor_hdmi) {
1099 /* Always call hdp_detect, as it also enables clocks, etc. */
Hans de Goede91593712014-12-28 09:13:21 +01001100 ret = sunxi_hdmi_hpd_detect(hpd_delay);
Hans de Goede7977ec22014-12-25 13:52:04 +01001101 if (ret) {
1102 printf("HDMI connected: ");
1103 if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0)
1104 mode = &custom;
1105 } else if (hpd) {
1106 sunxi_hdmi_shutdown();
1107 /* Fallback to lcd / vga / none */
1108 if (lcd_mode[0]) {
1109 sunxi_display.monitor = sunxi_monitor_lcd;
1110 } else {
Hans de Goede260f5202014-12-25 13:58:06 +01001111#if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA
Hans de Goede7977ec22014-12-25 13:52:04 +01001112 sunxi_display.monitor = sunxi_monitor_vga;
1113#else
1114 sunxi_display.monitor = sunxi_monitor_none;
1115#endif
1116 }
1117 } /* else continue with hdmi/dvi without a cable connected */
1118 }
1119#endif
1120
Hans de Goede4125f922014-12-21 14:49:34 +01001121 switch (sunxi_display.monitor) {
1122 case sunxi_monitor_none:
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001123 return NULL;
Hans de Goede4125f922014-12-21 14:49:34 +01001124 case sunxi_monitor_dvi:
1125 case sunxi_monitor_hdmi:
Hans de Goede7977ec22014-12-25 13:52:04 +01001126#ifdef CONFIG_VIDEO_HDMI
1127 break;
1128#else
Hans de Goedee9544592014-12-23 23:04:35 +01001129 printf("HDMI/DVI not supported on this board\n");
Hans de Goede83243c42014-12-24 19:47:14 +01001130 sunxi_display.monitor = sunxi_monitor_none;
Hans de Goedee9544592014-12-23 23:04:35 +01001131 return NULL;
Hans de Goedee9544592014-12-23 23:04:35 +01001132#endif
Hans de Goede4125f922014-12-21 14:49:34 +01001133 case sunxi_monitor_lcd:
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001134 if (lcd_mode[0]) {
1135 sunxi_display.depth = video_get_params(&custom, lcd_mode);
1136 mode = &custom;
1137 break;
1138 }
Hans de Goede4125f922014-12-21 14:49:34 +01001139 printf("LCD not supported on this board\n");
Hans de Goede83243c42014-12-24 19:47:14 +01001140 sunxi_display.monitor = sunxi_monitor_none;
Hans de Goede4125f922014-12-21 14:49:34 +01001141 return NULL;
1142 case sunxi_monitor_vga:
Hans de Goede260f5202014-12-25 13:58:06 +01001143#if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA
Hans de Goedeac1633c2014-12-24 12:17:07 +01001144 sunxi_display.depth = 18;
1145 break;
1146#else
Hans de Goede4125f922014-12-21 14:49:34 +01001147 printf("VGA not supported on this board\n");
Hans de Goede83243c42014-12-24 19:47:14 +01001148 sunxi_display.monitor = sunxi_monitor_none;
Hans de Goede4125f922014-12-21 14:49:34 +01001149 return NULL;
Hans de Goedeac1633c2014-12-24 12:17:07 +01001150#endif
Hans de Goedea5aa95f2014-12-19 16:05:12 +01001151 }
1152
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001153 if (mode->vmode != FB_VMODE_NONINTERLACED) {
1154 printf("Only non-interlaced modes supported, falling back to 1024x768\n");
1155 mode = &res_mode_init[RES_MODE_1024x768];
1156 } else {
Hans de Goedea0b1b732014-12-21 14:37:45 +01001157 printf("Setting up a %dx%d %s console\n", mode->xres,
1158 mode->yres, sunxi_get_mon_desc(sunxi_display.monitor));
Hans de Goede3f21d2a2014-12-19 14:03:40 +01001159 }
1160
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001161 sunxi_engines_init();
Hans de Goedea0b1b732014-12-21 14:37:45 +01001162 sunxi_mode_set(mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE);
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001163
1164 /*
1165 * These are the only members of this structure that are used. All the
1166 * others are driver specific. There is nothing to decribe pitch or
1167 * stride, but we are lucky with our hw.
1168 */
1169 graphic_device->frameAdrs = gd->fb_base;
1170 graphic_device->gdfIndex = GDF_32BIT_X888RGB;
1171 graphic_device->gdfBytesPP = 4;
Hans de Goedeccb0ed52014-12-19 13:46:33 +01001172 graphic_device->winSizeX = mode->xres;
1173 graphic_device->winSizeY = mode->yres;
Luc Verhaegenb01df1e2014-08-13 07:55:06 +02001174
1175 return graphic_device;
1176}
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001177
1178/*
1179 * Simplefb support.
1180 */
1181#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
1182int sunxi_simplefb_setup(void *blob)
1183{
1184 static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
1185 int offset, ret;
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001186 const char *pipeline = NULL;
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001187
Hans de Goedec3cc4262015-01-19 08:44:07 +01001188#ifdef CONFIG_MACH_SUN4I
1189#define PIPELINE_PREFIX "de_fe0-"
1190#else
1191#define PIPELINE_PREFIX
1192#endif
1193
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001194 switch (sunxi_display.monitor) {
1195 case sunxi_monitor_none:
1196 return 0;
1197 case sunxi_monitor_dvi:
1198 case sunxi_monitor_hdmi:
Hans de Goedec3cc4262015-01-19 08:44:07 +01001199 pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi";
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001200 break;
1201 case sunxi_monitor_lcd:
Hans de Goedec3cc4262015-01-19 08:44:07 +01001202 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001203 break;
1204 case sunxi_monitor_vga:
Hans de Goede260f5202014-12-25 13:58:06 +01001205#ifdef CONFIG_VIDEO_VGA
Hans de Goedec3cc4262015-01-19 08:44:07 +01001206 pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
Hans de Goede260f5202014-12-25 13:58:06 +01001207#elif defined CONFIG_VIDEO_VGA_VIA_LCD
Hans de Goedec3cc4262015-01-19 08:44:07 +01001208 pipeline = PIPELINE_PREFIX "de_be0-lcd0";
Hans de Goede260f5202014-12-25 13:58:06 +01001209#endif
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001210 break;
1211 }
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001212
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001213 /* Find a prefilled simpefb node, matching out pipeline config */
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001214 offset = fdt_node_offset_by_compatible(blob, -1,
1215 "allwinner,simple-framebuffer");
1216 while (offset >= 0) {
1217 ret = fdt_find_string(blob, offset, "allwinner,pipeline",
Hans de Goede7e68a1b2014-12-21 16:28:32 +01001218 pipeline);
Luc Verhaegen4869a8c2014-08-13 07:55:07 +02001219 if (ret == 0)
1220 break;
1221 offset = fdt_node_offset_by_compatible(blob, offset,
1222 "allwinner,simple-framebuffer");
1223 }
1224 if (offset < 0) {
1225 eprintf("Cannot setup simplefb: node not found\n");
1226 return 0; /* Keep older kernels working */
1227 }
1228
1229 ret = fdt_setup_simplefb_node(blob, offset, gd->fb_base,
1230 graphic_device->winSizeX, graphic_device->winSizeY,
1231 graphic_device->winSizeX * graphic_device->gdfBytesPP,
1232 "x8r8g8b8");
1233 if (ret)
1234 eprintf("Cannot setup simplefb: Error setting properties\n");
1235
1236 return ret;
1237}
1238#endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */