15e023e7eSJernej Skrabec /*
25e023e7eSJernej Skrabec * Timing controller 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 * (C) Copyright 2017 Jernej Skrabec <jernej.skrabec@siol.net>
75e023e7eSJernej Skrabec *
85e023e7eSJernej Skrabec * SPDX-License-Identifier: GPL-2.0+
95e023e7eSJernej Skrabec */
105e023e7eSJernej Skrabec
115e023e7eSJernej Skrabec #include <common.h>
125e023e7eSJernej Skrabec
135e023e7eSJernej Skrabec #include <asm/arch/lcdc.h>
145e023e7eSJernej Skrabec #include <asm/io.h>
155e023e7eSJernej Skrabec
lcdc_get_clk_delay(const struct display_timing * mode,int tcon)1630ca2023SJernej Skrabec static int lcdc_get_clk_delay(const struct display_timing *mode, int tcon)
175e023e7eSJernej Skrabec {
185e023e7eSJernej Skrabec int delay;
195e023e7eSJernej Skrabec
2030ca2023SJernej Skrabec delay = mode->vfront_porch.typ + mode->vsync_len.typ +
2130ca2023SJernej Skrabec mode->vback_porch.typ;
2230ca2023SJernej Skrabec if (mode->flags & DISPLAY_FLAGS_INTERLACED)
235e023e7eSJernej Skrabec delay /= 2;
245e023e7eSJernej Skrabec if (tcon == 1)
255e023e7eSJernej Skrabec delay -= 2;
265e023e7eSJernej Skrabec
275e023e7eSJernej Skrabec return (delay > 30) ? 30 : delay;
285e023e7eSJernej Skrabec }
295e023e7eSJernej Skrabec
lcdc_init(struct sunxi_lcdc_reg * const lcdc)305e023e7eSJernej Skrabec void lcdc_init(struct sunxi_lcdc_reg * const lcdc)
315e023e7eSJernej Skrabec {
325e023e7eSJernej Skrabec /* Init lcdc */
335e023e7eSJernej Skrabec writel(0, &lcdc->ctrl); /* Disable tcon */
345e023e7eSJernej Skrabec writel(0, &lcdc->int0); /* Disable all interrupts */
355e023e7eSJernej Skrabec
365e023e7eSJernej Skrabec /* Disable tcon0 dot clock */
375e023e7eSJernej Skrabec clrbits_le32(&lcdc->tcon0_dclk, SUNXI_LCDC_TCON0_DCLK_ENABLE);
385e023e7eSJernej Skrabec
395e023e7eSJernej Skrabec /* Set all io lines to tristate */
405e023e7eSJernej Skrabec writel(0xffffffff, &lcdc->tcon0_io_tristate);
415e023e7eSJernej Skrabec writel(0xffffffff, &lcdc->tcon1_io_tristate);
425e023e7eSJernej Skrabec }
435e023e7eSJernej Skrabec
lcdc_enable(struct sunxi_lcdc_reg * const lcdc,int depth)445e023e7eSJernej Skrabec void lcdc_enable(struct sunxi_lcdc_reg * const lcdc, int depth)
455e023e7eSJernej Skrabec {
465e023e7eSJernej Skrabec setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
475e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_LVDS
485e023e7eSJernej Skrabec setbits_le32(&lcdc->tcon0_lvds_intf, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE);
495e023e7eSJernej Skrabec setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0);
505e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I
515e023e7eSJernej Skrabec udelay(2); /* delay at least 1200 ns */
525e023e7eSJernej Skrabec setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_EN_MB);
535e023e7eSJernej Skrabec udelay(2); /* delay at least 1200 ns */
545e023e7eSJernej Skrabec setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVC);
555e023e7eSJernej Skrabec if (depth == 18)
565e023e7eSJernej Skrabec setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0x7));
575e023e7eSJernej Skrabec else
585e023e7eSJernej Skrabec setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0xf));
595e023e7eSJernej Skrabec #else
605e023e7eSJernej Skrabec setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
615e023e7eSJernej Skrabec udelay(2); /* delay at least 1200 ns */
625e023e7eSJernej Skrabec setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT1);
635e023e7eSJernej Skrabec udelay(1); /* delay at least 120 ns */
645e023e7eSJernej Skrabec setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT2);
655e023e7eSJernej Skrabec setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
665e023e7eSJernej Skrabec #endif
675e023e7eSJernej Skrabec #endif
685e023e7eSJernej Skrabec }
695e023e7eSJernej Skrabec
lcdc_tcon0_mode_set(struct sunxi_lcdc_reg * const lcdc,const struct display_timing * mode,int clk_div,bool for_ext_vga_dac,int depth,int dclk_phase)705e023e7eSJernej Skrabec void lcdc_tcon0_mode_set(struct sunxi_lcdc_reg * const lcdc,
7130ca2023SJernej Skrabec const struct display_timing *mode,
725e023e7eSJernej Skrabec int clk_div, bool for_ext_vga_dac,
735e023e7eSJernej Skrabec int depth, int dclk_phase)
745e023e7eSJernej Skrabec {
755e023e7eSJernej Skrabec int bp, clk_delay, total, val;
765e023e7eSJernej Skrabec
77*1ae5def6SJernej Skrabec #ifndef CONFIG_SUNXI_DE2
785e023e7eSJernej Skrabec /* Use tcon0 */
795e023e7eSJernej Skrabec clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
805e023e7eSJernej Skrabec SUNXI_LCDC_CTRL_IO_MAP_TCON0);
81*1ae5def6SJernej Skrabec #endif
825e023e7eSJernej Skrabec
835e023e7eSJernej Skrabec clk_delay = lcdc_get_clk_delay(mode, 0);
845e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
855e023e7eSJernej Skrabec SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
865e023e7eSJernej Skrabec
875e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
885e023e7eSJernej Skrabec SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
895e023e7eSJernej Skrabec
9030ca2023SJernej Skrabec writel(SUNXI_LCDC_X(mode->hactive.typ) |
9130ca2023SJernej Skrabec SUNXI_LCDC_Y(mode->vactive.typ), &lcdc->tcon0_timing_active);
925e023e7eSJernej Skrabec
9330ca2023SJernej Skrabec bp = mode->hsync_len.typ + mode->hback_porch.typ;
9430ca2023SJernej Skrabec total = mode->hactive.typ + mode->hfront_porch.typ + bp;
955e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
965e023e7eSJernej Skrabec SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
975e023e7eSJernej Skrabec
9830ca2023SJernej Skrabec bp = mode->vsync_len.typ + mode->vback_porch.typ;
9930ca2023SJernej Skrabec total = mode->vactive.typ + mode->vfront_porch.typ + bp;
1005e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
1015e023e7eSJernej Skrabec SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
1025e023e7eSJernej Skrabec
1035e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
10430ca2023SJernej Skrabec writel(SUNXI_LCDC_X(mode->hsync_len.typ) |
10530ca2023SJernej Skrabec SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon0_timing_sync);
1065e023e7eSJernej Skrabec
1075e023e7eSJernej Skrabec writel(0, &lcdc->tcon0_hv_intf);
1085e023e7eSJernej Skrabec writel(0, &lcdc->tcon0_cpu_intf);
1095e023e7eSJernej Skrabec #endif
1105e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_LVDS
1115e023e7eSJernej Skrabec val = (depth == 18) ? 1 : 0;
1125e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val) |
1135e023e7eSJernej Skrabec SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0, &lcdc->tcon0_lvds_intf);
1145e023e7eSJernej Skrabec #endif
1155e023e7eSJernej Skrabec
1165e023e7eSJernej Skrabec if (depth == 18 || depth == 16) {
1175e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
1185e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]);
1195e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]);
1205e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]);
1215e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]);
1225e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]);
1235e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]);
1245e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]);
1255e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]);
1265e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]);
1275e023e7eSJernej Skrabec writel(((depth == 18) ?
1285e023e7eSJernej Skrabec SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 :
1295e023e7eSJernej Skrabec SUNXI_LCDC_TCON0_FRM_CTRL_RGB565),
1305e023e7eSJernej Skrabec &lcdc->tcon0_frm_ctrl);
1315e023e7eSJernej Skrabec }
1325e023e7eSJernej Skrabec
1335e023e7eSJernej Skrabec val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(dclk_phase);
13430ca2023SJernej Skrabec if (mode->flags & DISPLAY_FLAGS_HSYNC_LOW)
1355e023e7eSJernej Skrabec val |= SUNXI_LCDC_TCON_HSYNC_MASK;
13630ca2023SJernej Skrabec if (mode->flags & DISPLAY_FLAGS_VSYNC_LOW)
1375e023e7eSJernej Skrabec val |= SUNXI_LCDC_TCON_VSYNC_MASK;
1385e023e7eSJernej Skrabec
1395e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH
1405e023e7eSJernej Skrabec if (for_ext_vga_dac)
1415e023e7eSJernej Skrabec val = 0;
1425e023e7eSJernej Skrabec #endif
1435e023e7eSJernej Skrabec writel(val, &lcdc->tcon0_io_polarity);
1445e023e7eSJernej Skrabec
1455e023e7eSJernej Skrabec writel(0, &lcdc->tcon0_io_tristate);
1465e023e7eSJernej Skrabec }
1475e023e7eSJernej Skrabec
lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc,const struct display_timing * mode,bool ext_hvsync,bool is_composite)1485e023e7eSJernej Skrabec void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc,
14930ca2023SJernej Skrabec const struct display_timing *mode,
1505e023e7eSJernej Skrabec bool ext_hvsync, bool is_composite)
1515e023e7eSJernej Skrabec {
1525e023e7eSJernej Skrabec int bp, clk_delay, total, val, yres;
1535e023e7eSJernej Skrabec
154*1ae5def6SJernej Skrabec #ifndef CONFIG_SUNXI_DE2
1555e023e7eSJernej Skrabec /* Use tcon1 */
1565e023e7eSJernej Skrabec clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
1575e023e7eSJernej Skrabec SUNXI_LCDC_CTRL_IO_MAP_TCON1);
158*1ae5def6SJernej Skrabec #endif
1595e023e7eSJernej Skrabec
1605e023e7eSJernej Skrabec clk_delay = lcdc_get_clk_delay(mode, 1);
1615e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON1_CTRL_ENABLE |
16230ca2023SJernej Skrabec ((mode->flags & DISPLAY_FLAGS_INTERLACED) ?
1635e023e7eSJernej Skrabec SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE : 0) |
1645e023e7eSJernej Skrabec SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl);
1655e023e7eSJernej Skrabec
16630ca2023SJernej Skrabec yres = mode->vactive.typ;
16730ca2023SJernej Skrabec if (mode->flags & DISPLAY_FLAGS_INTERLACED)
1685e023e7eSJernej Skrabec yres /= 2;
16930ca2023SJernej Skrabec writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres),
1705e023e7eSJernej Skrabec &lcdc->tcon1_timing_source);
17130ca2023SJernej Skrabec writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres),
1725e023e7eSJernej Skrabec &lcdc->tcon1_timing_scale);
17330ca2023SJernej Skrabec writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres),
1745e023e7eSJernej Skrabec &lcdc->tcon1_timing_out);
1755e023e7eSJernej Skrabec
17630ca2023SJernej Skrabec bp = mode->hsync_len.typ + mode->hback_porch.typ;
17730ca2023SJernej Skrabec total = mode->hactive.typ + mode->hfront_porch.typ + bp;
1785e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) |
1795e023e7eSJernej Skrabec SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h);
1805e023e7eSJernej Skrabec
18130ca2023SJernej Skrabec bp = mode->vsync_len.typ + mode->vback_porch.typ;
18230ca2023SJernej Skrabec total = mode->vactive.typ + mode->vfront_porch.typ + bp;
18330ca2023SJernej Skrabec if (!(mode->flags & DISPLAY_FLAGS_INTERLACED))
1845e023e7eSJernej Skrabec total *= 2;
1855e023e7eSJernej Skrabec writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) |
1865e023e7eSJernej Skrabec SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v);
1875e023e7eSJernej Skrabec
18830ca2023SJernej Skrabec writel(SUNXI_LCDC_X(mode->hsync_len.typ) |
18930ca2023SJernej Skrabec SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon1_timing_sync);
1905e023e7eSJernej Skrabec
1915e023e7eSJernej Skrabec if (ext_hvsync) {
1925e023e7eSJernej Skrabec val = 0;
19330ca2023SJernej Skrabec if (mode->flags & DISPLAY_FLAGS_HSYNC_HIGH)
1945e023e7eSJernej Skrabec val |= SUNXI_LCDC_TCON_HSYNC_MASK;
19530ca2023SJernej Skrabec if (mode->flags & DISPLAY_FLAGS_VSYNC_HIGH)
1965e023e7eSJernej Skrabec val |= SUNXI_LCDC_TCON_VSYNC_MASK;
1975e023e7eSJernej Skrabec writel(val, &lcdc->tcon1_io_polarity);
1985e023e7eSJernej Skrabec
1995e023e7eSJernej Skrabec clrbits_le32(&lcdc->tcon1_io_tristate,
2005e023e7eSJernej Skrabec SUNXI_LCDC_TCON_VSYNC_MASK |
2015e023e7eSJernej Skrabec SUNXI_LCDC_TCON_HSYNC_MASK);
2025e023e7eSJernej Skrabec }
2035e023e7eSJernej Skrabec
2045e023e7eSJernej Skrabec #ifdef CONFIG_MACH_SUN5I
2055e023e7eSJernej Skrabec if (is_composite)
2065e023e7eSJernej Skrabec clrsetbits_le32(&lcdc->mux_ctrl, SUNXI_LCDC_MUX_CTRL_SRC0_MASK,
2075e023e7eSJernej Skrabec SUNXI_LCDC_MUX_CTRL_SRC0(1));
2085e023e7eSJernej Skrabec #endif
2095e023e7eSJernej Skrabec }
210