15e023e7eSJernej Skrabec /*
25e023e7eSJernej Skrabec * Display driver for Allwinner SoCs.
35e023e7eSJernej Skrabec *
45e023e7eSJernej Skrabec * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
55e023e7eSJernej Skrabec * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
65e023e7eSJernej Skrabec *
75e023e7eSJernej Skrabec * SPDX-License-Identifier: GPL-2.0+
85e023e7eSJernej Skrabec */
95e023e7eSJernej Skrabec
105e023e7eSJernej Skrabec #include <common.h>
115e023e7eSJernej Skrabec
125e023e7eSJernej Skrabec #include <asm/arch/clock.h>
135e023e7eSJernej Skrabec #include <asm/arch/display.h>
145e023e7eSJernej Skrabec #include <asm/arch/gpio.h>
155e023e7eSJernej Skrabec #include <asm/arch/lcdc.h>
165e023e7eSJernej Skrabec #include <asm/arch/pwm.h>
17*c1080626SJernej Skrabec #include <asm/arch/tve.h>
185e023e7eSJernej Skrabec #include <asm/global_data.h>
195e023e7eSJernej Skrabec #include <asm/gpio.h>
205e023e7eSJernej Skrabec #include <asm/io.h>
215e023e7eSJernej Skrabec #include <axp_pmic.h>
225e023e7eSJernej Skrabec #include <errno.h>
235e023e7eSJernej Skrabec #include <fdtdec.h>
245e023e7eSJernej Skrabec #include <fdt_support.h>
255e023e7eSJernej Skrabec #include <i2c.h>
265e023e7eSJernej Skrabec #include <malloc.h>
275e023e7eSJernej Skrabec #include <video_fb.h>
285e023e7eSJernej Skrabec #include "../videomodes.h"
295e023e7eSJernej Skrabec #include "../anx9804.h"
305e023e7eSJernej Skrabec #include "../hitachi_tx18d42vm_lcd.h"
315e023e7eSJernej Skrabec #include "../ssd2828.h"
325e023e7eSJernej Skrabec
335e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW
345e023e7eSJernej Skrabec #define PWM_ON 0
355e023e7eSJernej Skrabec #define PWM_OFF 1
365e023e7eSJernej Skrabec #else
375e023e7eSJernej Skrabec #define PWM_ON 1
385e023e7eSJernej Skrabec #define PWM_OFF 0
395e023e7eSJernej Skrabec #endif
405e023e7eSJernej Skrabec
415e023e7eSJernej Skrabec DECLARE_GLOBAL_DATA_PTR;
425e023e7eSJernej Skrabec
435e023e7eSJernej Skrabec enum sunxi_monitor {
445e023e7eSJernej Skrabec sunxi_monitor_none,
455e023e7eSJernej Skrabec sunxi_monitor_dvi,
465e023e7eSJernej Skrabec sunxi_monitor_hdmi,
475e023e7eSJernej Skrabec sunxi_monitor_lcd,
485e023e7eSJernej Skrabec sunxi_monitor_vga,
495e023e7eSJernej Skrabec sunxi_monitor_composite_pal,
505e023e7eSJernej Skrabec sunxi_monitor_composite_ntsc,
515e023e7eSJernej Skrabec sunxi_monitor_composite_pal_m,
525e023e7eSJernej Skrabec sunxi_monitor_composite_pal_nc,
535e023e7eSJernej Skrabec };
545e023e7eSJernej Skrabec #define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc
555e023e7eSJernej Skrabec
565e023e7eSJernej Skrabec struct sunxi_display {
575e023e7eSJernej Skrabec GraphicDevice graphic_device;
585e023e7eSJernej Skrabec enum sunxi_monitor monitor;
595e023e7eSJernej Skrabec unsigned int depth;
605e023e7eSJernej Skrabec unsigned int fb_addr;
615e023e7eSJernej Skrabec unsigned int fb_size;
625e023e7eSJernej Skrabec } sunxi_display;
635e023e7eSJernej Skrabec
645e023e7eSJernej Skrabec const struct ctfb_res_modes composite_video_modes[2] = {
655e023e7eSJernej Skrabec /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */
665e023e7eSJernej Skrabec { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED },
675e023e7eSJernej Skrabec { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED },
685e023e7eSJernej Skrabec };
695e023e7eSJernej Skrabec
705e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI
715e023e7eSJernej Skrabec
725e023e7eSJernej Skrabec /*
735e023e7eSJernej Skrabec * Wait up to 200ms for value to be set in given part of reg.
745e023e7eSJernej Skrabec */
await_completion(u32 * reg,u32 mask,u32 val)755e023e7eSJernej Skrabec static int await_completion(u32 *reg, u32 mask, u32 val)
765e023e7eSJernej Skrabec {
775e023e7eSJernej Skrabec unsigned long tmo = timer_get_us() + 200000;
785e023e7eSJernej Skrabec
795e023e7eSJernej Skrabec while ((readl(reg) & mask) != val) {
805e023e7eSJernej Skrabec if (timer_get_us() > tmo) {
815e023e7eSJernej Skrabec printf("DDC: timeout reading EDID\n");
825e023e7eSJernej Skrabec return -ETIME;
835e023e7eSJernej Skrabec }
845e023e7eSJernej Skrabec }
855e023e7eSJernej Skrabec return 0;
865e023e7eSJernej Skrabec }
875e023e7eSJernej Skrabec
sunxi_hdmi_hpd_detect(int hpd_delay)885e023e7eSJernej Skrabec static int sunxi_hdmi_hpd_detect(int hpd_delay)
895e023e7eSJernej Skrabec {
905e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm =
915e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
925e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi =
935e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
945e023e7eSJernej Skrabec unsigned long tmo = timer_get_us() + hpd_delay * 1000;
955e023e7eSJernej Skrabec
965e023e7eSJernej Skrabec /* Set pll3 to 300MHz */
975e023e7eSJernej Skrabec clock_set_pll3(300000000);
985e023e7eSJernej Skrabec
995e023e7eSJernej Skrabec /* Set hdmi parent to pll3 */
1005e023e7eSJernej Skrabec clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK,
1015e023e7eSJernej Skrabec CCM_HDMI_CTRL_PLL3);
1025e023e7eSJernej Skrabec
1035e023e7eSJernej Skrabec /* Set ahb gating to pass */
1045e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I
1055e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
1065e023e7eSJernej Skrabec #endif
1075e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
1085e023e7eSJernej Skrabec
1095e023e7eSJernej Skrabec /* Clock on */
1105e023e7eSJernej Skrabec setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
1115e023e7eSJernej Skrabec
1125e023e7eSJernej Skrabec writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl);
1135e023e7eSJernej Skrabec writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0);
1145e023e7eSJernej Skrabec
1155e023e7eSJernej Skrabec while (timer_get_us() < tmo) {
1165e023e7eSJernej Skrabec if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT)
1175e023e7eSJernej Skrabec return 1;
1185e023e7eSJernej Skrabec }
1195e023e7eSJernej Skrabec
1205e023e7eSJernej Skrabec return 0;
1215e023e7eSJernej Skrabec }
1225e023e7eSJernej Skrabec
sunxi_hdmi_shutdown(void)1235e023e7eSJernej Skrabec static void sunxi_hdmi_shutdown(void)
1245e023e7eSJernej Skrabec {
1255e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm =
1265e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
1275e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi =
1285e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
1295e023e7eSJernej Skrabec
1305e023e7eSJernej Skrabec clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE);
1315e023e7eSJernej Skrabec clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE);
1325e023e7eSJernej Skrabec clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI);
1335e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I
1345e023e7eSJernej Skrabec clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI);
1355e023e7eSJernej Skrabec #endif
1365e023e7eSJernej Skrabec clock_set_pll3(0);
1375e023e7eSJernej Skrabec }
1385e023e7eSJernej Skrabec
sunxi_hdmi_ddc_do_command(u32 cmnd,int offset,int n)1395e023e7eSJernej Skrabec static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n)
1405e023e7eSJernej Skrabec {
1415e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi =
1425e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
1435e023e7eSJernej Skrabec
1445e023e7eSJernej Skrabec setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR);
1455e023e7eSJernej Skrabec writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) |
1465e023e7eSJernej Skrabec SUNXI_HMDI_DDC_ADDR_EDDC_ADDR |
1475e023e7eSJernej Skrabec SUNXI_HMDI_DDC_ADDR_OFFSET(offset) |
1485e023e7eSJernej Skrabec SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr);
1495e023e7eSJernej Skrabec #ifndef CONFIG_MACH_SUN6I
1505e023e7eSJernej Skrabec writel(n, &hdmi->ddc_byte_count);
1515e023e7eSJernej Skrabec writel(cmnd, &hdmi->ddc_cmnd);
1525e023e7eSJernej Skrabec #else
1535e023e7eSJernej Skrabec writel(n << 16 | cmnd, &hdmi->ddc_cmnd);
1545e023e7eSJernej Skrabec #endif
1555e023e7eSJernej Skrabec setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START);
1565e023e7eSJernej Skrabec
1575e023e7eSJernej Skrabec return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0);
1585e023e7eSJernej Skrabec }
1595e023e7eSJernej Skrabec
sunxi_hdmi_ddc_read(int offset,u8 * buf,int count)1605e023e7eSJernej Skrabec static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count)
1615e023e7eSJernej Skrabec {
1625e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi =
1635e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
1645e023e7eSJernej Skrabec int i, n;
1655e023e7eSJernej Skrabec
1665e023e7eSJernej Skrabec while (count > 0) {
1675e023e7eSJernej Skrabec if (count > 16)
1685e023e7eSJernej Skrabec n = 16;
1695e023e7eSJernej Skrabec else
1705e023e7eSJernej Skrabec n = count;
1715e023e7eSJernej Skrabec
1725e023e7eSJernej Skrabec if (sunxi_hdmi_ddc_do_command(
1735e023e7eSJernej Skrabec SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ,
1745e023e7eSJernej Skrabec offset, n))
1755e023e7eSJernej Skrabec return -ETIME;
1765e023e7eSJernej Skrabec
1775e023e7eSJernej Skrabec for (i = 0; i < n; i++)
1785e023e7eSJernej Skrabec *buf++ = readb(&hdmi->ddc_fifo_data);
1795e023e7eSJernej Skrabec
1805e023e7eSJernej Skrabec offset += n;
1815e023e7eSJernej Skrabec count -= n;
1825e023e7eSJernej Skrabec }
1835e023e7eSJernej Skrabec
1845e023e7eSJernej Skrabec return 0;
1855e023e7eSJernej Skrabec }
1865e023e7eSJernej Skrabec
sunxi_hdmi_edid_get_block(int block,u8 * buf)1875e023e7eSJernej Skrabec static int sunxi_hdmi_edid_get_block(int block, u8 *buf)
1885e023e7eSJernej Skrabec {
1895e023e7eSJernej Skrabec int r, retries = 2;
1905e023e7eSJernej Skrabec
1915e023e7eSJernej Skrabec do {
1925e023e7eSJernej Skrabec r = sunxi_hdmi_ddc_read(block * 128, buf, 128);
1935e023e7eSJernej Skrabec if (r)
1945e023e7eSJernej Skrabec continue;
1955e023e7eSJernej Skrabec r = edid_check_checksum(buf);
1965e023e7eSJernej Skrabec if (r) {
1975e023e7eSJernej Skrabec printf("EDID block %d: checksum error%s\n",
1985e023e7eSJernej Skrabec block, retries ? ", retrying" : "");
1995e023e7eSJernej Skrabec }
2005e023e7eSJernej Skrabec } while (r && retries--);
2015e023e7eSJernej Skrabec
2025e023e7eSJernej Skrabec return r;
2035e023e7eSJernej Skrabec }
2045e023e7eSJernej Skrabec
sunxi_hdmi_edid_get_mode(struct ctfb_res_modes * mode)2055e023e7eSJernej Skrabec static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode)
2065e023e7eSJernej Skrabec {
2075e023e7eSJernej Skrabec struct edid1_info edid1;
2085e023e7eSJernej Skrabec struct edid_cea861_info cea681[4];
2095e023e7eSJernej Skrabec struct edid_detailed_timing *t =
2105e023e7eSJernej Skrabec (struct edid_detailed_timing *)edid1.monitor_details.timing;
2115e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi =
2125e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
2135e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm =
2145e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
2155e023e7eSJernej Skrabec int i, r, ext_blocks = 0;
2165e023e7eSJernej Skrabec
2175e023e7eSJernej Skrabec /* SUNXI_HDMI_CTRL_ENABLE & PAD_CTRL0 are already set by hpd_detect */
2185e023e7eSJernej Skrabec writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE,
2195e023e7eSJernej Skrabec &hdmi->pad_ctrl1);
2205e023e7eSJernej Skrabec writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15),
2215e023e7eSJernej Skrabec &hdmi->pll_ctrl);
2225e023e7eSJernej Skrabec writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
2235e023e7eSJernej Skrabec
2245e023e7eSJernej Skrabec /* Reset i2c controller */
2255e023e7eSJernej Skrabec setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
2265e023e7eSJernej Skrabec writel(SUNXI_HMDI_DDC_CTRL_ENABLE |
2275e023e7eSJernej Skrabec SUNXI_HMDI_DDC_CTRL_SDA_ENABLE |
2285e023e7eSJernej Skrabec SUNXI_HMDI_DDC_CTRL_SCL_ENABLE |
2295e023e7eSJernej Skrabec SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl);
2305e023e7eSJernej Skrabec if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0))
2315e023e7eSJernej Skrabec return -EIO;
2325e023e7eSJernej Skrabec
2335e023e7eSJernej Skrabec writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock);
2345e023e7eSJernej Skrabec #ifndef CONFIG_MACH_SUN6I
2355e023e7eSJernej Skrabec writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE |
2365e023e7eSJernej Skrabec SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl);
2375e023e7eSJernej Skrabec #endif
2385e023e7eSJernej Skrabec
2395e023e7eSJernej Skrabec r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1);
2405e023e7eSJernej Skrabec if (r == 0) {
2415e023e7eSJernej Skrabec r = edid_check_info(&edid1);
2425e023e7eSJernej Skrabec if (r) {
2435e023e7eSJernej Skrabec printf("EDID: invalid EDID data\n");
2445e023e7eSJernej Skrabec r = -EINVAL;
2455e023e7eSJernej Skrabec }
2465e023e7eSJernej Skrabec }
2475e023e7eSJernej Skrabec if (r == 0) {
2485e023e7eSJernej Skrabec ext_blocks = edid1.extension_flag;
2495e023e7eSJernej Skrabec if (ext_blocks > 4)
2505e023e7eSJernej Skrabec ext_blocks = 4;
2515e023e7eSJernej Skrabec for (i = 0; i < ext_blocks; i++) {
2525e023e7eSJernej Skrabec if (sunxi_hdmi_edid_get_block(1 + i,
2535e023e7eSJernej Skrabec (u8 *)&cea681[i]) != 0) {
2545e023e7eSJernej Skrabec ext_blocks = i;
2555e023e7eSJernej Skrabec break;
2565e023e7eSJernej Skrabec }
2575e023e7eSJernej Skrabec }
2585e023e7eSJernej Skrabec }
2595e023e7eSJernej Skrabec
2605e023e7eSJernej Skrabec /* Disable DDC engine, no longer needed */
2615e023e7eSJernej Skrabec clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE);
2625e023e7eSJernej Skrabec clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE);
2635e023e7eSJernej Skrabec
2645e023e7eSJernej Skrabec if (r)
2655e023e7eSJernej Skrabec return r;
2665e023e7eSJernej Skrabec
2675e023e7eSJernej Skrabec /* We want version 1.3 or 1.2 with detailed timing info */
2685e023e7eSJernej Skrabec if (edid1.version != 1 || (edid1.revision < 3 &&
2695e023e7eSJernej Skrabec !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) {
2705e023e7eSJernej Skrabec printf("EDID: unsupported version %d.%d\n",
2715e023e7eSJernej Skrabec edid1.version, edid1.revision);
2725e023e7eSJernej Skrabec return -EINVAL;
2735e023e7eSJernej Skrabec }
2745e023e7eSJernej Skrabec
2755e023e7eSJernej Skrabec /* Take the first usable detailed timing */
2765e023e7eSJernej Skrabec for (i = 0; i < 4; i++, t++) {
2775e023e7eSJernej Skrabec r = video_edid_dtd_to_ctfb_res_modes(t, mode);
2785e023e7eSJernej Skrabec if (r == 0)
2795e023e7eSJernej Skrabec break;
2805e023e7eSJernej Skrabec }
2815e023e7eSJernej Skrabec if (i == 4) {
2825e023e7eSJernej Skrabec printf("EDID: no usable detailed timing found\n");
2835e023e7eSJernej Skrabec return -ENOENT;
2845e023e7eSJernej Skrabec }
2855e023e7eSJernej Skrabec
2865e023e7eSJernej Skrabec /* Check for basic audio support, if found enable hdmi output */
2875e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_dvi;
2885e023e7eSJernej Skrabec for (i = 0; i < ext_blocks; i++) {
2895e023e7eSJernej Skrabec if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG ||
2905e023e7eSJernej Skrabec cea681[i].revision < 2)
2915e023e7eSJernej Skrabec continue;
2925e023e7eSJernej Skrabec
2935e023e7eSJernej Skrabec if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i]))
2945e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_hdmi;
2955e023e7eSJernej Skrabec }
2965e023e7eSJernej Skrabec
2975e023e7eSJernej Skrabec return 0;
2985e023e7eSJernej Skrabec }
2995e023e7eSJernej Skrabec
3005e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_HDMI */
3015e023e7eSJernej Skrabec
3025e023e7eSJernej Skrabec #ifdef CONFIG_MACH_SUN4I
3035e023e7eSJernej Skrabec /*
3045e023e7eSJernej Skrabec * Testing has shown that on sun4i the display backend engine does not have
3055e023e7eSJernej Skrabec * deep enough fifo-s causing flickering / tearing in full-hd mode due to
3065e023e7eSJernej Skrabec * fifo underruns. So on sun4i we use the display frontend engine to do the
3075e023e7eSJernej Skrabec * dma from memory, as the frontend does have deep enough fifo-s.
3085e023e7eSJernej Skrabec */
3095e023e7eSJernej Skrabec
3105e023e7eSJernej Skrabec static const u32 sun4i_vert_coef[32] = {
3115e023e7eSJernej Skrabec 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
3125e023e7eSJernej Skrabec 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
3135e023e7eSJernej Skrabec 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
3145e023e7eSJernej Skrabec 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
3155e023e7eSJernej Skrabec 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
3165e023e7eSJernej Skrabec 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
3175e023e7eSJernej Skrabec 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
3185e023e7eSJernej Skrabec 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
3195e023e7eSJernej Skrabec };
3205e023e7eSJernej Skrabec
3215e023e7eSJernej Skrabec static const u32 sun4i_horz_coef[64] = {
3225e023e7eSJernej Skrabec 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
3235e023e7eSJernej Skrabec 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
3245e023e7eSJernej Skrabec 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
3255e023e7eSJernej Skrabec 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
3265e023e7eSJernej Skrabec 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
3275e023e7eSJernej Skrabec 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
3285e023e7eSJernej Skrabec 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
3295e023e7eSJernej Skrabec 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
3305e023e7eSJernej Skrabec 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
3315e023e7eSJernej Skrabec 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
3325e023e7eSJernej Skrabec 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
3335e023e7eSJernej Skrabec 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
3345e023e7eSJernej Skrabec 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
3355e023e7eSJernej Skrabec 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
3365e023e7eSJernej Skrabec 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
3375e023e7eSJernej Skrabec 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
3385e023e7eSJernej Skrabec };
3395e023e7eSJernej Skrabec
sunxi_frontend_init(void)3405e023e7eSJernej Skrabec static void sunxi_frontend_init(void)
3415e023e7eSJernej Skrabec {
3425e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm =
3435e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
3445e023e7eSJernej Skrabec struct sunxi_de_fe_reg * const de_fe =
3455e023e7eSJernej Skrabec (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
3465e023e7eSJernej Skrabec int i;
3475e023e7eSJernej Skrabec
3485e023e7eSJernej Skrabec /* Clocks on */
3495e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0);
3505e023e7eSJernej Skrabec setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0);
3515e023e7eSJernej Skrabec clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000);
3525e023e7eSJernej Skrabec
3535e023e7eSJernej Skrabec setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN);
3545e023e7eSJernej Skrabec
3555e023e7eSJernej Skrabec for (i = 0; i < 32; i++) {
3565e023e7eSJernej Skrabec writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]);
3575e023e7eSJernej Skrabec writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]);
3585e023e7eSJernej Skrabec writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]);
3595e023e7eSJernej Skrabec writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]);
3605e023e7eSJernej Skrabec writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]);
3615e023e7eSJernej Skrabec writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]);
3625e023e7eSJernej Skrabec }
3635e023e7eSJernej Skrabec
3645e023e7eSJernej Skrabec setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY);
3655e023e7eSJernej Skrabec }
3665e023e7eSJernej Skrabec
sunxi_frontend_mode_set(const struct ctfb_res_modes * mode,unsigned int address)3675e023e7eSJernej Skrabec static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
3685e023e7eSJernej Skrabec unsigned int address)
3695e023e7eSJernej Skrabec {
3705e023e7eSJernej Skrabec struct sunxi_de_fe_reg * const de_fe =
3715e023e7eSJernej Skrabec (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
3725e023e7eSJernej Skrabec
3735e023e7eSJernej Skrabec setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS);
3745e023e7eSJernej Skrabec writel(CONFIG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr);
3755e023e7eSJernej Skrabec writel(mode->xres * 4, &de_fe->ch0_stride);
3765e023e7eSJernej Skrabec writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt);
3775e023e7eSJernej Skrabec writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt);
3785e023e7eSJernej Skrabec
3795e023e7eSJernej Skrabec writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
3805e023e7eSJernej Skrabec &de_fe->ch0_insize);
3815e023e7eSJernej Skrabec writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
3825e023e7eSJernej Skrabec &de_fe->ch0_outsize);
3835e023e7eSJernej Skrabec writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact);
3845e023e7eSJernej Skrabec writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact);
3855e023e7eSJernej Skrabec
3865e023e7eSJernej Skrabec writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
3875e023e7eSJernej Skrabec &de_fe->ch1_insize);
3885e023e7eSJernej Skrabec writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres),
3895e023e7eSJernej Skrabec &de_fe->ch1_outsize);
3905e023e7eSJernej Skrabec writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact);
3915e023e7eSJernej Skrabec writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact);
3925e023e7eSJernej Skrabec
3935e023e7eSJernej Skrabec setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY);
3945e023e7eSJernej Skrabec }
3955e023e7eSJernej Skrabec
sunxi_frontend_enable(void)3965e023e7eSJernej Skrabec static void sunxi_frontend_enable(void)
3975e023e7eSJernej Skrabec {
3985e023e7eSJernej Skrabec struct sunxi_de_fe_reg * const de_fe =
3995e023e7eSJernej Skrabec (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE;
4005e023e7eSJernej Skrabec
4015e023e7eSJernej Skrabec setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START);
4025e023e7eSJernej Skrabec }
4035e023e7eSJernej Skrabec #else
sunxi_frontend_init(void)4045e023e7eSJernej Skrabec static void sunxi_frontend_init(void) {}
sunxi_frontend_mode_set(const struct ctfb_res_modes * mode,unsigned int address)4055e023e7eSJernej Skrabec static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode,
4065e023e7eSJernej Skrabec unsigned int address) {}
sunxi_frontend_enable(void)4075e023e7eSJernej Skrabec static void sunxi_frontend_enable(void) {}
4085e023e7eSJernej Skrabec #endif
4095e023e7eSJernej Skrabec
sunxi_is_composite(void)4105e023e7eSJernej Skrabec static bool sunxi_is_composite(void)
4115e023e7eSJernej Skrabec {
4125e023e7eSJernej Skrabec switch (sunxi_display.monitor) {
4135e023e7eSJernej Skrabec case sunxi_monitor_none:
4145e023e7eSJernej Skrabec case sunxi_monitor_dvi:
4155e023e7eSJernej Skrabec case sunxi_monitor_hdmi:
4165e023e7eSJernej Skrabec case sunxi_monitor_lcd:
4175e023e7eSJernej Skrabec case sunxi_monitor_vga:
4185e023e7eSJernej Skrabec return false;
4195e023e7eSJernej Skrabec case sunxi_monitor_composite_pal:
4205e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc:
4215e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m:
4225e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc:
4235e023e7eSJernej Skrabec return true;
4245e023e7eSJernej Skrabec }
4255e023e7eSJernej Skrabec
4265e023e7eSJernej Skrabec return false; /* Never reached */
4275e023e7eSJernej Skrabec }
4285e023e7eSJernej Skrabec
4295e023e7eSJernej Skrabec /*
4305e023e7eSJernej Skrabec * This is the entity that mixes and matches the different layers and inputs.
4315e023e7eSJernej Skrabec * Allwinner calls it the back-end, but i like composer better.
4325e023e7eSJernej Skrabec */
sunxi_composer_init(void)4335e023e7eSJernej Skrabec static void sunxi_composer_init(void)
4345e023e7eSJernej Skrabec {
4355e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm =
4365e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
4375e023e7eSJernej Skrabec struct sunxi_de_be_reg * const de_be =
4385e023e7eSJernej Skrabec (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
4395e023e7eSJernej Skrabec int i;
4405e023e7eSJernej Skrabec
4415e023e7eSJernej Skrabec sunxi_frontend_init();
4425e023e7eSJernej Skrabec
4435e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I
4445e023e7eSJernej Skrabec /* Reset off */
4455e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0);
4465e023e7eSJernej Skrabec #endif
4475e023e7eSJernej Skrabec
4485e023e7eSJernej Skrabec /* Clocks on */
4495e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0);
4505e023e7eSJernej Skrabec #ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
4515e023e7eSJernej Skrabec setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0);
4525e023e7eSJernej Skrabec #endif
4535e023e7eSJernej Skrabec clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000);
4545e023e7eSJernej Skrabec
4555e023e7eSJernej Skrabec /* Engine bug, clear registers after reset */
4565e023e7eSJernej Skrabec for (i = 0x0800; i < 0x1000; i += 4)
4575e023e7eSJernej Skrabec writel(0, SUNXI_DE_BE0_BASE + i);
4585e023e7eSJernej Skrabec
4595e023e7eSJernej Skrabec setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE);
4605e023e7eSJernej Skrabec }
4615e023e7eSJernej Skrabec
4625e023e7eSJernej Skrabec static u32 sunxi_rgb2yuv_coef[12] = {
4635e023e7eSJernej Skrabec 0x00000107, 0x00000204, 0x00000064, 0x00000108,
4645e023e7eSJernej Skrabec 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,
4655e023e7eSJernej Skrabec 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
4665e023e7eSJernej Skrabec };
4675e023e7eSJernej Skrabec
sunxi_composer_mode_set(const struct ctfb_res_modes * mode,unsigned int address)4685e023e7eSJernej Skrabec static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode,
4695e023e7eSJernej Skrabec unsigned int address)
4705e023e7eSJernej Skrabec {
4715e023e7eSJernej Skrabec struct sunxi_de_be_reg * const de_be =
4725e023e7eSJernej Skrabec (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
4735e023e7eSJernej Skrabec int i;
4745e023e7eSJernej Skrabec
4755e023e7eSJernej Skrabec sunxi_frontend_mode_set(mode, address);
4765e023e7eSJernej Skrabec
4775e023e7eSJernej Skrabec writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
4785e023e7eSJernej Skrabec &de_be->disp_size);
4795e023e7eSJernej Skrabec writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres),
4805e023e7eSJernej Skrabec &de_be->layer0_size);
4815e023e7eSJernej Skrabec #ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */
4825e023e7eSJernej Skrabec writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride);
4835e023e7eSJernej Skrabec writel(address << 3, &de_be->layer0_addr_low32b);
4845e023e7eSJernej Skrabec writel(address >> 29, &de_be->layer0_addr_high4b);
4855e023e7eSJernej Skrabec #else
4865e023e7eSJernej Skrabec writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl);
4875e023e7eSJernej Skrabec #endif
4885e023e7eSJernej Skrabec writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl);
4895e023e7eSJernej Skrabec
4905e023e7eSJernej Skrabec setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE);
4915e023e7eSJernej Skrabec if (mode->vmode == FB_VMODE_INTERLACED)
4925e023e7eSJernej Skrabec setbits_le32(&de_be->mode,
4935e023e7eSJernej Skrabec #ifndef CONFIG_MACH_SUN5I
4945e023e7eSJernej Skrabec SUNXI_DE_BE_MODE_DEFLICKER_ENABLE |
4955e023e7eSJernej Skrabec #endif
4965e023e7eSJernej Skrabec SUNXI_DE_BE_MODE_INTERLACE_ENABLE);
4975e023e7eSJernej Skrabec
4985e023e7eSJernej Skrabec if (sunxi_is_composite()) {
4995e023e7eSJernej Skrabec writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE,
5005e023e7eSJernej Skrabec &de_be->output_color_ctrl);
5015e023e7eSJernej Skrabec for (i = 0; i < 12; i++)
5025e023e7eSJernej Skrabec writel(sunxi_rgb2yuv_coef[i],
5035e023e7eSJernej Skrabec &de_be->output_color_coef[i]);
5045e023e7eSJernej Skrabec }
5055e023e7eSJernej Skrabec }
5065e023e7eSJernej Skrabec
sunxi_composer_enable(void)5075e023e7eSJernej Skrabec static void sunxi_composer_enable(void)
5085e023e7eSJernej Skrabec {
5095e023e7eSJernej Skrabec struct sunxi_de_be_reg * const de_be =
5105e023e7eSJernej Skrabec (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE;
5115e023e7eSJernej Skrabec
5125e023e7eSJernej Skrabec sunxi_frontend_enable();
5135e023e7eSJernej Skrabec
5145e023e7eSJernej Skrabec setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS);
5155e023e7eSJernej Skrabec setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
5165e023e7eSJernej Skrabec }
5175e023e7eSJernej Skrabec
5185e023e7eSJernej Skrabec /*
5195e023e7eSJernej Skrabec * LCDC, what allwinner calls a CRTC, so timing controller and serializer.
5205e023e7eSJernej Skrabec */
sunxi_lcdc_pll_set(int tcon,int dotclock,int * clk_div,int * clk_double)5215e023e7eSJernej Skrabec static void sunxi_lcdc_pll_set(int tcon, int dotclock,
5225e023e7eSJernej Skrabec int *clk_div, int *clk_double)
5235e023e7eSJernej Skrabec {
5245e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm =
5255e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
5265e023e7eSJernej Skrabec int value, n, m, min_m, max_m, diff;
5275e023e7eSJernej Skrabec int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF;
5285e023e7eSJernej Skrabec int best_double = 0;
5295e023e7eSJernej Skrabec bool use_mipi_pll = false;
5305e023e7eSJernej Skrabec
5315e023e7eSJernej Skrabec if (tcon == 0) {
5325e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
5335e023e7eSJernej Skrabec min_m = 6;
5345e023e7eSJernej Skrabec max_m = 127;
5355e023e7eSJernej Skrabec #endif
5365e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_LVDS
5375e023e7eSJernej Skrabec min_m = max_m = 7;
5385e023e7eSJernej Skrabec #endif
5395e023e7eSJernej Skrabec } else {
5405e023e7eSJernej Skrabec min_m = 1;
5415e023e7eSJernej Skrabec max_m = 15;
5425e023e7eSJernej Skrabec }
5435e023e7eSJernej Skrabec
5445e023e7eSJernej Skrabec /*
5455e023e7eSJernej Skrabec * Find the lowest divider resulting in a matching clock, if there
5465e023e7eSJernej Skrabec * is no match, pick the closest lower clock, as monitors tend to
5475e023e7eSJernej Skrabec * not sync to higher frequencies.
5485e023e7eSJernej Skrabec */
5495e023e7eSJernej Skrabec for (m = min_m; m <= max_m; m++) {
5505e023e7eSJernej Skrabec n = (m * dotclock) / 3000;
5515e023e7eSJernej Skrabec
5525e023e7eSJernej Skrabec if ((n >= 9) && (n <= 127)) {
5535e023e7eSJernej Skrabec value = (3000 * n) / m;
5545e023e7eSJernej Skrabec diff = dotclock - value;
5555e023e7eSJernej Skrabec if (diff < best_diff) {
5565e023e7eSJernej Skrabec best_diff = diff;
5575e023e7eSJernej Skrabec best_m = m;
5585e023e7eSJernej Skrabec best_n = n;
5595e023e7eSJernej Skrabec best_double = 0;
5605e023e7eSJernej Skrabec }
5615e023e7eSJernej Skrabec }
5625e023e7eSJernej Skrabec
5635e023e7eSJernej Skrabec /* These are just duplicates */
5645e023e7eSJernej Skrabec if (!(m & 1))
5655e023e7eSJernej Skrabec continue;
5665e023e7eSJernej Skrabec
5675e023e7eSJernej Skrabec n = (m * dotclock) / 6000;
5685e023e7eSJernej Skrabec if ((n >= 9) && (n <= 127)) {
5695e023e7eSJernej Skrabec value = (6000 * n) / m;
5705e023e7eSJernej Skrabec diff = dotclock - value;
5715e023e7eSJernej Skrabec if (diff < best_diff) {
5725e023e7eSJernej Skrabec best_diff = diff;
5735e023e7eSJernej Skrabec best_m = m;
5745e023e7eSJernej Skrabec best_n = n;
5755e023e7eSJernej Skrabec best_double = 1;
5765e023e7eSJernej Skrabec }
5775e023e7eSJernej Skrabec }
5785e023e7eSJernej Skrabec }
5795e023e7eSJernej Skrabec
5805e023e7eSJernej Skrabec #ifdef CONFIG_MACH_SUN6I
5815e023e7eSJernej Skrabec /*
5825e023e7eSJernej Skrabec * Use the MIPI pll if we've been unable to find any matching setting
5835e023e7eSJernej Skrabec * for PLL3, this happens with high dotclocks because of min_m = 6.
5845e023e7eSJernej Skrabec */
5855e023e7eSJernej Skrabec if (tcon == 0 && best_n == 0) {
5865e023e7eSJernej Skrabec use_mipi_pll = true;
5875e023e7eSJernej Skrabec best_m = 6; /* Minimum m for tcon0 */
5885e023e7eSJernej Skrabec }
5895e023e7eSJernej Skrabec
5905e023e7eSJernej Skrabec if (use_mipi_pll) {
5915e023e7eSJernej Skrabec clock_set_pll3(297000000); /* Fix the video pll at 297 MHz */
5925e023e7eSJernej Skrabec clock_set_mipi_pll(best_m * dotclock * 1000);
5935e023e7eSJernej Skrabec debug("dotclock: %dkHz = %dkHz via mipi pll\n",
5945e023e7eSJernej Skrabec dotclock, clock_get_mipi_pll() / best_m / 1000);
5955e023e7eSJernej Skrabec } else
5965e023e7eSJernej Skrabec #endif
5975e023e7eSJernej Skrabec {
5985e023e7eSJernej Skrabec clock_set_pll3(best_n * 3000000);
5995e023e7eSJernej Skrabec debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n",
6005e023e7eSJernej Skrabec dotclock,
6015e023e7eSJernej Skrabec (best_double + 1) * clock_get_pll3() / best_m / 1000,
6025e023e7eSJernej Skrabec best_double + 1, best_n, best_m);
6035e023e7eSJernej Skrabec }
6045e023e7eSJernej Skrabec
6055e023e7eSJernej Skrabec if (tcon == 0) {
6065e023e7eSJernej Skrabec u32 pll;
6075e023e7eSJernej Skrabec
6085e023e7eSJernej Skrabec if (use_mipi_pll)
6095e023e7eSJernej Skrabec pll = CCM_LCD_CH0_CTRL_MIPI_PLL;
6105e023e7eSJernej Skrabec else if (best_double)
6115e023e7eSJernej Skrabec pll = CCM_LCD_CH0_CTRL_PLL3_2X;
6125e023e7eSJernej Skrabec else
6135e023e7eSJernej Skrabec pll = CCM_LCD_CH0_CTRL_PLL3;
6145e023e7eSJernej Skrabec
6155e023e7eSJernej Skrabec writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll,
6165e023e7eSJernej Skrabec &ccm->lcd0_ch0_clk_cfg);
6175e023e7eSJernej Skrabec } else {
6185e023e7eSJernej Skrabec writel(CCM_LCD_CH1_CTRL_GATE |
6195e023e7eSJernej Skrabec (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X :
6205e023e7eSJernej Skrabec CCM_LCD_CH1_CTRL_PLL3) |
6215e023e7eSJernej Skrabec CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg);
6225e023e7eSJernej Skrabec if (sunxi_is_composite())
6235e023e7eSJernej Skrabec setbits_le32(&ccm->lcd0_ch1_clk_cfg,
6245e023e7eSJernej Skrabec CCM_LCD_CH1_CTRL_HALF_SCLK1);
6255e023e7eSJernej Skrabec }
6265e023e7eSJernej Skrabec
6275e023e7eSJernej Skrabec *clk_div = best_m;
6285e023e7eSJernej Skrabec *clk_double = best_double;
6295e023e7eSJernej Skrabec }
6305e023e7eSJernej Skrabec
sunxi_lcdc_init(void)6315e023e7eSJernej Skrabec static void sunxi_lcdc_init(void)
6325e023e7eSJernej Skrabec {
6335e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm =
6345e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
6355e023e7eSJernej Skrabec struct sunxi_lcdc_reg * const lcdc =
6365e023e7eSJernej Skrabec (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
6375e023e7eSJernej Skrabec
6385e023e7eSJernej Skrabec /* Reset off */
6395e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I
6405e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
6415e023e7eSJernej Skrabec #else
6425e023e7eSJernej Skrabec setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST);
6435e023e7eSJernej Skrabec #endif
6445e023e7eSJernej Skrabec
6455e023e7eSJernej Skrabec /* Clock on */
6465e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
6475e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_LVDS
6485e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I
6495e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset2_cfg, 1 << AHB_RESET_OFFSET_LVDS);
6505e023e7eSJernej Skrabec #else
6515e023e7eSJernej Skrabec setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
6525e023e7eSJernej Skrabec #endif
6535e023e7eSJernej Skrabec #endif
6545e023e7eSJernej Skrabec
6555e023e7eSJernej Skrabec lcdc_init(lcdc);
6565e023e7eSJernej Skrabec }
6575e023e7eSJernej Skrabec
sunxi_lcdc_panel_enable(void)6585e023e7eSJernej Skrabec static void sunxi_lcdc_panel_enable(void)
6595e023e7eSJernej Skrabec {
6605e023e7eSJernej Skrabec int pin, reset_pin;
6615e023e7eSJernej Skrabec
6625e023e7eSJernej Skrabec /*
6635e023e7eSJernej Skrabec * Start with backlight disabled to avoid the screen flashing to
6645e023e7eSJernej Skrabec * white while the lcd inits.
6655e023e7eSJernej Skrabec */
6665e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
6675e023e7eSJernej Skrabec if (pin >= 0) {
6685e023e7eSJernej Skrabec gpio_request(pin, "lcd_backlight_enable");
6695e023e7eSJernej Skrabec gpio_direction_output(pin, 0);
6705e023e7eSJernej Skrabec }
6715e023e7eSJernej Skrabec
6725e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
6735e023e7eSJernej Skrabec if (pin >= 0) {
6745e023e7eSJernej Skrabec gpio_request(pin, "lcd_backlight_pwm");
6755e023e7eSJernej Skrabec gpio_direction_output(pin, PWM_OFF);
6765e023e7eSJernej Skrabec }
6775e023e7eSJernej Skrabec
6785e023e7eSJernej Skrabec reset_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_RESET);
6795e023e7eSJernej Skrabec if (reset_pin >= 0) {
6805e023e7eSJernej Skrabec gpio_request(reset_pin, "lcd_reset");
6815e023e7eSJernej Skrabec gpio_direction_output(reset_pin, 0); /* Assert reset */
6825e023e7eSJernej Skrabec }
6835e023e7eSJernej Skrabec
6845e023e7eSJernej Skrabec /* Give the backlight some time to turn off and power up the panel. */
6855e023e7eSJernej Skrabec mdelay(40);
6865e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER);
6875e023e7eSJernej Skrabec if (pin >= 0) {
6885e023e7eSJernej Skrabec gpio_request(pin, "lcd_power");
6895e023e7eSJernej Skrabec gpio_direction_output(pin, 1);
6905e023e7eSJernej Skrabec }
6915e023e7eSJernej Skrabec
6925e023e7eSJernej Skrabec if (reset_pin >= 0)
6935e023e7eSJernej Skrabec gpio_direction_output(reset_pin, 1); /* De-assert reset */
6945e023e7eSJernej Skrabec }
6955e023e7eSJernej Skrabec
sunxi_lcdc_backlight_enable(void)6965e023e7eSJernej Skrabec static void sunxi_lcdc_backlight_enable(void)
6975e023e7eSJernej Skrabec {
6985e023e7eSJernej Skrabec int pin;
6995e023e7eSJernej Skrabec
7005e023e7eSJernej Skrabec /*
7015e023e7eSJernej Skrabec * We want to have scanned out at least one frame before enabling the
7025e023e7eSJernej Skrabec * backlight to avoid the screen flashing to white when we enable it.
7035e023e7eSJernej Skrabec */
7045e023e7eSJernej Skrabec mdelay(40);
7055e023e7eSJernej Skrabec
7065e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN);
7075e023e7eSJernej Skrabec if (pin >= 0)
7085e023e7eSJernej Skrabec gpio_direction_output(pin, 1);
7095e023e7eSJernej Skrabec
7105e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM);
7115e023e7eSJernej Skrabec #ifdef SUNXI_PWM_PIN0
7125e023e7eSJernej Skrabec if (pin == SUNXI_PWM_PIN0) {
7135e023e7eSJernej Skrabec writel(SUNXI_PWM_CTRL_POLARITY0(PWM_ON) |
7145e023e7eSJernej Skrabec SUNXI_PWM_CTRL_ENABLE0 |
7155e023e7eSJernej Skrabec SUNXI_PWM_CTRL_PRESCALE0(0xf), SUNXI_PWM_CTRL_REG);
7165e023e7eSJernej Skrabec writel(SUNXI_PWM_PERIOD_80PCT, SUNXI_PWM_CH0_PERIOD);
7175e023e7eSJernej Skrabec sunxi_gpio_set_cfgpin(pin, SUNXI_PWM_MUX);
7185e023e7eSJernej Skrabec return;
7195e023e7eSJernej Skrabec }
7205e023e7eSJernej Skrabec #endif
7215e023e7eSJernej Skrabec if (pin >= 0)
7225e023e7eSJernej Skrabec gpio_direction_output(pin, PWM_ON);
7235e023e7eSJernej Skrabec }
7245e023e7eSJernej Skrabec
sunxi_ctfb_mode_to_display_timing(const struct ctfb_res_modes * mode,struct display_timing * timing)72530ca2023SJernej Skrabec static void sunxi_ctfb_mode_to_display_timing(const struct ctfb_res_modes *mode,
72630ca2023SJernej Skrabec struct display_timing *timing)
72730ca2023SJernej Skrabec {
72830ca2023SJernej Skrabec timing->pixelclock.typ = mode->pixclock_khz * 1000;
72930ca2023SJernej Skrabec
73030ca2023SJernej Skrabec timing->hactive.typ = mode->xres;
73130ca2023SJernej Skrabec timing->hfront_porch.typ = mode->right_margin;
73230ca2023SJernej Skrabec timing->hback_porch.typ = mode->left_margin;
73330ca2023SJernej Skrabec timing->hsync_len.typ = mode->hsync_len;
73430ca2023SJernej Skrabec
73530ca2023SJernej Skrabec timing->vactive.typ = mode->yres;
73630ca2023SJernej Skrabec timing->vfront_porch.typ = mode->lower_margin;
73730ca2023SJernej Skrabec timing->vback_porch.typ = mode->upper_margin;
73830ca2023SJernej Skrabec timing->vsync_len.typ = mode->vsync_len;
73930ca2023SJernej Skrabec
74030ca2023SJernej Skrabec if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
74130ca2023SJernej Skrabec timing->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
74230ca2023SJernej Skrabec else
74330ca2023SJernej Skrabec timing->flags |= DISPLAY_FLAGS_HSYNC_LOW;
74430ca2023SJernej Skrabec if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
74530ca2023SJernej Skrabec timing->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
74630ca2023SJernej Skrabec else
74730ca2023SJernej Skrabec timing->flags |= DISPLAY_FLAGS_VSYNC_LOW;
74830ca2023SJernej Skrabec if (mode->vmode == FB_VMODE_INTERLACED)
74930ca2023SJernej Skrabec timing->flags |= DISPLAY_FLAGS_INTERLACED;
75030ca2023SJernej Skrabec }
75130ca2023SJernej Skrabec
sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes * mode,bool for_ext_vga_dac)7525e023e7eSJernej Skrabec static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
7535e023e7eSJernej Skrabec bool for_ext_vga_dac)
7545e023e7eSJernej Skrabec {
7555e023e7eSJernej Skrabec struct sunxi_lcdc_reg * const lcdc =
7565e023e7eSJernej Skrabec (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
7575e023e7eSJernej Skrabec int clk_div, clk_double, pin;
75830ca2023SJernej Skrabec struct display_timing timing;
7595e023e7eSJernej Skrabec
7605e023e7eSJernej Skrabec #if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS
7615e023e7eSJernej Skrabec for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) {
7625e023e7eSJernej Skrabec #else
7635e023e7eSJernej Skrabec for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) {
7645e023e7eSJernej Skrabec #endif
7655e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
7665e023e7eSJernej Skrabec sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0);
7675e023e7eSJernej Skrabec #endif
7685e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_LVDS
7695e023e7eSJernej Skrabec sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LVDS0);
7705e023e7eSJernej Skrabec #endif
7715e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804
7725e023e7eSJernej Skrabec sunxi_gpio_set_drv(pin, 3);
7735e023e7eSJernej Skrabec #endif
7745e023e7eSJernej Skrabec }
7755e023e7eSJernej Skrabec
7765e023e7eSJernej Skrabec sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
7775e023e7eSJernej Skrabec
77830ca2023SJernej Skrabec sunxi_ctfb_mode_to_display_timing(mode, &timing);
77930ca2023SJernej Skrabec lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
7805e023e7eSJernej Skrabec sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
7815e023e7eSJernej Skrabec }
7825e023e7eSJernej Skrabec
7835e023e7eSJernej Skrabec #if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
7845e023e7eSJernej Skrabec static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
7855e023e7eSJernej Skrabec int *clk_div, int *clk_double,
7865e023e7eSJernej Skrabec bool use_portd_hvsync)
7875e023e7eSJernej Skrabec {
7885e023e7eSJernej Skrabec struct sunxi_lcdc_reg * const lcdc =
7895e023e7eSJernej Skrabec (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
79030ca2023SJernej Skrabec struct display_timing timing;
7915e023e7eSJernej Skrabec
79230ca2023SJernej Skrabec sunxi_ctfb_mode_to_display_timing(mode, &timing);
79330ca2023SJernej Skrabec lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
7945e023e7eSJernej Skrabec sunxi_is_composite());
7955e023e7eSJernej Skrabec
7965e023e7eSJernej Skrabec if (use_portd_hvsync) {
7975e023e7eSJernej Skrabec sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
7985e023e7eSJernej Skrabec sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0);
7995e023e7eSJernej Skrabec }
8005e023e7eSJernej Skrabec
8015e023e7eSJernej Skrabec sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double);
8025e023e7eSJernej Skrabec }
8035e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
8045e023e7eSJernej Skrabec
8055e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI
8065e023e7eSJernej Skrabec
8075e023e7eSJernej Skrabec static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
8085e023e7eSJernej Skrabec {
8095e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi =
8105e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
8115e023e7eSJernej Skrabec u8 checksum = 0;
8125e023e7eSJernej Skrabec u8 avi_info_frame[17] = {
8135e023e7eSJernej Skrabec 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00,
8145e023e7eSJernej Skrabec 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8155e023e7eSJernej Skrabec 0x00
8165e023e7eSJernej Skrabec };
8175e023e7eSJernej Skrabec u8 vendor_info_frame[19] = {
8185e023e7eSJernej Skrabec 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40,
8195e023e7eSJernej Skrabec 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8205e023e7eSJernej Skrabec 0x00, 0x00, 0x00
8215e023e7eSJernej Skrabec };
8225e023e7eSJernej Skrabec int i;
8235e023e7eSJernej Skrabec
8245e023e7eSJernej Skrabec if (mode->pixclock_khz <= 27000)
8255e023e7eSJernej Skrabec avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */
8265e023e7eSJernej Skrabec else
8275e023e7eSJernej Skrabec avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */
8285e023e7eSJernej Skrabec
8295e023e7eSJernej Skrabec if (mode->xres * 100 / mode->yres < 156)
8305e023e7eSJernej Skrabec avi_info_frame[5] |= 0x18; /* 4 : 3 */
8315e023e7eSJernej Skrabec else
8325e023e7eSJernej Skrabec avi_info_frame[5] |= 0x28; /* 16 : 9 */
8335e023e7eSJernej Skrabec
8345e023e7eSJernej Skrabec for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
8355e023e7eSJernej Skrabec checksum += avi_info_frame[i];
8365e023e7eSJernej Skrabec
8375e023e7eSJernej Skrabec avi_info_frame[3] = 0x100 - checksum;
8385e023e7eSJernej Skrabec
8395e023e7eSJernej Skrabec for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++)
8405e023e7eSJernej Skrabec writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]);
8415e023e7eSJernej Skrabec
8425e023e7eSJernej Skrabec writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0);
8435e023e7eSJernej Skrabec writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1);
8445e023e7eSJernej Skrabec
8455e023e7eSJernej Skrabec for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++)
8465e023e7eSJernej Skrabec writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]);
8475e023e7eSJernej Skrabec
8485e023e7eSJernej Skrabec writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0);
8495e023e7eSJernej Skrabec writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1);
8505e023e7eSJernej Skrabec
8515e023e7eSJernej Skrabec setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI);
8525e023e7eSJernej Skrabec }
8535e023e7eSJernej Skrabec
8545e023e7eSJernej Skrabec static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode,
8555e023e7eSJernej Skrabec int clk_div, int clk_double)
8565e023e7eSJernej Skrabec {
8575e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi =
8585e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
8595e023e7eSJernej Skrabec int x, y;
8605e023e7eSJernej Skrabec
8615e023e7eSJernej Skrabec /* Write clear interrupt status bits */
8625e023e7eSJernej Skrabec writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq);
8635e023e7eSJernej Skrabec
8645e023e7eSJernej Skrabec if (sunxi_display.monitor == sunxi_monitor_hdmi)
8655e023e7eSJernej Skrabec sunxi_hdmi_setup_info_frames(mode);
8665e023e7eSJernej Skrabec
8675e023e7eSJernej Skrabec /* Set input sync enable */
8685e023e7eSJernej Skrabec writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown);
8695e023e7eSJernej Skrabec
8705e023e7eSJernej Skrabec /* Init various registers, select pll3 as clock source */
8715e023e7eSJernej Skrabec writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity);
8725e023e7eSJernej Skrabec writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0);
8735e023e7eSJernej Skrabec writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1);
8745e023e7eSJernej Skrabec writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl);
8755e023e7eSJernej Skrabec writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0);
8765e023e7eSJernej Skrabec
8775e023e7eSJernej Skrabec /* Setup clk div and doubler */
8785e023e7eSJernej Skrabec clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK,
8795e023e7eSJernej Skrabec SUNXI_HDMI_PLL_CTRL_DIV(clk_div));
8805e023e7eSJernej Skrabec if (!clk_double)
8815e023e7eSJernej Skrabec setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE);
8825e023e7eSJernej Skrabec
8835e023e7eSJernej Skrabec /* Setup timing registers */
8845e023e7eSJernej Skrabec writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres),
8855e023e7eSJernej Skrabec &hdmi->video_size);
8865e023e7eSJernej Skrabec
8875e023e7eSJernej Skrabec x = mode->hsync_len + mode->left_margin;
8885e023e7eSJernej Skrabec y = mode->vsync_len + mode->upper_margin;
8895e023e7eSJernej Skrabec writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp);
8905e023e7eSJernej Skrabec
8915e023e7eSJernej Skrabec x = mode->right_margin;
8925e023e7eSJernej Skrabec y = mode->lower_margin;
8935e023e7eSJernej Skrabec writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp);
8945e023e7eSJernej Skrabec
8955e023e7eSJernej Skrabec x = mode->hsync_len;
8965e023e7eSJernej Skrabec y = mode->vsync_len;
8975e023e7eSJernej Skrabec writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw);
8985e023e7eSJernej Skrabec
8995e023e7eSJernej Skrabec if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
9005e023e7eSJernej Skrabec setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR);
9015e023e7eSJernej Skrabec
9025e023e7eSJernej Skrabec if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
9035e023e7eSJernej Skrabec setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER);
9045e023e7eSJernej Skrabec }
9055e023e7eSJernej Skrabec
9065e023e7eSJernej Skrabec static void sunxi_hdmi_enable(void)
9075e023e7eSJernej Skrabec {
9085e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi =
9095e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE;
9105e023e7eSJernej Skrabec
9115e023e7eSJernej Skrabec udelay(100);
9125e023e7eSJernej Skrabec setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE);
9135e023e7eSJernej Skrabec }
9145e023e7eSJernej Skrabec
9155e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_HDMI */
9165e023e7eSJernej Skrabec
9175e023e7eSJernej Skrabec #if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
9185e023e7eSJernej Skrabec
9195e023e7eSJernej Skrabec static void sunxi_tvencoder_mode_set(void)
9205e023e7eSJernej Skrabec {
9215e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm =
9225e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
9235e023e7eSJernej Skrabec struct sunxi_tve_reg * const tve =
9245e023e7eSJernej Skrabec (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
9255e023e7eSJernej Skrabec
9265e023e7eSJernej Skrabec /* Reset off */
9275e023e7eSJernej Skrabec setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_TVE_RST);
9285e023e7eSJernej Skrabec /* Clock on */
9295e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
9305e023e7eSJernej Skrabec
9315e023e7eSJernej Skrabec switch (sunxi_display.monitor) {
9325e023e7eSJernej Skrabec case sunxi_monitor_vga:
933*c1080626SJernej Skrabec tvencoder_mode_set(tve, tve_mode_vga);
9345e023e7eSJernej Skrabec break;
9355e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc:
936*c1080626SJernej Skrabec tvencoder_mode_set(tve, tve_mode_composite_pal_nc);
937*c1080626SJernej Skrabec break;
9385e023e7eSJernej Skrabec case sunxi_monitor_composite_pal:
939*c1080626SJernej Skrabec tvencoder_mode_set(tve, tve_mode_composite_pal);
9405e023e7eSJernej Skrabec break;
9415e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m:
942*c1080626SJernej Skrabec tvencoder_mode_set(tve, tve_mode_composite_pal_m);
943*c1080626SJernej Skrabec break;
9445e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc:
945*c1080626SJernej Skrabec tvencoder_mode_set(tve, tve_mode_composite_ntsc);
9465e023e7eSJernej Skrabec break;
9475e023e7eSJernej Skrabec case sunxi_monitor_none:
9485e023e7eSJernej Skrabec case sunxi_monitor_dvi:
9495e023e7eSJernej Skrabec case sunxi_monitor_hdmi:
9505e023e7eSJernej Skrabec case sunxi_monitor_lcd:
9515e023e7eSJernej Skrabec break;
9525e023e7eSJernej Skrabec }
9535e023e7eSJernej Skrabec }
9545e023e7eSJernej Skrabec
9555e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE */
9565e023e7eSJernej Skrabec
9575e023e7eSJernej Skrabec static void sunxi_drc_init(void)
9585e023e7eSJernej Skrabec {
9595e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I
9605e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm =
9615e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
9625e023e7eSJernej Skrabec
9635e023e7eSJernej Skrabec /* On sun6i the drc must be clocked even when in pass-through mode */
9645e023e7eSJernej Skrabec #ifdef CONFIG_MACH_SUN8I_A33
9655e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_SAT);
9665e023e7eSJernej Skrabec #endif
9675e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0);
9685e023e7eSJernej Skrabec clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000);
9695e023e7eSJernej Skrabec #endif
9705e023e7eSJernej Skrabec }
9715e023e7eSJernej Skrabec
9725e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_VGA_VIA_LCD
9735e023e7eSJernej Skrabec static void sunxi_vga_external_dac_enable(void)
9745e023e7eSJernej Skrabec {
9755e023e7eSJernej Skrabec int pin;
9765e023e7eSJernej Skrabec
9775e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN);
9785e023e7eSJernej Skrabec if (pin >= 0) {
9795e023e7eSJernej Skrabec gpio_request(pin, "vga_enable");
9805e023e7eSJernej Skrabec gpio_direction_output(pin, 1);
9815e023e7eSJernej Skrabec }
9825e023e7eSJernej Skrabec }
9835e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_VGA_VIA_LCD */
9845e023e7eSJernej Skrabec
9855e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_SSD2828
9865e023e7eSJernej Skrabec static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode)
9875e023e7eSJernej Skrabec {
9885e023e7eSJernej Skrabec struct ssd2828_config cfg = {
9895e023e7eSJernej Skrabec .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS),
9905e023e7eSJernej Skrabec .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK),
9915e023e7eSJernej Skrabec .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI),
9925e023e7eSJernej Skrabec .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO),
9935e023e7eSJernej Skrabec .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET),
9945e023e7eSJernej Skrabec .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000,
9955e023e7eSJernej Skrabec .ssd2828_color_depth = 24,
9965e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828
9975e023e7eSJernej Skrabec .mipi_dsi_number_of_data_lanes = 4,
9985e023e7eSJernej Skrabec .mipi_dsi_bitrate_per_data_lane_mbps = 513,
9995e023e7eSJernej Skrabec .mipi_dsi_delay_after_exit_sleep_mode_ms = 100,
10005e023e7eSJernej Skrabec .mipi_dsi_delay_after_set_display_on_ms = 200
10015e023e7eSJernej Skrabec #else
10025e023e7eSJernej Skrabec #error MIPI LCD panel needs configuration parameters
10035e023e7eSJernej Skrabec #endif
10045e023e7eSJernej Skrabec };
10055e023e7eSJernej Skrabec
10065e023e7eSJernej Skrabec if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) {
10075e023e7eSJernej Skrabec printf("SSD2828: SPI pins are not properly configured\n");
10085e023e7eSJernej Skrabec return 1;
10095e023e7eSJernej Skrabec }
10105e023e7eSJernej Skrabec if (cfg.reset_pin == -1) {
10115e023e7eSJernej Skrabec printf("SSD2828: Reset pin is not properly configured\n");
10125e023e7eSJernej Skrabec return 1;
10135e023e7eSJernej Skrabec }
10145e023e7eSJernej Skrabec
10155e023e7eSJernej Skrabec return ssd2828_init(&cfg, mode);
10165e023e7eSJernej Skrabec }
10175e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_LCD_SSD2828 */
10185e023e7eSJernej Skrabec
10195e023e7eSJernej Skrabec static void sunxi_engines_init(void)
10205e023e7eSJernej Skrabec {
10215e023e7eSJernej Skrabec sunxi_composer_init();
10225e023e7eSJernej Skrabec sunxi_lcdc_init();
10235e023e7eSJernej Skrabec sunxi_drc_init();
10245e023e7eSJernej Skrabec }
10255e023e7eSJernej Skrabec
10265e023e7eSJernej Skrabec static void sunxi_mode_set(const struct ctfb_res_modes *mode,
10275e023e7eSJernej Skrabec unsigned int address)
10285e023e7eSJernej Skrabec {
10295e023e7eSJernej Skrabec int __maybe_unused clk_div, clk_double;
10305e023e7eSJernej Skrabec struct sunxi_lcdc_reg * const lcdc =
10315e023e7eSJernej Skrabec (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
1032*c1080626SJernej Skrabec struct sunxi_tve_reg * __maybe_unused const tve =
1033*c1080626SJernej Skrabec (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
10345e023e7eSJernej Skrabec
10355e023e7eSJernej Skrabec switch (sunxi_display.monitor) {
10365e023e7eSJernej Skrabec case sunxi_monitor_none:
10375e023e7eSJernej Skrabec break;
10385e023e7eSJernej Skrabec case sunxi_monitor_dvi:
10395e023e7eSJernej Skrabec case sunxi_monitor_hdmi:
10405e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI
10415e023e7eSJernej Skrabec sunxi_composer_mode_set(mode, address);
10425e023e7eSJernej Skrabec sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
10435e023e7eSJernej Skrabec sunxi_hdmi_mode_set(mode, clk_div, clk_double);
10445e023e7eSJernej Skrabec sunxi_composer_enable();
10455e023e7eSJernej Skrabec lcdc_enable(lcdc, sunxi_display.depth);
10465e023e7eSJernej Skrabec sunxi_hdmi_enable();
10475e023e7eSJernej Skrabec #endif
10485e023e7eSJernej Skrabec break;
10495e023e7eSJernej Skrabec case sunxi_monitor_lcd:
10505e023e7eSJernej Skrabec sunxi_lcdc_panel_enable();
10515e023e7eSJernej Skrabec if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804)) {
10525e023e7eSJernej Skrabec /*
10535e023e7eSJernej Skrabec * The anx9804 needs 1.8V from eldo3, we do this here
10545e023e7eSJernej Skrabec * and not via CONFIG_AXP_ELDO3_VOLT from board_init()
10555e023e7eSJernej Skrabec * to avoid turning this on when using hdmi output.
10565e023e7eSJernej Skrabec */
10575e023e7eSJernej Skrabec axp_set_eldo(3, 1800);
10585e023e7eSJernej Skrabec anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4,
10595e023e7eSJernej Skrabec ANX9804_DATA_RATE_1620M,
10605e023e7eSJernej Skrabec sunxi_display.depth);
10615e023e7eSJernej Skrabec }
10625e023e7eSJernej Skrabec if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) {
10635e023e7eSJernej Skrabec mdelay(50); /* Wait for lcd controller power on */
10645e023e7eSJernej Skrabec hitachi_tx18d42vm_init();
10655e023e7eSJernej Skrabec }
10665e023e7eSJernej Skrabec if (IS_ENABLED(CONFIG_VIDEO_LCD_TL059WV5C0)) {
10675e023e7eSJernej Skrabec unsigned int orig_i2c_bus = i2c_get_bus_num();
10685e023e7eSJernej Skrabec i2c_set_bus_num(CONFIG_VIDEO_LCD_I2C_BUS);
10695e023e7eSJernej Skrabec i2c_reg_write(0x5c, 0x04, 0x42); /* Turn on the LCD */
10705e023e7eSJernej Skrabec i2c_set_bus_num(orig_i2c_bus);
10715e023e7eSJernej Skrabec }
10725e023e7eSJernej Skrabec sunxi_composer_mode_set(mode, address);
10735e023e7eSJernej Skrabec sunxi_lcdc_tcon0_mode_set(mode, false);
10745e023e7eSJernej Skrabec sunxi_composer_enable();
10755e023e7eSJernej Skrabec lcdc_enable(lcdc, sunxi_display.depth);
10765e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_SSD2828
10775e023e7eSJernej Skrabec sunxi_ssd2828_init(mode);
10785e023e7eSJernej Skrabec #endif
10795e023e7eSJernej Skrabec sunxi_lcdc_backlight_enable();
10805e023e7eSJernej Skrabec break;
10815e023e7eSJernej Skrabec case sunxi_monitor_vga:
10825e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_VGA
10835e023e7eSJernej Skrabec sunxi_composer_mode_set(mode, address);
10845e023e7eSJernej Skrabec sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
10855e023e7eSJernej Skrabec sunxi_tvencoder_mode_set();
10865e023e7eSJernej Skrabec sunxi_composer_enable();
10875e023e7eSJernej Skrabec lcdc_enable(lcdc, sunxi_display.depth);
1088*c1080626SJernej Skrabec tvencoder_enable(tve);
10895e023e7eSJernej Skrabec #elif defined CONFIG_VIDEO_VGA_VIA_LCD
10905e023e7eSJernej Skrabec sunxi_composer_mode_set(mode, address);
10915e023e7eSJernej Skrabec sunxi_lcdc_tcon0_mode_set(mode, true);
10925e023e7eSJernej Skrabec sunxi_composer_enable();
10935e023e7eSJernej Skrabec lcdc_enable(lcdc, sunxi_display.depth);
10945e023e7eSJernej Skrabec sunxi_vga_external_dac_enable();
10955e023e7eSJernej Skrabec #endif
10965e023e7eSJernej Skrabec break;
10975e023e7eSJernej Skrabec case sunxi_monitor_composite_pal:
10985e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc:
10995e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m:
11005e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc:
11015e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_COMPOSITE
11025e023e7eSJernej Skrabec sunxi_composer_mode_set(mode, address);
11035e023e7eSJernej Skrabec sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
11045e023e7eSJernej Skrabec sunxi_tvencoder_mode_set();
11055e023e7eSJernej Skrabec sunxi_composer_enable();
11065e023e7eSJernej Skrabec lcdc_enable(lcdc, sunxi_display.depth);
1107*c1080626SJernej Skrabec tvencoder_enable(tve);
11085e023e7eSJernej Skrabec #endif
11095e023e7eSJernej Skrabec break;
11105e023e7eSJernej Skrabec }
11115e023e7eSJernej Skrabec }
11125e023e7eSJernej Skrabec
11135e023e7eSJernej Skrabec static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor)
11145e023e7eSJernej Skrabec {
11155e023e7eSJernej Skrabec switch (monitor) {
11165e023e7eSJernej Skrabec case sunxi_monitor_none: return "none";
11175e023e7eSJernej Skrabec case sunxi_monitor_dvi: return "dvi";
11185e023e7eSJernej Skrabec case sunxi_monitor_hdmi: return "hdmi";
11195e023e7eSJernej Skrabec case sunxi_monitor_lcd: return "lcd";
11205e023e7eSJernej Skrabec case sunxi_monitor_vga: return "vga";
11215e023e7eSJernej Skrabec case sunxi_monitor_composite_pal: return "composite-pal";
11225e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc: return "composite-ntsc";
11235e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m: return "composite-pal-m";
11245e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc: return "composite-pal-nc";
11255e023e7eSJernej Skrabec }
11265e023e7eSJernej Skrabec return NULL; /* never reached */
11275e023e7eSJernej Skrabec }
11285e023e7eSJernej Skrabec
11295e023e7eSJernej Skrabec ulong board_get_usable_ram_top(ulong total_size)
11305e023e7eSJernej Skrabec {
11315e023e7eSJernej Skrabec return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE;
11325e023e7eSJernej Skrabec }
11335e023e7eSJernej Skrabec
11345e023e7eSJernej Skrabec static bool sunxi_has_hdmi(void)
11355e023e7eSJernej Skrabec {
11365e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI
11375e023e7eSJernej Skrabec return true;
11385e023e7eSJernej Skrabec #else
11395e023e7eSJernej Skrabec return false;
11405e023e7eSJernej Skrabec #endif
11415e023e7eSJernej Skrabec }
11425e023e7eSJernej Skrabec
11435e023e7eSJernej Skrabec static bool sunxi_has_lcd(void)
11445e023e7eSJernej Skrabec {
11455e023e7eSJernej Skrabec char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
11465e023e7eSJernej Skrabec
11475e023e7eSJernej Skrabec return lcd_mode[0] != 0;
11485e023e7eSJernej Skrabec }
11495e023e7eSJernej Skrabec
11505e023e7eSJernej Skrabec static bool sunxi_has_vga(void)
11515e023e7eSJernej Skrabec {
11525e023e7eSJernej Skrabec #if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_VGA_VIA_LCD
11535e023e7eSJernej Skrabec return true;
11545e023e7eSJernej Skrabec #else
11555e023e7eSJernej Skrabec return false;
11565e023e7eSJernej Skrabec #endif
11575e023e7eSJernej Skrabec }
11585e023e7eSJernej Skrabec
11595e023e7eSJernej Skrabec static bool sunxi_has_composite(void)
11605e023e7eSJernej Skrabec {
11615e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_COMPOSITE
11625e023e7eSJernej Skrabec return true;
11635e023e7eSJernej Skrabec #else
11645e023e7eSJernej Skrabec return false;
11655e023e7eSJernej Skrabec #endif
11665e023e7eSJernej Skrabec }
11675e023e7eSJernej Skrabec
11685e023e7eSJernej Skrabec static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi)
11695e023e7eSJernej Skrabec {
11705e023e7eSJernej Skrabec if (allow_hdmi && sunxi_has_hdmi())
11715e023e7eSJernej Skrabec return sunxi_monitor_dvi;
11725e023e7eSJernej Skrabec else if (sunxi_has_lcd())
11735e023e7eSJernej Skrabec return sunxi_monitor_lcd;
11745e023e7eSJernej Skrabec else if (sunxi_has_vga())
11755e023e7eSJernej Skrabec return sunxi_monitor_vga;
11765e023e7eSJernej Skrabec else if (sunxi_has_composite())
11775e023e7eSJernej Skrabec return sunxi_monitor_composite_pal;
11785e023e7eSJernej Skrabec else
11795e023e7eSJernej Skrabec return sunxi_monitor_none;
11805e023e7eSJernej Skrabec }
11815e023e7eSJernej Skrabec
11825e023e7eSJernej Skrabec void *video_hw_init(void)
11835e023e7eSJernej Skrabec {
11845e023e7eSJernej Skrabec static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
11855e023e7eSJernej Skrabec const struct ctfb_res_modes *mode;
11865e023e7eSJernej Skrabec struct ctfb_res_modes custom;
11875e023e7eSJernej Skrabec const char *options;
11885e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI
11895e023e7eSJernej Skrabec int ret, hpd, hpd_delay, edid;
11905e023e7eSJernej Skrabec #endif
11915e023e7eSJernej Skrabec int i, overscan_offset, overscan_x, overscan_y;
11925e023e7eSJernej Skrabec unsigned int fb_dma_addr;
11935e023e7eSJernej Skrabec char mon[16];
11945e023e7eSJernej Skrabec char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
11955e023e7eSJernej Skrabec
11965e023e7eSJernej Skrabec memset(&sunxi_display, 0, sizeof(struct sunxi_display));
11975e023e7eSJernej Skrabec
11985e023e7eSJernej Skrabec video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,
11995e023e7eSJernej Skrabec &sunxi_display.depth, &options);
12005e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI
12015e023e7eSJernej Skrabec hpd = video_get_option_int(options, "hpd", 1);
12025e023e7eSJernej Skrabec hpd_delay = video_get_option_int(options, "hpd_delay", 500);
12035e023e7eSJernej Skrabec edid = video_get_option_int(options, "edid", 1);
12045e023e7eSJernej Skrabec #endif
12055e023e7eSJernej Skrabec overscan_x = video_get_option_int(options, "overscan_x", -1);
12065e023e7eSJernej Skrabec overscan_y = video_get_option_int(options, "overscan_y", -1);
12075e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_get_default_mon(true);
12085e023e7eSJernej Skrabec video_get_option_string(options, "monitor", mon, sizeof(mon),
12095e023e7eSJernej Skrabec sunxi_get_mon_desc(sunxi_display.monitor));
12105e023e7eSJernej Skrabec for (i = 0; i <= SUNXI_MONITOR_LAST; i++) {
12115e023e7eSJernej Skrabec if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) {
12125e023e7eSJernej Skrabec sunxi_display.monitor = i;
12135e023e7eSJernej Skrabec break;
12145e023e7eSJernej Skrabec }
12155e023e7eSJernej Skrabec }
12165e023e7eSJernej Skrabec if (i > SUNXI_MONITOR_LAST)
12175e023e7eSJernej Skrabec printf("Unknown monitor: '%s', falling back to '%s'\n",
12185e023e7eSJernej Skrabec mon, sunxi_get_mon_desc(sunxi_display.monitor));
12195e023e7eSJernej Skrabec
12205e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI
12215e023e7eSJernej Skrabec /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */
12225e023e7eSJernej Skrabec if (sunxi_display.monitor == sunxi_monitor_dvi ||
12235e023e7eSJernej Skrabec sunxi_display.monitor == sunxi_monitor_hdmi) {
12245e023e7eSJernej Skrabec /* Always call hdp_detect, as it also enables clocks, etc. */
12255e023e7eSJernej Skrabec ret = sunxi_hdmi_hpd_detect(hpd_delay);
12265e023e7eSJernej Skrabec if (ret) {
12275e023e7eSJernej Skrabec printf("HDMI connected: ");
12285e023e7eSJernej Skrabec if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0)
12295e023e7eSJernej Skrabec mode = &custom;
12305e023e7eSJernej Skrabec } else if (hpd) {
12315e023e7eSJernej Skrabec sunxi_hdmi_shutdown();
12325e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_get_default_mon(false);
12335e023e7eSJernej Skrabec } /* else continue with hdmi/dvi without a cable connected */
12345e023e7eSJernej Skrabec }
12355e023e7eSJernej Skrabec #endif
12365e023e7eSJernej Skrabec
12375e023e7eSJernej Skrabec switch (sunxi_display.monitor) {
12385e023e7eSJernej Skrabec case sunxi_monitor_none:
12395e023e7eSJernej Skrabec return NULL;
12405e023e7eSJernej Skrabec case sunxi_monitor_dvi:
12415e023e7eSJernej Skrabec case sunxi_monitor_hdmi:
12425e023e7eSJernej Skrabec if (!sunxi_has_hdmi()) {
12435e023e7eSJernej Skrabec printf("HDMI/DVI not supported on this board\n");
12445e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_none;
12455e023e7eSJernej Skrabec return NULL;
12465e023e7eSJernej Skrabec }
12475e023e7eSJernej Skrabec break;
12485e023e7eSJernej Skrabec case sunxi_monitor_lcd:
12495e023e7eSJernej Skrabec if (!sunxi_has_lcd()) {
12505e023e7eSJernej Skrabec printf("LCD not supported on this board\n");
12515e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_none;
12525e023e7eSJernej Skrabec return NULL;
12535e023e7eSJernej Skrabec }
12545e023e7eSJernej Skrabec sunxi_display.depth = video_get_params(&custom, lcd_mode);
12555e023e7eSJernej Skrabec mode = &custom;
12565e023e7eSJernej Skrabec break;
12575e023e7eSJernej Skrabec case sunxi_monitor_vga:
12585e023e7eSJernej Skrabec if (!sunxi_has_vga()) {
12595e023e7eSJernej Skrabec printf("VGA not supported on this board\n");
12605e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_none;
12615e023e7eSJernej Skrabec return NULL;
12625e023e7eSJernej Skrabec }
12635e023e7eSJernej Skrabec sunxi_display.depth = 18;
12645e023e7eSJernej Skrabec break;
12655e023e7eSJernej Skrabec case sunxi_monitor_composite_pal:
12665e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc:
12675e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m:
12685e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc:
12695e023e7eSJernej Skrabec if (!sunxi_has_composite()) {
12705e023e7eSJernej Skrabec printf("Composite video not supported on this board\n");
12715e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_none;
12725e023e7eSJernej Skrabec return NULL;
12735e023e7eSJernej Skrabec }
12745e023e7eSJernej Skrabec if (sunxi_display.monitor == sunxi_monitor_composite_pal ||
12755e023e7eSJernej Skrabec sunxi_display.monitor == sunxi_monitor_composite_pal_nc)
12765e023e7eSJernej Skrabec mode = &composite_video_modes[0];
12775e023e7eSJernej Skrabec else
12785e023e7eSJernej Skrabec mode = &composite_video_modes[1];
12795e023e7eSJernej Skrabec sunxi_display.depth = 24;
12805e023e7eSJernej Skrabec break;
12815e023e7eSJernej Skrabec }
12825e023e7eSJernej Skrabec
12835e023e7eSJernej Skrabec /* Yes these defaults are quite high, overscan on composite sucks... */
12845e023e7eSJernej Skrabec if (overscan_x == -1)
12855e023e7eSJernej Skrabec overscan_x = sunxi_is_composite() ? 32 : 0;
12865e023e7eSJernej Skrabec if (overscan_y == -1)
12875e023e7eSJernej Skrabec overscan_y = sunxi_is_composite() ? 20 : 0;
12885e023e7eSJernej Skrabec
12895e023e7eSJernej Skrabec sunxi_display.fb_size =
12905e023e7eSJernej Skrabec (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff;
12915e023e7eSJernej Skrabec overscan_offset = (overscan_y * mode->xres + overscan_x) * 4;
12925e023e7eSJernej Skrabec /* We want to keep the fb_base for simplefb page aligned, where as
12935e023e7eSJernej Skrabec * the sunxi dma engines will happily accept an unaligned address. */
12945e023e7eSJernej Skrabec if (overscan_offset)
12955e023e7eSJernej Skrabec sunxi_display.fb_size += 0x1000;
12965e023e7eSJernej Skrabec
12975e023e7eSJernej Skrabec if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) {
12985e023e7eSJernej Skrabec printf("Error need %dkB for fb, but only %dkB is reserved\n",
12995e023e7eSJernej Skrabec sunxi_display.fb_size >> 10,
13005e023e7eSJernej Skrabec CONFIG_SUNXI_MAX_FB_SIZE >> 10);
13015e023e7eSJernej Skrabec return NULL;
13025e023e7eSJernej Skrabec }
13035e023e7eSJernej Skrabec
13045e023e7eSJernej Skrabec printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n",
13055e023e7eSJernej Skrabec mode->xres, mode->yres,
13065e023e7eSJernej Skrabec (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "",
13075e023e7eSJernej Skrabec sunxi_get_mon_desc(sunxi_display.monitor),
13085e023e7eSJernej Skrabec overscan_x, overscan_y);
13095e023e7eSJernej Skrabec
13105e023e7eSJernej Skrabec gd->fb_base = gd->bd->bi_dram[0].start +
13115e023e7eSJernej Skrabec gd->bd->bi_dram[0].size - sunxi_display.fb_size;
13125e023e7eSJernej Skrabec sunxi_engines_init();
13135e023e7eSJernej Skrabec
13145e023e7eSJernej Skrabec fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE;
13155e023e7eSJernej Skrabec sunxi_display.fb_addr = gd->fb_base;
13165e023e7eSJernej Skrabec if (overscan_offset) {
13175e023e7eSJernej Skrabec fb_dma_addr += 0x1000 - (overscan_offset & 0xfff);
13185e023e7eSJernej Skrabec sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff;
13195e023e7eSJernej Skrabec memset((void *)gd->fb_base, 0, sunxi_display.fb_size);
13205e023e7eSJernej Skrabec flush_cache(gd->fb_base, sunxi_display.fb_size);
13215e023e7eSJernej Skrabec }
13225e023e7eSJernej Skrabec sunxi_mode_set(mode, fb_dma_addr);
13235e023e7eSJernej Skrabec
13245e023e7eSJernej Skrabec /*
13255e023e7eSJernej Skrabec * These are the only members of this structure that are used. All the
13265e023e7eSJernej Skrabec * others are driver specific. The pitch is stored in plnSizeX.
13275e023e7eSJernej Skrabec */
13285e023e7eSJernej Skrabec graphic_device->frameAdrs = sunxi_display.fb_addr;
13295e023e7eSJernej Skrabec graphic_device->gdfIndex = GDF_32BIT_X888RGB;
13305e023e7eSJernej Skrabec graphic_device->gdfBytesPP = 4;
13315e023e7eSJernej Skrabec graphic_device->winSizeX = mode->xres - 2 * overscan_x;
13325e023e7eSJernej Skrabec graphic_device->winSizeY = mode->yres - 2 * overscan_y;
13335e023e7eSJernej Skrabec graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
13345e023e7eSJernej Skrabec
13355e023e7eSJernej Skrabec return graphic_device;
13365e023e7eSJernej Skrabec }
13375e023e7eSJernej Skrabec
13385e023e7eSJernej Skrabec /*
13395e023e7eSJernej Skrabec * Simplefb support.
13405e023e7eSJernej Skrabec */
13415e023e7eSJernej Skrabec #if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB)
13425e023e7eSJernej Skrabec int sunxi_simplefb_setup(void *blob)
13435e023e7eSJernej Skrabec {
13445e023e7eSJernej Skrabec static GraphicDevice *graphic_device = &sunxi_display.graphic_device;
13455e023e7eSJernej Skrabec int offset, ret;
13465e023e7eSJernej Skrabec u64 start, size;
13475e023e7eSJernej Skrabec const char *pipeline = NULL;
13485e023e7eSJernej Skrabec
13495e023e7eSJernej Skrabec #ifdef CONFIG_MACH_SUN4I
13505e023e7eSJernej Skrabec #define PIPELINE_PREFIX "de_fe0-"
13515e023e7eSJernej Skrabec #else
13525e023e7eSJernej Skrabec #define PIPELINE_PREFIX
13535e023e7eSJernej Skrabec #endif
13545e023e7eSJernej Skrabec
13555e023e7eSJernej Skrabec switch (sunxi_display.monitor) {
13565e023e7eSJernej Skrabec case sunxi_monitor_none:
13575e023e7eSJernej Skrabec return 0;
13585e023e7eSJernej Skrabec case sunxi_monitor_dvi:
13595e023e7eSJernej Skrabec case sunxi_monitor_hdmi:
13605e023e7eSJernej Skrabec pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi";
13615e023e7eSJernej Skrabec break;
13625e023e7eSJernej Skrabec case sunxi_monitor_lcd:
13635e023e7eSJernej Skrabec pipeline = PIPELINE_PREFIX "de_be0-lcd0";
13645e023e7eSJernej Skrabec break;
13655e023e7eSJernej Skrabec case sunxi_monitor_vga:
13665e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_VGA
13675e023e7eSJernej Skrabec pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
13685e023e7eSJernej Skrabec #elif defined CONFIG_VIDEO_VGA_VIA_LCD
13695e023e7eSJernej Skrabec pipeline = PIPELINE_PREFIX "de_be0-lcd0";
13705e023e7eSJernej Skrabec #endif
13715e023e7eSJernej Skrabec break;
13725e023e7eSJernej Skrabec case sunxi_monitor_composite_pal:
13735e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc:
13745e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m:
13755e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc:
13765e023e7eSJernej Skrabec pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0";
13775e023e7eSJernej Skrabec break;
13785e023e7eSJernej Skrabec }
13795e023e7eSJernej Skrabec
13805e023e7eSJernej Skrabec /* Find a prefilled simpefb node, matching out pipeline config */
13815e023e7eSJernej Skrabec offset = fdt_node_offset_by_compatible(blob, -1,
13825e023e7eSJernej Skrabec "allwinner,simple-framebuffer");
13835e023e7eSJernej Skrabec while (offset >= 0) {
13845e023e7eSJernej Skrabec ret = fdt_stringlist_search(blob, offset, "allwinner,pipeline",
13855e023e7eSJernej Skrabec pipeline);
13865e023e7eSJernej Skrabec if (ret == 0)
13875e023e7eSJernej Skrabec break;
13885e023e7eSJernej Skrabec offset = fdt_node_offset_by_compatible(blob, offset,
13895e023e7eSJernej Skrabec "allwinner,simple-framebuffer");
13905e023e7eSJernej Skrabec }
13915e023e7eSJernej Skrabec if (offset < 0) {
13925e023e7eSJernej Skrabec eprintf("Cannot setup simplefb: node not found\n");
13935e023e7eSJernej Skrabec return 0; /* Keep older kernels working */
13945e023e7eSJernej Skrabec }
13955e023e7eSJernej Skrabec
13965e023e7eSJernej Skrabec /*
13975e023e7eSJernej Skrabec * Do not report the framebuffer as free RAM to the OS, note we cannot
13985e023e7eSJernej Skrabec * use fdt_add_mem_rsv() here, because then it is still seen as RAM,
13995e023e7eSJernej Skrabec * and e.g. Linux refuses to iomap RAM on ARM, see:
14005e023e7eSJernej Skrabec * linux/arch/arm/mm/ioremap.c around line 301.
14015e023e7eSJernej Skrabec */
14025e023e7eSJernej Skrabec start = gd->bd->bi_dram[0].start;
14035e023e7eSJernej Skrabec size = gd->bd->bi_dram[0].size - sunxi_display.fb_size;
14045e023e7eSJernej Skrabec ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
14055e023e7eSJernej Skrabec if (ret) {
14065e023e7eSJernej Skrabec eprintf("Cannot setup simplefb: Error reserving memory\n");
14075e023e7eSJernej Skrabec return ret;
14085e023e7eSJernej Skrabec }
14095e023e7eSJernej Skrabec
14105e023e7eSJernej Skrabec ret = fdt_setup_simplefb_node(blob, offset, sunxi_display.fb_addr,
14115e023e7eSJernej Skrabec graphic_device->winSizeX, graphic_device->winSizeY,
14125e023e7eSJernej Skrabec graphic_device->plnSizeX, "x8r8g8b8");
14135e023e7eSJernej Skrabec if (ret)
14145e023e7eSJernej Skrabec eprintf("Cannot setup simplefb: Error setting properties\n");
14155e023e7eSJernej Skrabec
14165e023e7eSJernej Skrabec return ret;
14175e023e7eSJernej Skrabec }
14185e023e7eSJernej Skrabec #endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */
1419