1 /* 2 * Timing controller driver for Allwinner SoCs. 3 * 4 * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be> 5 * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com> 6 * (C) Copyright 2017 Jernej Skrabec <jernej.skrabec@siol.net> 7 * 8 * SPDX-License-Identifier: GPL-2.0+ 9 */ 10 11 #include <common.h> 12 13 #include <asm/arch/lcdc.h> 14 #include <asm/io.h> 15 16 static int lcdc_get_clk_delay(const struct display_timing *mode, int tcon) 17 { 18 int delay; 19 20 delay = mode->vfront_porch.typ + mode->vsync_len.typ + 21 mode->vback_porch.typ; 22 if (mode->flags & DISPLAY_FLAGS_INTERLACED) 23 delay /= 2; 24 if (tcon == 1) 25 delay -= 2; 26 27 return (delay > 30) ? 30 : delay; 28 } 29 30 void lcdc_init(struct sunxi_lcdc_reg * const lcdc) 31 { 32 /* Init lcdc */ 33 writel(0, &lcdc->ctrl); /* Disable tcon */ 34 writel(0, &lcdc->int0); /* Disable all interrupts */ 35 36 /* Disable tcon0 dot clock */ 37 clrbits_le32(&lcdc->tcon0_dclk, SUNXI_LCDC_TCON0_DCLK_ENABLE); 38 39 /* Set all io lines to tristate */ 40 writel(0xffffffff, &lcdc->tcon0_io_tristate); 41 writel(0xffffffff, &lcdc->tcon1_io_tristate); 42 } 43 44 void lcdc_enable(struct sunxi_lcdc_reg * const lcdc, int depth) 45 { 46 setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE); 47 #ifdef CONFIG_VIDEO_LCD_IF_LVDS 48 setbits_le32(&lcdc->tcon0_lvds_intf, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE); 49 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0); 50 #ifdef CONFIG_SUNXI_GEN_SUN6I 51 udelay(2); /* delay at least 1200 ns */ 52 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_EN_MB); 53 udelay(2); /* delay at least 1200 ns */ 54 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVC); 55 if (depth == 18) 56 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0x7)); 57 else 58 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0xf)); 59 #else 60 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE); 61 udelay(2); /* delay at least 1200 ns */ 62 setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT1); 63 udelay(1); /* delay at least 120 ns */ 64 setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT2); 65 setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE); 66 #endif 67 #endif 68 } 69 70 void lcdc_tcon0_mode_set(struct sunxi_lcdc_reg * const lcdc, 71 const struct display_timing *mode, 72 int clk_div, bool for_ext_vga_dac, 73 int depth, int dclk_phase) 74 { 75 int bp, clk_delay, total, val; 76 77 /* Use tcon0 */ 78 clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK, 79 SUNXI_LCDC_CTRL_IO_MAP_TCON0); 80 81 clk_delay = lcdc_get_clk_delay(mode, 0); 82 writel(SUNXI_LCDC_TCON0_CTRL_ENABLE | 83 SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl); 84 85 writel(SUNXI_LCDC_TCON0_DCLK_ENABLE | 86 SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk); 87 88 writel(SUNXI_LCDC_X(mode->hactive.typ) | 89 SUNXI_LCDC_Y(mode->vactive.typ), &lcdc->tcon0_timing_active); 90 91 bp = mode->hsync_len.typ + mode->hback_porch.typ; 92 total = mode->hactive.typ + mode->hfront_porch.typ + bp; 93 writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) | 94 SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h); 95 96 bp = mode->vsync_len.typ + mode->vback_porch.typ; 97 total = mode->vactive.typ + mode->vfront_porch.typ + bp; 98 writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) | 99 SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v); 100 101 #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL 102 writel(SUNXI_LCDC_X(mode->hsync_len.typ) | 103 SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon0_timing_sync); 104 105 writel(0, &lcdc->tcon0_hv_intf); 106 writel(0, &lcdc->tcon0_cpu_intf); 107 #endif 108 #ifdef CONFIG_VIDEO_LCD_IF_LVDS 109 val = (depth == 18) ? 1 : 0; 110 writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val) | 111 SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0, &lcdc->tcon0_lvds_intf); 112 #endif 113 114 if (depth == 18 || depth == 16) { 115 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]); 116 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]); 117 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]); 118 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]); 119 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]); 120 writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]); 121 writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]); 122 writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]); 123 writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]); 124 writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]); 125 writel(((depth == 18) ? 126 SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 : 127 SUNXI_LCDC_TCON0_FRM_CTRL_RGB565), 128 &lcdc->tcon0_frm_ctrl); 129 } 130 131 val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(dclk_phase); 132 if (mode->flags & DISPLAY_FLAGS_HSYNC_LOW) 133 val |= SUNXI_LCDC_TCON_HSYNC_MASK; 134 if (mode->flags & DISPLAY_FLAGS_VSYNC_LOW) 135 val |= SUNXI_LCDC_TCON_VSYNC_MASK; 136 137 #ifdef CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH 138 if (for_ext_vga_dac) 139 val = 0; 140 #endif 141 writel(val, &lcdc->tcon0_io_polarity); 142 143 writel(0, &lcdc->tcon0_io_tristate); 144 } 145 146 void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc, 147 const struct display_timing *mode, 148 bool ext_hvsync, bool is_composite) 149 { 150 int bp, clk_delay, total, val, yres; 151 152 /* Use tcon1 */ 153 clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK, 154 SUNXI_LCDC_CTRL_IO_MAP_TCON1); 155 156 clk_delay = lcdc_get_clk_delay(mode, 1); 157 writel(SUNXI_LCDC_TCON1_CTRL_ENABLE | 158 ((mode->flags & DISPLAY_FLAGS_INTERLACED) ? 159 SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE : 0) | 160 SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl); 161 162 yres = mode->vactive.typ; 163 if (mode->flags & DISPLAY_FLAGS_INTERLACED) 164 yres /= 2; 165 writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres), 166 &lcdc->tcon1_timing_source); 167 writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres), 168 &lcdc->tcon1_timing_scale); 169 writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres), 170 &lcdc->tcon1_timing_out); 171 172 bp = mode->hsync_len.typ + mode->hback_porch.typ; 173 total = mode->hactive.typ + mode->hfront_porch.typ + bp; 174 writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) | 175 SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h); 176 177 bp = mode->vsync_len.typ + mode->vback_porch.typ; 178 total = mode->vactive.typ + mode->vfront_porch.typ + bp; 179 if (!(mode->flags & DISPLAY_FLAGS_INTERLACED)) 180 total *= 2; 181 writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) | 182 SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v); 183 184 writel(SUNXI_LCDC_X(mode->hsync_len.typ) | 185 SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon1_timing_sync); 186 187 if (ext_hvsync) { 188 val = 0; 189 if (mode->flags & DISPLAY_FLAGS_HSYNC_HIGH) 190 val |= SUNXI_LCDC_TCON_HSYNC_MASK; 191 if (mode->flags & DISPLAY_FLAGS_VSYNC_HIGH) 192 val |= SUNXI_LCDC_TCON_VSYNC_MASK; 193 writel(val, &lcdc->tcon1_io_polarity); 194 195 clrbits_le32(&lcdc->tcon1_io_tristate, 196 SUNXI_LCDC_TCON_VSYNC_MASK | 197 SUNXI_LCDC_TCON_HSYNC_MASK); 198 } 199 200 #ifdef CONFIG_MACH_SUN5I 201 if (is_composite) 202 clrsetbits_le32(&lcdc->mux_ctrl, SUNXI_LCDC_MUX_CTRL_SRC0_MASK, 203 SUNXI_LCDC_MUX_CTRL_SRC0(1)); 204 #endif 205 } 206