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