1*5e023e7eSJernej Skrabec /* 2*5e023e7eSJernej Skrabec * Display 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 * 7*5e023e7eSJernej Skrabec * SPDX-License-Identifier: GPL-2.0+ 8*5e023e7eSJernej Skrabec */ 9*5e023e7eSJernej Skrabec 10*5e023e7eSJernej Skrabec #include <common.h> 11*5e023e7eSJernej Skrabec 12*5e023e7eSJernej Skrabec #include <asm/arch/clock.h> 13*5e023e7eSJernej Skrabec #include <asm/arch/display.h> 14*5e023e7eSJernej Skrabec #include <asm/arch/gpio.h> 15*5e023e7eSJernej Skrabec #include <asm/arch/lcdc.h> 16*5e023e7eSJernej Skrabec #include <asm/arch/pwm.h> 17*5e023e7eSJernej Skrabec #include <asm/global_data.h> 18*5e023e7eSJernej Skrabec #include <asm/gpio.h> 19*5e023e7eSJernej Skrabec #include <asm/io.h> 20*5e023e7eSJernej Skrabec #include <axp_pmic.h> 21*5e023e7eSJernej Skrabec #include <errno.h> 22*5e023e7eSJernej Skrabec #include <fdtdec.h> 23*5e023e7eSJernej Skrabec #include <fdt_support.h> 24*5e023e7eSJernej Skrabec #include <i2c.h> 25*5e023e7eSJernej Skrabec #include <malloc.h> 26*5e023e7eSJernej Skrabec #include <video_fb.h> 27*5e023e7eSJernej Skrabec #include "../videomodes.h" 28*5e023e7eSJernej Skrabec #include "../anx9804.h" 29*5e023e7eSJernej Skrabec #include "../hitachi_tx18d42vm_lcd.h" 30*5e023e7eSJernej Skrabec #include "../ssd2828.h" 31*5e023e7eSJernej Skrabec 32*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW 33*5e023e7eSJernej Skrabec #define PWM_ON 0 34*5e023e7eSJernej Skrabec #define PWM_OFF 1 35*5e023e7eSJernej Skrabec #else 36*5e023e7eSJernej Skrabec #define PWM_ON 1 37*5e023e7eSJernej Skrabec #define PWM_OFF 0 38*5e023e7eSJernej Skrabec #endif 39*5e023e7eSJernej Skrabec 40*5e023e7eSJernej Skrabec DECLARE_GLOBAL_DATA_PTR; 41*5e023e7eSJernej Skrabec 42*5e023e7eSJernej Skrabec enum sunxi_monitor { 43*5e023e7eSJernej Skrabec sunxi_monitor_none, 44*5e023e7eSJernej Skrabec sunxi_monitor_dvi, 45*5e023e7eSJernej Skrabec sunxi_monitor_hdmi, 46*5e023e7eSJernej Skrabec sunxi_monitor_lcd, 47*5e023e7eSJernej Skrabec sunxi_monitor_vga, 48*5e023e7eSJernej Skrabec sunxi_monitor_composite_pal, 49*5e023e7eSJernej Skrabec sunxi_monitor_composite_ntsc, 50*5e023e7eSJernej Skrabec sunxi_monitor_composite_pal_m, 51*5e023e7eSJernej Skrabec sunxi_monitor_composite_pal_nc, 52*5e023e7eSJernej Skrabec }; 53*5e023e7eSJernej Skrabec #define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc 54*5e023e7eSJernej Skrabec 55*5e023e7eSJernej Skrabec struct sunxi_display { 56*5e023e7eSJernej Skrabec GraphicDevice graphic_device; 57*5e023e7eSJernej Skrabec enum sunxi_monitor monitor; 58*5e023e7eSJernej Skrabec unsigned int depth; 59*5e023e7eSJernej Skrabec unsigned int fb_addr; 60*5e023e7eSJernej Skrabec unsigned int fb_size; 61*5e023e7eSJernej Skrabec } sunxi_display; 62*5e023e7eSJernej Skrabec 63*5e023e7eSJernej Skrabec const struct ctfb_res_modes composite_video_modes[2] = { 64*5e023e7eSJernej Skrabec /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */ 65*5e023e7eSJernej Skrabec { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED }, 66*5e023e7eSJernej Skrabec { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED }, 67*5e023e7eSJernej Skrabec }; 68*5e023e7eSJernej Skrabec 69*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI 70*5e023e7eSJernej Skrabec 71*5e023e7eSJernej Skrabec /* 72*5e023e7eSJernej Skrabec * Wait up to 200ms for value to be set in given part of reg. 73*5e023e7eSJernej Skrabec */ 74*5e023e7eSJernej Skrabec static int await_completion(u32 *reg, u32 mask, u32 val) 75*5e023e7eSJernej Skrabec { 76*5e023e7eSJernej Skrabec unsigned long tmo = timer_get_us() + 200000; 77*5e023e7eSJernej Skrabec 78*5e023e7eSJernej Skrabec while ((readl(reg) & mask) != val) { 79*5e023e7eSJernej Skrabec if (timer_get_us() > tmo) { 80*5e023e7eSJernej Skrabec printf("DDC: timeout reading EDID\n"); 81*5e023e7eSJernej Skrabec return -ETIME; 82*5e023e7eSJernej Skrabec } 83*5e023e7eSJernej Skrabec } 84*5e023e7eSJernej Skrabec return 0; 85*5e023e7eSJernej Skrabec } 86*5e023e7eSJernej Skrabec 87*5e023e7eSJernej Skrabec static int sunxi_hdmi_hpd_detect(int hpd_delay) 88*5e023e7eSJernej Skrabec { 89*5e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm = 90*5e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 91*5e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi = 92*5e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; 93*5e023e7eSJernej Skrabec unsigned long tmo = timer_get_us() + hpd_delay * 1000; 94*5e023e7eSJernej Skrabec 95*5e023e7eSJernej Skrabec /* Set pll3 to 300MHz */ 96*5e023e7eSJernej Skrabec clock_set_pll3(300000000); 97*5e023e7eSJernej Skrabec 98*5e023e7eSJernej Skrabec /* Set hdmi parent to pll3 */ 99*5e023e7eSJernej Skrabec clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK, 100*5e023e7eSJernej Skrabec CCM_HDMI_CTRL_PLL3); 101*5e023e7eSJernej Skrabec 102*5e023e7eSJernej Skrabec /* Set ahb gating to pass */ 103*5e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I 104*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI); 105*5e023e7eSJernej Skrabec #endif 106*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI); 107*5e023e7eSJernej Skrabec 108*5e023e7eSJernej Skrabec /* Clock on */ 109*5e023e7eSJernej Skrabec setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE); 110*5e023e7eSJernej Skrabec 111*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl); 112*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0); 113*5e023e7eSJernej Skrabec 114*5e023e7eSJernej Skrabec while (timer_get_us() < tmo) { 115*5e023e7eSJernej Skrabec if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT) 116*5e023e7eSJernej Skrabec return 1; 117*5e023e7eSJernej Skrabec } 118*5e023e7eSJernej Skrabec 119*5e023e7eSJernej Skrabec return 0; 120*5e023e7eSJernej Skrabec } 121*5e023e7eSJernej Skrabec 122*5e023e7eSJernej Skrabec static void sunxi_hdmi_shutdown(void) 123*5e023e7eSJernej Skrabec { 124*5e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm = 125*5e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 126*5e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi = 127*5e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; 128*5e023e7eSJernej Skrabec 129*5e023e7eSJernej Skrabec clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE); 130*5e023e7eSJernej Skrabec clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE); 131*5e023e7eSJernej Skrabec clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI); 132*5e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I 133*5e023e7eSJernej Skrabec clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI); 134*5e023e7eSJernej Skrabec #endif 135*5e023e7eSJernej Skrabec clock_set_pll3(0); 136*5e023e7eSJernej Skrabec } 137*5e023e7eSJernej Skrabec 138*5e023e7eSJernej Skrabec static int sunxi_hdmi_ddc_do_command(u32 cmnd, int offset, int n) 139*5e023e7eSJernej Skrabec { 140*5e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi = 141*5e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; 142*5e023e7eSJernej Skrabec 143*5e023e7eSJernej Skrabec setbits_le32(&hdmi->ddc_fifo_ctrl, SUNXI_HDMI_DDC_FIFO_CTRL_CLEAR); 144*5e023e7eSJernej Skrabec writel(SUNXI_HMDI_DDC_ADDR_EDDC_SEGMENT(offset >> 8) | 145*5e023e7eSJernej Skrabec SUNXI_HMDI_DDC_ADDR_EDDC_ADDR | 146*5e023e7eSJernej Skrabec SUNXI_HMDI_DDC_ADDR_OFFSET(offset) | 147*5e023e7eSJernej Skrabec SUNXI_HMDI_DDC_ADDR_SLAVE_ADDR, &hdmi->ddc_addr); 148*5e023e7eSJernej Skrabec #ifndef CONFIG_MACH_SUN6I 149*5e023e7eSJernej Skrabec writel(n, &hdmi->ddc_byte_count); 150*5e023e7eSJernej Skrabec writel(cmnd, &hdmi->ddc_cmnd); 151*5e023e7eSJernej Skrabec #else 152*5e023e7eSJernej Skrabec writel(n << 16 | cmnd, &hdmi->ddc_cmnd); 153*5e023e7eSJernej Skrabec #endif 154*5e023e7eSJernej Skrabec setbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START); 155*5e023e7eSJernej Skrabec 156*5e023e7eSJernej Skrabec return await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_START, 0); 157*5e023e7eSJernej Skrabec } 158*5e023e7eSJernej Skrabec 159*5e023e7eSJernej Skrabec static int sunxi_hdmi_ddc_read(int offset, u8 *buf, int count) 160*5e023e7eSJernej Skrabec { 161*5e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi = 162*5e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; 163*5e023e7eSJernej Skrabec int i, n; 164*5e023e7eSJernej Skrabec 165*5e023e7eSJernej Skrabec while (count > 0) { 166*5e023e7eSJernej Skrabec if (count > 16) 167*5e023e7eSJernej Skrabec n = 16; 168*5e023e7eSJernej Skrabec else 169*5e023e7eSJernej Skrabec n = count; 170*5e023e7eSJernej Skrabec 171*5e023e7eSJernej Skrabec if (sunxi_hdmi_ddc_do_command( 172*5e023e7eSJernej Skrabec SUNXI_HDMI_DDC_CMND_EXPLICIT_EDDC_READ, 173*5e023e7eSJernej Skrabec offset, n)) 174*5e023e7eSJernej Skrabec return -ETIME; 175*5e023e7eSJernej Skrabec 176*5e023e7eSJernej Skrabec for (i = 0; i < n; i++) 177*5e023e7eSJernej Skrabec *buf++ = readb(&hdmi->ddc_fifo_data); 178*5e023e7eSJernej Skrabec 179*5e023e7eSJernej Skrabec offset += n; 180*5e023e7eSJernej Skrabec count -= n; 181*5e023e7eSJernej Skrabec } 182*5e023e7eSJernej Skrabec 183*5e023e7eSJernej Skrabec return 0; 184*5e023e7eSJernej Skrabec } 185*5e023e7eSJernej Skrabec 186*5e023e7eSJernej Skrabec static int sunxi_hdmi_edid_get_block(int block, u8 *buf) 187*5e023e7eSJernej Skrabec { 188*5e023e7eSJernej Skrabec int r, retries = 2; 189*5e023e7eSJernej Skrabec 190*5e023e7eSJernej Skrabec do { 191*5e023e7eSJernej Skrabec r = sunxi_hdmi_ddc_read(block * 128, buf, 128); 192*5e023e7eSJernej Skrabec if (r) 193*5e023e7eSJernej Skrabec continue; 194*5e023e7eSJernej Skrabec r = edid_check_checksum(buf); 195*5e023e7eSJernej Skrabec if (r) { 196*5e023e7eSJernej Skrabec printf("EDID block %d: checksum error%s\n", 197*5e023e7eSJernej Skrabec block, retries ? ", retrying" : ""); 198*5e023e7eSJernej Skrabec } 199*5e023e7eSJernej Skrabec } while (r && retries--); 200*5e023e7eSJernej Skrabec 201*5e023e7eSJernej Skrabec return r; 202*5e023e7eSJernej Skrabec } 203*5e023e7eSJernej Skrabec 204*5e023e7eSJernej Skrabec static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode) 205*5e023e7eSJernej Skrabec { 206*5e023e7eSJernej Skrabec struct edid1_info edid1; 207*5e023e7eSJernej Skrabec struct edid_cea861_info cea681[4]; 208*5e023e7eSJernej Skrabec struct edid_detailed_timing *t = 209*5e023e7eSJernej Skrabec (struct edid_detailed_timing *)edid1.monitor_details.timing; 210*5e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi = 211*5e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; 212*5e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm = 213*5e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 214*5e023e7eSJernej Skrabec int i, r, ext_blocks = 0; 215*5e023e7eSJernej Skrabec 216*5e023e7eSJernej Skrabec /* SUNXI_HDMI_CTRL_ENABLE & PAD_CTRL0 are already set by hpd_detect */ 217*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_PAD_CTRL1 | SUNXI_HDMI_PAD_CTRL1_HALVE, 218*5e023e7eSJernej Skrabec &hdmi->pad_ctrl1); 219*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_PLL_CTRL | SUNXI_HDMI_PLL_CTRL_DIV(15), 220*5e023e7eSJernej Skrabec &hdmi->pll_ctrl); 221*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0); 222*5e023e7eSJernej Skrabec 223*5e023e7eSJernej Skrabec /* Reset i2c controller */ 224*5e023e7eSJernej Skrabec setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE); 225*5e023e7eSJernej Skrabec writel(SUNXI_HMDI_DDC_CTRL_ENABLE | 226*5e023e7eSJernej Skrabec SUNXI_HMDI_DDC_CTRL_SDA_ENABLE | 227*5e023e7eSJernej Skrabec SUNXI_HMDI_DDC_CTRL_SCL_ENABLE | 228*5e023e7eSJernej Skrabec SUNXI_HMDI_DDC_CTRL_RESET, &hdmi->ddc_ctrl); 229*5e023e7eSJernej Skrabec if (await_completion(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_RESET, 0)) 230*5e023e7eSJernej Skrabec return -EIO; 231*5e023e7eSJernej Skrabec 232*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_DDC_CLOCK, &hdmi->ddc_clock); 233*5e023e7eSJernej Skrabec #ifndef CONFIG_MACH_SUN6I 234*5e023e7eSJernej Skrabec writel(SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE | 235*5e023e7eSJernej Skrabec SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE, &hdmi->ddc_line_ctrl); 236*5e023e7eSJernej Skrabec #endif 237*5e023e7eSJernej Skrabec 238*5e023e7eSJernej Skrabec r = sunxi_hdmi_edid_get_block(0, (u8 *)&edid1); 239*5e023e7eSJernej Skrabec if (r == 0) { 240*5e023e7eSJernej Skrabec r = edid_check_info(&edid1); 241*5e023e7eSJernej Skrabec if (r) { 242*5e023e7eSJernej Skrabec printf("EDID: invalid EDID data\n"); 243*5e023e7eSJernej Skrabec r = -EINVAL; 244*5e023e7eSJernej Skrabec } 245*5e023e7eSJernej Skrabec } 246*5e023e7eSJernej Skrabec if (r == 0) { 247*5e023e7eSJernej Skrabec ext_blocks = edid1.extension_flag; 248*5e023e7eSJernej Skrabec if (ext_blocks > 4) 249*5e023e7eSJernej Skrabec ext_blocks = 4; 250*5e023e7eSJernej Skrabec for (i = 0; i < ext_blocks; i++) { 251*5e023e7eSJernej Skrabec if (sunxi_hdmi_edid_get_block(1 + i, 252*5e023e7eSJernej Skrabec (u8 *)&cea681[i]) != 0) { 253*5e023e7eSJernej Skrabec ext_blocks = i; 254*5e023e7eSJernej Skrabec break; 255*5e023e7eSJernej Skrabec } 256*5e023e7eSJernej Skrabec } 257*5e023e7eSJernej Skrabec } 258*5e023e7eSJernej Skrabec 259*5e023e7eSJernej Skrabec /* Disable DDC engine, no longer needed */ 260*5e023e7eSJernej Skrabec clrbits_le32(&hdmi->ddc_ctrl, SUNXI_HMDI_DDC_CTRL_ENABLE); 261*5e023e7eSJernej Skrabec clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_DDC_GATE); 262*5e023e7eSJernej Skrabec 263*5e023e7eSJernej Skrabec if (r) 264*5e023e7eSJernej Skrabec return r; 265*5e023e7eSJernej Skrabec 266*5e023e7eSJernej Skrabec /* We want version 1.3 or 1.2 with detailed timing info */ 267*5e023e7eSJernej Skrabec if (edid1.version != 1 || (edid1.revision < 3 && 268*5e023e7eSJernej Skrabec !EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(edid1))) { 269*5e023e7eSJernej Skrabec printf("EDID: unsupported version %d.%d\n", 270*5e023e7eSJernej Skrabec edid1.version, edid1.revision); 271*5e023e7eSJernej Skrabec return -EINVAL; 272*5e023e7eSJernej Skrabec } 273*5e023e7eSJernej Skrabec 274*5e023e7eSJernej Skrabec /* Take the first usable detailed timing */ 275*5e023e7eSJernej Skrabec for (i = 0; i < 4; i++, t++) { 276*5e023e7eSJernej Skrabec r = video_edid_dtd_to_ctfb_res_modes(t, mode); 277*5e023e7eSJernej Skrabec if (r == 0) 278*5e023e7eSJernej Skrabec break; 279*5e023e7eSJernej Skrabec } 280*5e023e7eSJernej Skrabec if (i == 4) { 281*5e023e7eSJernej Skrabec printf("EDID: no usable detailed timing found\n"); 282*5e023e7eSJernej Skrabec return -ENOENT; 283*5e023e7eSJernej Skrabec } 284*5e023e7eSJernej Skrabec 285*5e023e7eSJernej Skrabec /* Check for basic audio support, if found enable hdmi output */ 286*5e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_dvi; 287*5e023e7eSJernej Skrabec for (i = 0; i < ext_blocks; i++) { 288*5e023e7eSJernej Skrabec if (cea681[i].extension_tag != EDID_CEA861_EXTENSION_TAG || 289*5e023e7eSJernej Skrabec cea681[i].revision < 2) 290*5e023e7eSJernej Skrabec continue; 291*5e023e7eSJernej Skrabec 292*5e023e7eSJernej Skrabec if (EDID_CEA861_SUPPORTS_BASIC_AUDIO(cea681[i])) 293*5e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_hdmi; 294*5e023e7eSJernej Skrabec } 295*5e023e7eSJernej Skrabec 296*5e023e7eSJernej Skrabec return 0; 297*5e023e7eSJernej Skrabec } 298*5e023e7eSJernej Skrabec 299*5e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_HDMI */ 300*5e023e7eSJernej Skrabec 301*5e023e7eSJernej Skrabec #ifdef CONFIG_MACH_SUN4I 302*5e023e7eSJernej Skrabec /* 303*5e023e7eSJernej Skrabec * Testing has shown that on sun4i the display backend engine does not have 304*5e023e7eSJernej Skrabec * deep enough fifo-s causing flickering / tearing in full-hd mode due to 305*5e023e7eSJernej Skrabec * fifo underruns. So on sun4i we use the display frontend engine to do the 306*5e023e7eSJernej Skrabec * dma from memory, as the frontend does have deep enough fifo-s. 307*5e023e7eSJernej Skrabec */ 308*5e023e7eSJernej Skrabec 309*5e023e7eSJernej Skrabec static const u32 sun4i_vert_coef[32] = { 310*5e023e7eSJernej Skrabec 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, 311*5e023e7eSJernej Skrabec 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb, 312*5e023e7eSJernej Skrabec 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb, 313*5e023e7eSJernej Skrabec 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc, 314*5e023e7eSJernej Skrabec 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd, 315*5e023e7eSJernej Skrabec 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff, 316*5e023e7eSJernej Skrabec 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff, 317*5e023e7eSJernej Skrabec 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, 318*5e023e7eSJernej Skrabec }; 319*5e023e7eSJernej Skrabec 320*5e023e7eSJernej Skrabec static const u32 sun4i_horz_coef[64] = { 321*5e023e7eSJernej Skrabec 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03, 322*5e023e7eSJernej Skrabec 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06, 323*5e023e7eSJernej Skrabec 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09, 324*5e023e7eSJernej Skrabec 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f, 325*5e023e7eSJernej Skrabec 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12, 326*5e023e7eSJernej Skrabec 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18, 327*5e023e7eSJernej Skrabec 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e, 328*5e023e7eSJernej Skrabec 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23, 329*5e023e7eSJernej Skrabec 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29, 330*5e023e7eSJernej Skrabec 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e, 331*5e023e7eSJernej Skrabec 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33, 332*5e023e7eSJernej Skrabec 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37, 333*5e023e7eSJernej Skrabec 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b, 334*5e023e7eSJernej Skrabec 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e, 335*5e023e7eSJernej Skrabec 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40, 336*5e023e7eSJernej Skrabec 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42, 337*5e023e7eSJernej Skrabec }; 338*5e023e7eSJernej Skrabec 339*5e023e7eSJernej Skrabec static void sunxi_frontend_init(void) 340*5e023e7eSJernej Skrabec { 341*5e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm = 342*5e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 343*5e023e7eSJernej Skrabec struct sunxi_de_fe_reg * const de_fe = 344*5e023e7eSJernej Skrabec (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE; 345*5e023e7eSJernej Skrabec int i; 346*5e023e7eSJernej Skrabec 347*5e023e7eSJernej Skrabec /* Clocks on */ 348*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_FE0); 349*5e023e7eSJernej Skrabec setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_FE0); 350*5e023e7eSJernej Skrabec clock_set_de_mod_clock(&ccm->fe0_clk_cfg, 300000000); 351*5e023e7eSJernej Skrabec 352*5e023e7eSJernej Skrabec setbits_le32(&de_fe->enable, SUNXI_DE_FE_ENABLE_EN); 353*5e023e7eSJernej Skrabec 354*5e023e7eSJernej Skrabec for (i = 0; i < 32; i++) { 355*5e023e7eSJernej Skrabec writel(sun4i_horz_coef[2 * i], &de_fe->ch0_horzcoef0[i]); 356*5e023e7eSJernej Skrabec writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch0_horzcoef1[i]); 357*5e023e7eSJernej Skrabec writel(sun4i_vert_coef[i], &de_fe->ch0_vertcoef[i]); 358*5e023e7eSJernej Skrabec writel(sun4i_horz_coef[2 * i], &de_fe->ch1_horzcoef0[i]); 359*5e023e7eSJernej Skrabec writel(sun4i_horz_coef[2 * i + 1], &de_fe->ch1_horzcoef1[i]); 360*5e023e7eSJernej Skrabec writel(sun4i_vert_coef[i], &de_fe->ch1_vertcoef[i]); 361*5e023e7eSJernej Skrabec } 362*5e023e7eSJernej Skrabec 363*5e023e7eSJernej Skrabec setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_COEF_RDY); 364*5e023e7eSJernej Skrabec } 365*5e023e7eSJernej Skrabec 366*5e023e7eSJernej Skrabec static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode, 367*5e023e7eSJernej Skrabec unsigned int address) 368*5e023e7eSJernej Skrabec { 369*5e023e7eSJernej Skrabec struct sunxi_de_fe_reg * const de_fe = 370*5e023e7eSJernej Skrabec (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE; 371*5e023e7eSJernej Skrabec 372*5e023e7eSJernej Skrabec setbits_le32(&de_fe->bypass, SUNXI_DE_FE_BYPASS_CSC_BYPASS); 373*5e023e7eSJernej Skrabec writel(CONFIG_SYS_SDRAM_BASE + address, &de_fe->ch0_addr); 374*5e023e7eSJernej Skrabec writel(mode->xres * 4, &de_fe->ch0_stride); 375*5e023e7eSJernej Skrabec writel(SUNXI_DE_FE_INPUT_FMT_ARGB8888, &de_fe->input_fmt); 376*5e023e7eSJernej Skrabec writel(SUNXI_DE_FE_OUTPUT_FMT_ARGB8888, &de_fe->output_fmt); 377*5e023e7eSJernej Skrabec 378*5e023e7eSJernej Skrabec writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres), 379*5e023e7eSJernej Skrabec &de_fe->ch0_insize); 380*5e023e7eSJernej Skrabec writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres), 381*5e023e7eSJernej Skrabec &de_fe->ch0_outsize); 382*5e023e7eSJernej Skrabec writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_horzfact); 383*5e023e7eSJernej Skrabec writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch0_vertfact); 384*5e023e7eSJernej Skrabec 385*5e023e7eSJernej Skrabec writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres), 386*5e023e7eSJernej Skrabec &de_fe->ch1_insize); 387*5e023e7eSJernej Skrabec writel(SUNXI_DE_FE_HEIGHT(mode->yres) | SUNXI_DE_FE_WIDTH(mode->xres), 388*5e023e7eSJernej Skrabec &de_fe->ch1_outsize); 389*5e023e7eSJernej Skrabec writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_horzfact); 390*5e023e7eSJernej Skrabec writel(SUNXI_DE_FE_FACTOR_INT(1), &de_fe->ch1_vertfact); 391*5e023e7eSJernej Skrabec 392*5e023e7eSJernej Skrabec setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_REG_RDY); 393*5e023e7eSJernej Skrabec } 394*5e023e7eSJernej Skrabec 395*5e023e7eSJernej Skrabec static void sunxi_frontend_enable(void) 396*5e023e7eSJernej Skrabec { 397*5e023e7eSJernej Skrabec struct sunxi_de_fe_reg * const de_fe = 398*5e023e7eSJernej Skrabec (struct sunxi_de_fe_reg *)SUNXI_DE_FE0_BASE; 399*5e023e7eSJernej Skrabec 400*5e023e7eSJernej Skrabec setbits_le32(&de_fe->frame_ctrl, SUNXI_DE_FE_FRAME_CTRL_FRM_START); 401*5e023e7eSJernej Skrabec } 402*5e023e7eSJernej Skrabec #else 403*5e023e7eSJernej Skrabec static void sunxi_frontend_init(void) {} 404*5e023e7eSJernej Skrabec static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode, 405*5e023e7eSJernej Skrabec unsigned int address) {} 406*5e023e7eSJernej Skrabec static void sunxi_frontend_enable(void) {} 407*5e023e7eSJernej Skrabec #endif 408*5e023e7eSJernej Skrabec 409*5e023e7eSJernej Skrabec static bool sunxi_is_composite(void) 410*5e023e7eSJernej Skrabec { 411*5e023e7eSJernej Skrabec switch (sunxi_display.monitor) { 412*5e023e7eSJernej Skrabec case sunxi_monitor_none: 413*5e023e7eSJernej Skrabec case sunxi_monitor_dvi: 414*5e023e7eSJernej Skrabec case sunxi_monitor_hdmi: 415*5e023e7eSJernej Skrabec case sunxi_monitor_lcd: 416*5e023e7eSJernej Skrabec case sunxi_monitor_vga: 417*5e023e7eSJernej Skrabec return false; 418*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal: 419*5e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc: 420*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m: 421*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc: 422*5e023e7eSJernej Skrabec return true; 423*5e023e7eSJernej Skrabec } 424*5e023e7eSJernej Skrabec 425*5e023e7eSJernej Skrabec return false; /* Never reached */ 426*5e023e7eSJernej Skrabec } 427*5e023e7eSJernej Skrabec 428*5e023e7eSJernej Skrabec /* 429*5e023e7eSJernej Skrabec * This is the entity that mixes and matches the different layers and inputs. 430*5e023e7eSJernej Skrabec * Allwinner calls it the back-end, but i like composer better. 431*5e023e7eSJernej Skrabec */ 432*5e023e7eSJernej Skrabec static void sunxi_composer_init(void) 433*5e023e7eSJernej Skrabec { 434*5e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm = 435*5e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 436*5e023e7eSJernej Skrabec struct sunxi_de_be_reg * const de_be = 437*5e023e7eSJernej Skrabec (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; 438*5e023e7eSJernej Skrabec int i; 439*5e023e7eSJernej Skrabec 440*5e023e7eSJernej Skrabec sunxi_frontend_init(); 441*5e023e7eSJernej Skrabec 442*5e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I 443*5e023e7eSJernej Skrabec /* Reset off */ 444*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE_BE0); 445*5e023e7eSJernej Skrabec #endif 446*5e023e7eSJernej Skrabec 447*5e023e7eSJernej Skrabec /* Clocks on */ 448*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE_BE0); 449*5e023e7eSJernej Skrabec #ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */ 450*5e023e7eSJernej Skrabec setbits_le32(&ccm->dram_clk_gate, 1 << CCM_DRAM_GATE_OFFSET_DE_BE0); 451*5e023e7eSJernej Skrabec #endif 452*5e023e7eSJernej Skrabec clock_set_de_mod_clock(&ccm->be0_clk_cfg, 300000000); 453*5e023e7eSJernej Skrabec 454*5e023e7eSJernej Skrabec /* Engine bug, clear registers after reset */ 455*5e023e7eSJernej Skrabec for (i = 0x0800; i < 0x1000; i += 4) 456*5e023e7eSJernej Skrabec writel(0, SUNXI_DE_BE0_BASE + i); 457*5e023e7eSJernej Skrabec 458*5e023e7eSJernej Skrabec setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE); 459*5e023e7eSJernej Skrabec } 460*5e023e7eSJernej Skrabec 461*5e023e7eSJernej Skrabec static u32 sunxi_rgb2yuv_coef[12] = { 462*5e023e7eSJernej Skrabec 0x00000107, 0x00000204, 0x00000064, 0x00000108, 463*5e023e7eSJernej Skrabec 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808, 464*5e023e7eSJernej Skrabec 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808 465*5e023e7eSJernej Skrabec }; 466*5e023e7eSJernej Skrabec 467*5e023e7eSJernej Skrabec static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, 468*5e023e7eSJernej Skrabec unsigned int address) 469*5e023e7eSJernej Skrabec { 470*5e023e7eSJernej Skrabec struct sunxi_de_be_reg * const de_be = 471*5e023e7eSJernej Skrabec (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; 472*5e023e7eSJernej Skrabec int i; 473*5e023e7eSJernej Skrabec 474*5e023e7eSJernej Skrabec sunxi_frontend_mode_set(mode, address); 475*5e023e7eSJernej Skrabec 476*5e023e7eSJernej Skrabec writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres), 477*5e023e7eSJernej Skrabec &de_be->disp_size); 478*5e023e7eSJernej Skrabec writel(SUNXI_DE_BE_HEIGHT(mode->yres) | SUNXI_DE_BE_WIDTH(mode->xres), 479*5e023e7eSJernej Skrabec &de_be->layer0_size); 480*5e023e7eSJernej Skrabec #ifndef CONFIG_MACH_SUN4I /* On sun4i the frontend does the dma */ 481*5e023e7eSJernej Skrabec writel(SUNXI_DE_BE_LAYER_STRIDE(mode->xres), &de_be->layer0_stride); 482*5e023e7eSJernej Skrabec writel(address << 3, &de_be->layer0_addr_low32b); 483*5e023e7eSJernej Skrabec writel(address >> 29, &de_be->layer0_addr_high4b); 484*5e023e7eSJernej Skrabec #else 485*5e023e7eSJernej Skrabec writel(SUNXI_DE_BE_LAYER_ATTR0_SRC_FE0, &de_be->layer0_attr0_ctrl); 486*5e023e7eSJernej Skrabec #endif 487*5e023e7eSJernej Skrabec writel(SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888, &de_be->layer0_attr1_ctrl); 488*5e023e7eSJernej Skrabec 489*5e023e7eSJernej Skrabec setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE); 490*5e023e7eSJernej Skrabec if (mode->vmode == FB_VMODE_INTERLACED) 491*5e023e7eSJernej Skrabec setbits_le32(&de_be->mode, 492*5e023e7eSJernej Skrabec #ifndef CONFIG_MACH_SUN5I 493*5e023e7eSJernej Skrabec SUNXI_DE_BE_MODE_DEFLICKER_ENABLE | 494*5e023e7eSJernej Skrabec #endif 495*5e023e7eSJernej Skrabec SUNXI_DE_BE_MODE_INTERLACE_ENABLE); 496*5e023e7eSJernej Skrabec 497*5e023e7eSJernej Skrabec if (sunxi_is_composite()) { 498*5e023e7eSJernej Skrabec writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE, 499*5e023e7eSJernej Skrabec &de_be->output_color_ctrl); 500*5e023e7eSJernej Skrabec for (i = 0; i < 12; i++) 501*5e023e7eSJernej Skrabec writel(sunxi_rgb2yuv_coef[i], 502*5e023e7eSJernej Skrabec &de_be->output_color_coef[i]); 503*5e023e7eSJernej Skrabec } 504*5e023e7eSJernej Skrabec } 505*5e023e7eSJernej Skrabec 506*5e023e7eSJernej Skrabec static void sunxi_composer_enable(void) 507*5e023e7eSJernej Skrabec { 508*5e023e7eSJernej Skrabec struct sunxi_de_be_reg * const de_be = 509*5e023e7eSJernej Skrabec (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; 510*5e023e7eSJernej Skrabec 511*5e023e7eSJernej Skrabec sunxi_frontend_enable(); 512*5e023e7eSJernej Skrabec 513*5e023e7eSJernej Skrabec setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS); 514*5e023e7eSJernej Skrabec setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START); 515*5e023e7eSJernej Skrabec } 516*5e023e7eSJernej Skrabec 517*5e023e7eSJernej Skrabec /* 518*5e023e7eSJernej Skrabec * LCDC, what allwinner calls a CRTC, so timing controller and serializer. 519*5e023e7eSJernej Skrabec */ 520*5e023e7eSJernej Skrabec static void sunxi_lcdc_pll_set(int tcon, int dotclock, 521*5e023e7eSJernej Skrabec int *clk_div, int *clk_double) 522*5e023e7eSJernej Skrabec { 523*5e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm = 524*5e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 525*5e023e7eSJernej Skrabec int value, n, m, min_m, max_m, diff; 526*5e023e7eSJernej Skrabec int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF; 527*5e023e7eSJernej Skrabec int best_double = 0; 528*5e023e7eSJernej Skrabec bool use_mipi_pll = false; 529*5e023e7eSJernej Skrabec 530*5e023e7eSJernej Skrabec if (tcon == 0) { 531*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL 532*5e023e7eSJernej Skrabec min_m = 6; 533*5e023e7eSJernej Skrabec max_m = 127; 534*5e023e7eSJernej Skrabec #endif 535*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_LVDS 536*5e023e7eSJernej Skrabec min_m = max_m = 7; 537*5e023e7eSJernej Skrabec #endif 538*5e023e7eSJernej Skrabec } else { 539*5e023e7eSJernej Skrabec min_m = 1; 540*5e023e7eSJernej Skrabec max_m = 15; 541*5e023e7eSJernej Skrabec } 542*5e023e7eSJernej Skrabec 543*5e023e7eSJernej Skrabec /* 544*5e023e7eSJernej Skrabec * Find the lowest divider resulting in a matching clock, if there 545*5e023e7eSJernej Skrabec * is no match, pick the closest lower clock, as monitors tend to 546*5e023e7eSJernej Skrabec * not sync to higher frequencies. 547*5e023e7eSJernej Skrabec */ 548*5e023e7eSJernej Skrabec for (m = min_m; m <= max_m; m++) { 549*5e023e7eSJernej Skrabec n = (m * dotclock) / 3000; 550*5e023e7eSJernej Skrabec 551*5e023e7eSJernej Skrabec if ((n >= 9) && (n <= 127)) { 552*5e023e7eSJernej Skrabec value = (3000 * n) / m; 553*5e023e7eSJernej Skrabec diff = dotclock - value; 554*5e023e7eSJernej Skrabec if (diff < best_diff) { 555*5e023e7eSJernej Skrabec best_diff = diff; 556*5e023e7eSJernej Skrabec best_m = m; 557*5e023e7eSJernej Skrabec best_n = n; 558*5e023e7eSJernej Skrabec best_double = 0; 559*5e023e7eSJernej Skrabec } 560*5e023e7eSJernej Skrabec } 561*5e023e7eSJernej Skrabec 562*5e023e7eSJernej Skrabec /* These are just duplicates */ 563*5e023e7eSJernej Skrabec if (!(m & 1)) 564*5e023e7eSJernej Skrabec continue; 565*5e023e7eSJernej Skrabec 566*5e023e7eSJernej Skrabec n = (m * dotclock) / 6000; 567*5e023e7eSJernej Skrabec if ((n >= 9) && (n <= 127)) { 568*5e023e7eSJernej Skrabec value = (6000 * n) / m; 569*5e023e7eSJernej Skrabec diff = dotclock - value; 570*5e023e7eSJernej Skrabec if (diff < best_diff) { 571*5e023e7eSJernej Skrabec best_diff = diff; 572*5e023e7eSJernej Skrabec best_m = m; 573*5e023e7eSJernej Skrabec best_n = n; 574*5e023e7eSJernej Skrabec best_double = 1; 575*5e023e7eSJernej Skrabec } 576*5e023e7eSJernej Skrabec } 577*5e023e7eSJernej Skrabec } 578*5e023e7eSJernej Skrabec 579*5e023e7eSJernej Skrabec #ifdef CONFIG_MACH_SUN6I 580*5e023e7eSJernej Skrabec /* 581*5e023e7eSJernej Skrabec * Use the MIPI pll if we've been unable to find any matching setting 582*5e023e7eSJernej Skrabec * for PLL3, this happens with high dotclocks because of min_m = 6. 583*5e023e7eSJernej Skrabec */ 584*5e023e7eSJernej Skrabec if (tcon == 0 && best_n == 0) { 585*5e023e7eSJernej Skrabec use_mipi_pll = true; 586*5e023e7eSJernej Skrabec best_m = 6; /* Minimum m for tcon0 */ 587*5e023e7eSJernej Skrabec } 588*5e023e7eSJernej Skrabec 589*5e023e7eSJernej Skrabec if (use_mipi_pll) { 590*5e023e7eSJernej Skrabec clock_set_pll3(297000000); /* Fix the video pll at 297 MHz */ 591*5e023e7eSJernej Skrabec clock_set_mipi_pll(best_m * dotclock * 1000); 592*5e023e7eSJernej Skrabec debug("dotclock: %dkHz = %dkHz via mipi pll\n", 593*5e023e7eSJernej Skrabec dotclock, clock_get_mipi_pll() / best_m / 1000); 594*5e023e7eSJernej Skrabec } else 595*5e023e7eSJernej Skrabec #endif 596*5e023e7eSJernej Skrabec { 597*5e023e7eSJernej Skrabec clock_set_pll3(best_n * 3000000); 598*5e023e7eSJernej Skrabec debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n", 599*5e023e7eSJernej Skrabec dotclock, 600*5e023e7eSJernej Skrabec (best_double + 1) * clock_get_pll3() / best_m / 1000, 601*5e023e7eSJernej Skrabec best_double + 1, best_n, best_m); 602*5e023e7eSJernej Skrabec } 603*5e023e7eSJernej Skrabec 604*5e023e7eSJernej Skrabec if (tcon == 0) { 605*5e023e7eSJernej Skrabec u32 pll; 606*5e023e7eSJernej Skrabec 607*5e023e7eSJernej Skrabec if (use_mipi_pll) 608*5e023e7eSJernej Skrabec pll = CCM_LCD_CH0_CTRL_MIPI_PLL; 609*5e023e7eSJernej Skrabec else if (best_double) 610*5e023e7eSJernej Skrabec pll = CCM_LCD_CH0_CTRL_PLL3_2X; 611*5e023e7eSJernej Skrabec else 612*5e023e7eSJernej Skrabec pll = CCM_LCD_CH0_CTRL_PLL3; 613*5e023e7eSJernej Skrabec 614*5e023e7eSJernej Skrabec writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll, 615*5e023e7eSJernej Skrabec &ccm->lcd0_ch0_clk_cfg); 616*5e023e7eSJernej Skrabec } else { 617*5e023e7eSJernej Skrabec writel(CCM_LCD_CH1_CTRL_GATE | 618*5e023e7eSJernej Skrabec (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X : 619*5e023e7eSJernej Skrabec CCM_LCD_CH1_CTRL_PLL3) | 620*5e023e7eSJernej Skrabec CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg); 621*5e023e7eSJernej Skrabec if (sunxi_is_composite()) 622*5e023e7eSJernej Skrabec setbits_le32(&ccm->lcd0_ch1_clk_cfg, 623*5e023e7eSJernej Skrabec CCM_LCD_CH1_CTRL_HALF_SCLK1); 624*5e023e7eSJernej Skrabec } 625*5e023e7eSJernej Skrabec 626*5e023e7eSJernej Skrabec *clk_div = best_m; 627*5e023e7eSJernej Skrabec *clk_double = best_double; 628*5e023e7eSJernej Skrabec } 629*5e023e7eSJernej Skrabec 630*5e023e7eSJernej Skrabec static void sunxi_lcdc_init(void) 631*5e023e7eSJernej Skrabec { 632*5e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm = 633*5e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 634*5e023e7eSJernej Skrabec struct sunxi_lcdc_reg * const lcdc = 635*5e023e7eSJernej Skrabec (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; 636*5e023e7eSJernej Skrabec 637*5e023e7eSJernej Skrabec /* Reset off */ 638*5e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I 639*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0); 640*5e023e7eSJernej Skrabec #else 641*5e023e7eSJernej Skrabec setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_RST); 642*5e023e7eSJernej Skrabec #endif 643*5e023e7eSJernej Skrabec 644*5e023e7eSJernej Skrabec /* Clock on */ 645*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0); 646*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_LVDS 647*5e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I 648*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset2_cfg, 1 << AHB_RESET_OFFSET_LVDS); 649*5e023e7eSJernej Skrabec #else 650*5e023e7eSJernej Skrabec setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST); 651*5e023e7eSJernej Skrabec #endif 652*5e023e7eSJernej Skrabec #endif 653*5e023e7eSJernej Skrabec 654*5e023e7eSJernej Skrabec lcdc_init(lcdc); 655*5e023e7eSJernej Skrabec } 656*5e023e7eSJernej Skrabec 657*5e023e7eSJernej Skrabec static void sunxi_lcdc_panel_enable(void) 658*5e023e7eSJernej Skrabec { 659*5e023e7eSJernej Skrabec int pin, reset_pin; 660*5e023e7eSJernej Skrabec 661*5e023e7eSJernej Skrabec /* 662*5e023e7eSJernej Skrabec * Start with backlight disabled to avoid the screen flashing to 663*5e023e7eSJernej Skrabec * white while the lcd inits. 664*5e023e7eSJernej Skrabec */ 665*5e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN); 666*5e023e7eSJernej Skrabec if (pin >= 0) { 667*5e023e7eSJernej Skrabec gpio_request(pin, "lcd_backlight_enable"); 668*5e023e7eSJernej Skrabec gpio_direction_output(pin, 0); 669*5e023e7eSJernej Skrabec } 670*5e023e7eSJernej Skrabec 671*5e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM); 672*5e023e7eSJernej Skrabec if (pin >= 0) { 673*5e023e7eSJernej Skrabec gpio_request(pin, "lcd_backlight_pwm"); 674*5e023e7eSJernej Skrabec gpio_direction_output(pin, PWM_OFF); 675*5e023e7eSJernej Skrabec } 676*5e023e7eSJernej Skrabec 677*5e023e7eSJernej Skrabec reset_pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_RESET); 678*5e023e7eSJernej Skrabec if (reset_pin >= 0) { 679*5e023e7eSJernej Skrabec gpio_request(reset_pin, "lcd_reset"); 680*5e023e7eSJernej Skrabec gpio_direction_output(reset_pin, 0); /* Assert reset */ 681*5e023e7eSJernej Skrabec } 682*5e023e7eSJernej Skrabec 683*5e023e7eSJernej Skrabec /* Give the backlight some time to turn off and power up the panel. */ 684*5e023e7eSJernej Skrabec mdelay(40); 685*5e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_POWER); 686*5e023e7eSJernej Skrabec if (pin >= 0) { 687*5e023e7eSJernej Skrabec gpio_request(pin, "lcd_power"); 688*5e023e7eSJernej Skrabec gpio_direction_output(pin, 1); 689*5e023e7eSJernej Skrabec } 690*5e023e7eSJernej Skrabec 691*5e023e7eSJernej Skrabec if (reset_pin >= 0) 692*5e023e7eSJernej Skrabec gpio_direction_output(reset_pin, 1); /* De-assert reset */ 693*5e023e7eSJernej Skrabec } 694*5e023e7eSJernej Skrabec 695*5e023e7eSJernej Skrabec static void sunxi_lcdc_backlight_enable(void) 696*5e023e7eSJernej Skrabec { 697*5e023e7eSJernej Skrabec int pin; 698*5e023e7eSJernej Skrabec 699*5e023e7eSJernej Skrabec /* 700*5e023e7eSJernej Skrabec * We want to have scanned out at least one frame before enabling the 701*5e023e7eSJernej Skrabec * backlight to avoid the screen flashing to white when we enable it. 702*5e023e7eSJernej Skrabec */ 703*5e023e7eSJernej Skrabec mdelay(40); 704*5e023e7eSJernej Skrabec 705*5e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_EN); 706*5e023e7eSJernej Skrabec if (pin >= 0) 707*5e023e7eSJernej Skrabec gpio_direction_output(pin, 1); 708*5e023e7eSJernej Skrabec 709*5e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM); 710*5e023e7eSJernej Skrabec #ifdef SUNXI_PWM_PIN0 711*5e023e7eSJernej Skrabec if (pin == SUNXI_PWM_PIN0) { 712*5e023e7eSJernej Skrabec writel(SUNXI_PWM_CTRL_POLARITY0(PWM_ON) | 713*5e023e7eSJernej Skrabec SUNXI_PWM_CTRL_ENABLE0 | 714*5e023e7eSJernej Skrabec SUNXI_PWM_CTRL_PRESCALE0(0xf), SUNXI_PWM_CTRL_REG); 715*5e023e7eSJernej Skrabec writel(SUNXI_PWM_PERIOD_80PCT, SUNXI_PWM_CH0_PERIOD); 716*5e023e7eSJernej Skrabec sunxi_gpio_set_cfgpin(pin, SUNXI_PWM_MUX); 717*5e023e7eSJernej Skrabec return; 718*5e023e7eSJernej Skrabec } 719*5e023e7eSJernej Skrabec #endif 720*5e023e7eSJernej Skrabec if (pin >= 0) 721*5e023e7eSJernej Skrabec gpio_direction_output(pin, PWM_ON); 722*5e023e7eSJernej Skrabec } 723*5e023e7eSJernej Skrabec 724*5e023e7eSJernej Skrabec static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, 725*5e023e7eSJernej Skrabec bool for_ext_vga_dac) 726*5e023e7eSJernej Skrabec { 727*5e023e7eSJernej Skrabec struct sunxi_lcdc_reg * const lcdc = 728*5e023e7eSJernej Skrabec (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; 729*5e023e7eSJernej Skrabec int clk_div, clk_double, pin; 730*5e023e7eSJernej Skrabec 731*5e023e7eSJernej Skrabec #if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS 732*5e023e7eSJernej Skrabec for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) { 733*5e023e7eSJernej Skrabec #else 734*5e023e7eSJernej Skrabec for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) { 735*5e023e7eSJernej Skrabec #endif 736*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL 737*5e023e7eSJernej Skrabec sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0); 738*5e023e7eSJernej Skrabec #endif 739*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_IF_LVDS 740*5e023e7eSJernej Skrabec sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LVDS0); 741*5e023e7eSJernej Skrabec #endif 742*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804 743*5e023e7eSJernej Skrabec sunxi_gpio_set_drv(pin, 3); 744*5e023e7eSJernej Skrabec #endif 745*5e023e7eSJernej Skrabec } 746*5e023e7eSJernej Skrabec 747*5e023e7eSJernej Skrabec sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double); 748*5e023e7eSJernej Skrabec 749*5e023e7eSJernej Skrabec lcdc_tcon0_mode_set(lcdc, mode, clk_div, for_ext_vga_dac, 750*5e023e7eSJernej Skrabec sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE); 751*5e023e7eSJernej Skrabec } 752*5e023e7eSJernej Skrabec 753*5e023e7eSJernej Skrabec #if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE 754*5e023e7eSJernej Skrabec static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, 755*5e023e7eSJernej Skrabec int *clk_div, int *clk_double, 756*5e023e7eSJernej Skrabec bool use_portd_hvsync) 757*5e023e7eSJernej Skrabec { 758*5e023e7eSJernej Skrabec struct sunxi_lcdc_reg * const lcdc = 759*5e023e7eSJernej Skrabec (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; 760*5e023e7eSJernej Skrabec 761*5e023e7eSJernej Skrabec lcdc_tcon1_mode_set(lcdc, mode, use_portd_hvsync, 762*5e023e7eSJernej Skrabec sunxi_is_composite()); 763*5e023e7eSJernej Skrabec 764*5e023e7eSJernej Skrabec if (use_portd_hvsync) { 765*5e023e7eSJernej Skrabec sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0); 766*5e023e7eSJernej Skrabec sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0); 767*5e023e7eSJernej Skrabec } 768*5e023e7eSJernej Skrabec 769*5e023e7eSJernej Skrabec sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double); 770*5e023e7eSJernej Skrabec } 771*5e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */ 772*5e023e7eSJernej Skrabec 773*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI 774*5e023e7eSJernej Skrabec 775*5e023e7eSJernej Skrabec static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode) 776*5e023e7eSJernej Skrabec { 777*5e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi = 778*5e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; 779*5e023e7eSJernej Skrabec u8 checksum = 0; 780*5e023e7eSJernej Skrabec u8 avi_info_frame[17] = { 781*5e023e7eSJernej Skrabec 0x82, 0x02, 0x0d, 0x00, 0x12, 0x00, 0x88, 0x00, 782*5e023e7eSJernej Skrabec 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 783*5e023e7eSJernej Skrabec 0x00 784*5e023e7eSJernej Skrabec }; 785*5e023e7eSJernej Skrabec u8 vendor_info_frame[19] = { 786*5e023e7eSJernej Skrabec 0x81, 0x01, 0x06, 0x29, 0x03, 0x0c, 0x00, 0x40, 787*5e023e7eSJernej Skrabec 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 788*5e023e7eSJernej Skrabec 0x00, 0x00, 0x00 789*5e023e7eSJernej Skrabec }; 790*5e023e7eSJernej Skrabec int i; 791*5e023e7eSJernej Skrabec 792*5e023e7eSJernej Skrabec if (mode->pixclock_khz <= 27000) 793*5e023e7eSJernej Skrabec avi_info_frame[5] = 0x40; /* SD-modes, ITU601 colorspace */ 794*5e023e7eSJernej Skrabec else 795*5e023e7eSJernej Skrabec avi_info_frame[5] = 0x80; /* HD-modes, ITU709 colorspace */ 796*5e023e7eSJernej Skrabec 797*5e023e7eSJernej Skrabec if (mode->xres * 100 / mode->yres < 156) 798*5e023e7eSJernej Skrabec avi_info_frame[5] |= 0x18; /* 4 : 3 */ 799*5e023e7eSJernej Skrabec else 800*5e023e7eSJernej Skrabec avi_info_frame[5] |= 0x28; /* 16 : 9 */ 801*5e023e7eSJernej Skrabec 802*5e023e7eSJernej Skrabec for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++) 803*5e023e7eSJernej Skrabec checksum += avi_info_frame[i]; 804*5e023e7eSJernej Skrabec 805*5e023e7eSJernej Skrabec avi_info_frame[3] = 0x100 - checksum; 806*5e023e7eSJernej Skrabec 807*5e023e7eSJernej Skrabec for (i = 0; i < ARRAY_SIZE(avi_info_frame); i++) 808*5e023e7eSJernej Skrabec writeb(avi_info_frame[i], &hdmi->avi_info_frame[i]); 809*5e023e7eSJernej Skrabec 810*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_QCP_PACKET0, &hdmi->qcp_packet0); 811*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_QCP_PACKET1, &hdmi->qcp_packet1); 812*5e023e7eSJernej Skrabec 813*5e023e7eSJernej Skrabec for (i = 0; i < ARRAY_SIZE(vendor_info_frame); i++) 814*5e023e7eSJernej Skrabec writeb(vendor_info_frame[i], &hdmi->vendor_info_frame[i]); 815*5e023e7eSJernej Skrabec 816*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_PKT_CTRL0, &hdmi->pkt_ctrl0); 817*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_PKT_CTRL1, &hdmi->pkt_ctrl1); 818*5e023e7eSJernej Skrabec 819*5e023e7eSJernej Skrabec setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_HDMI); 820*5e023e7eSJernej Skrabec } 821*5e023e7eSJernej Skrabec 822*5e023e7eSJernej Skrabec static void sunxi_hdmi_mode_set(const struct ctfb_res_modes *mode, 823*5e023e7eSJernej Skrabec int clk_div, int clk_double) 824*5e023e7eSJernej Skrabec { 825*5e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi = 826*5e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; 827*5e023e7eSJernej Skrabec int x, y; 828*5e023e7eSJernej Skrabec 829*5e023e7eSJernej Skrabec /* Write clear interrupt status bits */ 830*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_IRQ_STATUS_BITS, &hdmi->irq); 831*5e023e7eSJernej Skrabec 832*5e023e7eSJernej Skrabec if (sunxi_display.monitor == sunxi_monitor_hdmi) 833*5e023e7eSJernej Skrabec sunxi_hdmi_setup_info_frames(mode); 834*5e023e7eSJernej Skrabec 835*5e023e7eSJernej Skrabec /* Set input sync enable */ 836*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_UNKNOWN_INPUT_SYNC, &hdmi->unknown); 837*5e023e7eSJernej Skrabec 838*5e023e7eSJernej Skrabec /* Init various registers, select pll3 as clock source */ 839*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_VIDEO_POL_TX_CLK, &hdmi->video_polarity); 840*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_PAD_CTRL0_RUN, &hdmi->pad_ctrl0); 841*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_PAD_CTRL1, &hdmi->pad_ctrl1); 842*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_PLL_CTRL, &hdmi->pll_ctrl); 843*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_PLL_DBG0_PLL3, &hdmi->pll_dbg0); 844*5e023e7eSJernej Skrabec 845*5e023e7eSJernej Skrabec /* Setup clk div and doubler */ 846*5e023e7eSJernej Skrabec clrsetbits_le32(&hdmi->pll_ctrl, SUNXI_HDMI_PLL_CTRL_DIV_MASK, 847*5e023e7eSJernej Skrabec SUNXI_HDMI_PLL_CTRL_DIV(clk_div)); 848*5e023e7eSJernej Skrabec if (!clk_double) 849*5e023e7eSJernej Skrabec setbits_le32(&hdmi->pad_ctrl1, SUNXI_HDMI_PAD_CTRL1_HALVE); 850*5e023e7eSJernej Skrabec 851*5e023e7eSJernej Skrabec /* Setup timing registers */ 852*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_Y(mode->yres) | SUNXI_HDMI_X(mode->xres), 853*5e023e7eSJernej Skrabec &hdmi->video_size); 854*5e023e7eSJernej Skrabec 855*5e023e7eSJernej Skrabec x = mode->hsync_len + mode->left_margin; 856*5e023e7eSJernej Skrabec y = mode->vsync_len + mode->upper_margin; 857*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_bp); 858*5e023e7eSJernej Skrabec 859*5e023e7eSJernej Skrabec x = mode->right_margin; 860*5e023e7eSJernej Skrabec y = mode->lower_margin; 861*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_fp); 862*5e023e7eSJernej Skrabec 863*5e023e7eSJernej Skrabec x = mode->hsync_len; 864*5e023e7eSJernej Skrabec y = mode->vsync_len; 865*5e023e7eSJernej Skrabec writel(SUNXI_HDMI_Y(y) | SUNXI_HDMI_X(x), &hdmi->video_spw); 866*5e023e7eSJernej Skrabec 867*5e023e7eSJernej Skrabec if (mode->sync & FB_SYNC_HOR_HIGH_ACT) 868*5e023e7eSJernej Skrabec setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_HOR); 869*5e023e7eSJernej Skrabec 870*5e023e7eSJernej Skrabec if (mode->sync & FB_SYNC_VERT_HIGH_ACT) 871*5e023e7eSJernej Skrabec setbits_le32(&hdmi->video_polarity, SUNXI_HDMI_VIDEO_POL_VER); 872*5e023e7eSJernej Skrabec } 873*5e023e7eSJernej Skrabec 874*5e023e7eSJernej Skrabec static void sunxi_hdmi_enable(void) 875*5e023e7eSJernej Skrabec { 876*5e023e7eSJernej Skrabec struct sunxi_hdmi_reg * const hdmi = 877*5e023e7eSJernej Skrabec (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; 878*5e023e7eSJernej Skrabec 879*5e023e7eSJernej Skrabec udelay(100); 880*5e023e7eSJernej Skrabec setbits_le32(&hdmi->video_ctrl, SUNXI_HDMI_VIDEO_CTRL_ENABLE); 881*5e023e7eSJernej Skrabec } 882*5e023e7eSJernej Skrabec 883*5e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_HDMI */ 884*5e023e7eSJernej Skrabec 885*5e023e7eSJernej Skrabec #if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE 886*5e023e7eSJernej Skrabec 887*5e023e7eSJernej Skrabec static void sunxi_tvencoder_mode_set(void) 888*5e023e7eSJernej Skrabec { 889*5e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm = 890*5e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 891*5e023e7eSJernej Skrabec struct sunxi_tve_reg * const tve = 892*5e023e7eSJernej Skrabec (struct sunxi_tve_reg *)SUNXI_TVE0_BASE; 893*5e023e7eSJernej Skrabec 894*5e023e7eSJernej Skrabec /* Reset off */ 895*5e023e7eSJernej Skrabec setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_TVE_RST); 896*5e023e7eSJernej Skrabec /* Clock on */ 897*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0); 898*5e023e7eSJernej Skrabec 899*5e023e7eSJernej Skrabec switch (sunxi_display.monitor) { 900*5e023e7eSJernej Skrabec case sunxi_monitor_vga: 901*5e023e7eSJernej Skrabec writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | 902*5e023e7eSJernej Skrabec SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | 903*5e023e7eSJernej Skrabec SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl); 904*5e023e7eSJernej Skrabec writel(SUNXI_TVE_CFG0_VGA, &tve->cfg0); 905*5e023e7eSJernej Skrabec writel(SUNXI_TVE_DAC_CFG0_VGA, &tve->dac_cfg0); 906*5e023e7eSJernej Skrabec writel(SUNXI_TVE_UNKNOWN1_VGA, &tve->unknown1); 907*5e023e7eSJernej Skrabec break; 908*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc: 909*5e023e7eSJernej Skrabec writel(SUNXI_TVE_CHROMA_FREQ_PAL_NC, &tve->chroma_freq); 910*5e023e7eSJernej Skrabec /* Fall through */ 911*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal: 912*5e023e7eSJernej Skrabec writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | 913*5e023e7eSJernej Skrabec SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | 914*5e023e7eSJernej Skrabec SUNXI_TVE_GCTRL_DAC_INPUT(2, 3) | 915*5e023e7eSJernej Skrabec SUNXI_TVE_GCTRL_DAC_INPUT(3, 4), &tve->gctrl); 916*5e023e7eSJernej Skrabec writel(SUNXI_TVE_CFG0_PAL, &tve->cfg0); 917*5e023e7eSJernej Skrabec writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0); 918*5e023e7eSJernej Skrabec writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter); 919*5e023e7eSJernej Skrabec writel(SUNXI_TVE_PORCH_NUM_PAL, &tve->porch_num); 920*5e023e7eSJernej Skrabec writel(SUNXI_TVE_LINE_NUM_PAL, &tve->line_num); 921*5e023e7eSJernej Skrabec writel(SUNXI_TVE_BLANK_BLACK_LEVEL_PAL, &tve->blank_black_level); 922*5e023e7eSJernej Skrabec writel(SUNXI_TVE_UNKNOWN1_COMPOSITE, &tve->unknown1); 923*5e023e7eSJernej Skrabec writel(SUNXI_TVE_CBR_LEVEL_PAL, &tve->cbr_level); 924*5e023e7eSJernej Skrabec writel(SUNXI_TVE_BURST_WIDTH_COMPOSITE, &tve->burst_width); 925*5e023e7eSJernej Skrabec writel(SUNXI_TVE_UNKNOWN2_PAL, &tve->unknown2); 926*5e023e7eSJernej Skrabec writel(SUNXI_TVE_ACTIVE_NUM_COMPOSITE, &tve->active_num); 927*5e023e7eSJernej Skrabec writel(SUNXI_TVE_CHROMA_BW_GAIN_COMP, &tve->chroma_bw_gain); 928*5e023e7eSJernej Skrabec writel(SUNXI_TVE_NOTCH_WIDTH_COMPOSITE, &tve->notch_width); 929*5e023e7eSJernej Skrabec writel(SUNXI_TVE_RESYNC_NUM_PAL, &tve->resync_num); 930*5e023e7eSJernej Skrabec writel(SUNXI_TVE_SLAVE_PARA_COMPOSITE, &tve->slave_para); 931*5e023e7eSJernej Skrabec break; 932*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m: 933*5e023e7eSJernej Skrabec writel(SUNXI_TVE_CHROMA_FREQ_PAL_M, &tve->chroma_freq); 934*5e023e7eSJernej Skrabec writel(SUNXI_TVE_COLOR_BURST_PAL_M, &tve->color_burst); 935*5e023e7eSJernej Skrabec /* Fall through */ 936*5e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc: 937*5e023e7eSJernej Skrabec writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | 938*5e023e7eSJernej Skrabec SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | 939*5e023e7eSJernej Skrabec SUNXI_TVE_GCTRL_DAC_INPUT(2, 3) | 940*5e023e7eSJernej Skrabec SUNXI_TVE_GCTRL_DAC_INPUT(3, 4), &tve->gctrl); 941*5e023e7eSJernej Skrabec writel(SUNXI_TVE_CFG0_NTSC, &tve->cfg0); 942*5e023e7eSJernej Skrabec writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0); 943*5e023e7eSJernej Skrabec writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter); 944*5e023e7eSJernej Skrabec writel(SUNXI_TVE_PORCH_NUM_NTSC, &tve->porch_num); 945*5e023e7eSJernej Skrabec writel(SUNXI_TVE_LINE_NUM_NTSC, &tve->line_num); 946*5e023e7eSJernej Skrabec writel(SUNXI_TVE_BLANK_BLACK_LEVEL_NTSC, &tve->blank_black_level); 947*5e023e7eSJernej Skrabec writel(SUNXI_TVE_UNKNOWN1_COMPOSITE, &tve->unknown1); 948*5e023e7eSJernej Skrabec writel(SUNXI_TVE_CBR_LEVEL_NTSC, &tve->cbr_level); 949*5e023e7eSJernej Skrabec writel(SUNXI_TVE_BURST_PHASE_NTSC, &tve->burst_phase); 950*5e023e7eSJernej Skrabec writel(SUNXI_TVE_BURST_WIDTH_COMPOSITE, &tve->burst_width); 951*5e023e7eSJernej Skrabec writel(SUNXI_TVE_UNKNOWN2_NTSC, &tve->unknown2); 952*5e023e7eSJernej Skrabec writel(SUNXI_TVE_SYNC_VBI_LEVEL_NTSC, &tve->sync_vbi_level); 953*5e023e7eSJernej Skrabec writel(SUNXI_TVE_ACTIVE_NUM_COMPOSITE, &tve->active_num); 954*5e023e7eSJernej Skrabec writel(SUNXI_TVE_CHROMA_BW_GAIN_COMP, &tve->chroma_bw_gain); 955*5e023e7eSJernej Skrabec writel(SUNXI_TVE_NOTCH_WIDTH_COMPOSITE, &tve->notch_width); 956*5e023e7eSJernej Skrabec writel(SUNXI_TVE_RESYNC_NUM_NTSC, &tve->resync_num); 957*5e023e7eSJernej Skrabec writel(SUNXI_TVE_SLAVE_PARA_COMPOSITE, &tve->slave_para); 958*5e023e7eSJernej Skrabec break; 959*5e023e7eSJernej Skrabec case sunxi_monitor_none: 960*5e023e7eSJernej Skrabec case sunxi_monitor_dvi: 961*5e023e7eSJernej Skrabec case sunxi_monitor_hdmi: 962*5e023e7eSJernej Skrabec case sunxi_monitor_lcd: 963*5e023e7eSJernej Skrabec break; 964*5e023e7eSJernej Skrabec } 965*5e023e7eSJernej Skrabec } 966*5e023e7eSJernej Skrabec 967*5e023e7eSJernej Skrabec static void sunxi_tvencoder_enable(void) 968*5e023e7eSJernej Skrabec { 969*5e023e7eSJernej Skrabec struct sunxi_tve_reg * const tve = 970*5e023e7eSJernej Skrabec (struct sunxi_tve_reg *)SUNXI_TVE0_BASE; 971*5e023e7eSJernej Skrabec 972*5e023e7eSJernej Skrabec setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE); 973*5e023e7eSJernej Skrabec } 974*5e023e7eSJernej Skrabec 975*5e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE */ 976*5e023e7eSJernej Skrabec 977*5e023e7eSJernej Skrabec static void sunxi_drc_init(void) 978*5e023e7eSJernej Skrabec { 979*5e023e7eSJernej Skrabec #ifdef CONFIG_SUNXI_GEN_SUN6I 980*5e023e7eSJernej Skrabec struct sunxi_ccm_reg * const ccm = 981*5e023e7eSJernej Skrabec (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; 982*5e023e7eSJernej Skrabec 983*5e023e7eSJernej Skrabec /* On sun6i the drc must be clocked even when in pass-through mode */ 984*5e023e7eSJernej Skrabec #ifdef CONFIG_MACH_SUN8I_A33 985*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_SAT); 986*5e023e7eSJernej Skrabec #endif 987*5e023e7eSJernej Skrabec setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DRC0); 988*5e023e7eSJernej Skrabec clock_set_de_mod_clock(&ccm->iep_drc0_clk_cfg, 300000000); 989*5e023e7eSJernej Skrabec #endif 990*5e023e7eSJernej Skrabec } 991*5e023e7eSJernej Skrabec 992*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_VGA_VIA_LCD 993*5e023e7eSJernej Skrabec static void sunxi_vga_external_dac_enable(void) 994*5e023e7eSJernej Skrabec { 995*5e023e7eSJernej Skrabec int pin; 996*5e023e7eSJernej Skrabec 997*5e023e7eSJernej Skrabec pin = sunxi_name_to_gpio(CONFIG_VIDEO_VGA_EXTERNAL_DAC_EN); 998*5e023e7eSJernej Skrabec if (pin >= 0) { 999*5e023e7eSJernej Skrabec gpio_request(pin, "vga_enable"); 1000*5e023e7eSJernej Skrabec gpio_direction_output(pin, 1); 1001*5e023e7eSJernej Skrabec } 1002*5e023e7eSJernej Skrabec } 1003*5e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_VGA_VIA_LCD */ 1004*5e023e7eSJernej Skrabec 1005*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_SSD2828 1006*5e023e7eSJernej Skrabec static int sunxi_ssd2828_init(const struct ctfb_res_modes *mode) 1007*5e023e7eSJernej Skrabec { 1008*5e023e7eSJernej Skrabec struct ssd2828_config cfg = { 1009*5e023e7eSJernej Skrabec .csx_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_CS), 1010*5e023e7eSJernej Skrabec .sck_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_SCLK), 1011*5e023e7eSJernej Skrabec .sdi_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MOSI), 1012*5e023e7eSJernej Skrabec .sdo_pin = name_to_gpio(CONFIG_VIDEO_LCD_SPI_MISO), 1013*5e023e7eSJernej Skrabec .reset_pin = name_to_gpio(CONFIG_VIDEO_LCD_SSD2828_RESET), 1014*5e023e7eSJernej Skrabec .ssd2828_tx_clk_khz = CONFIG_VIDEO_LCD_SSD2828_TX_CLK * 1000, 1015*5e023e7eSJernej Skrabec .ssd2828_color_depth = 24, 1016*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828 1017*5e023e7eSJernej Skrabec .mipi_dsi_number_of_data_lanes = 4, 1018*5e023e7eSJernej Skrabec .mipi_dsi_bitrate_per_data_lane_mbps = 513, 1019*5e023e7eSJernej Skrabec .mipi_dsi_delay_after_exit_sleep_mode_ms = 100, 1020*5e023e7eSJernej Skrabec .mipi_dsi_delay_after_set_display_on_ms = 200 1021*5e023e7eSJernej Skrabec #else 1022*5e023e7eSJernej Skrabec #error MIPI LCD panel needs configuration parameters 1023*5e023e7eSJernej Skrabec #endif 1024*5e023e7eSJernej Skrabec }; 1025*5e023e7eSJernej Skrabec 1026*5e023e7eSJernej Skrabec if (cfg.csx_pin == -1 || cfg.sck_pin == -1 || cfg.sdi_pin == -1) { 1027*5e023e7eSJernej Skrabec printf("SSD2828: SPI pins are not properly configured\n"); 1028*5e023e7eSJernej Skrabec return 1; 1029*5e023e7eSJernej Skrabec } 1030*5e023e7eSJernej Skrabec if (cfg.reset_pin == -1) { 1031*5e023e7eSJernej Skrabec printf("SSD2828: Reset pin is not properly configured\n"); 1032*5e023e7eSJernej Skrabec return 1; 1033*5e023e7eSJernej Skrabec } 1034*5e023e7eSJernej Skrabec 1035*5e023e7eSJernej Skrabec return ssd2828_init(&cfg, mode); 1036*5e023e7eSJernej Skrabec } 1037*5e023e7eSJernej Skrabec #endif /* CONFIG_VIDEO_LCD_SSD2828 */ 1038*5e023e7eSJernej Skrabec 1039*5e023e7eSJernej Skrabec static void sunxi_engines_init(void) 1040*5e023e7eSJernej Skrabec { 1041*5e023e7eSJernej Skrabec sunxi_composer_init(); 1042*5e023e7eSJernej Skrabec sunxi_lcdc_init(); 1043*5e023e7eSJernej Skrabec sunxi_drc_init(); 1044*5e023e7eSJernej Skrabec } 1045*5e023e7eSJernej Skrabec 1046*5e023e7eSJernej Skrabec static void sunxi_mode_set(const struct ctfb_res_modes *mode, 1047*5e023e7eSJernej Skrabec unsigned int address) 1048*5e023e7eSJernej Skrabec { 1049*5e023e7eSJernej Skrabec int __maybe_unused clk_div, clk_double; 1050*5e023e7eSJernej Skrabec struct sunxi_lcdc_reg * const lcdc = 1051*5e023e7eSJernej Skrabec (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; 1052*5e023e7eSJernej Skrabec 1053*5e023e7eSJernej Skrabec switch (sunxi_display.monitor) { 1054*5e023e7eSJernej Skrabec case sunxi_monitor_none: 1055*5e023e7eSJernej Skrabec break; 1056*5e023e7eSJernej Skrabec case sunxi_monitor_dvi: 1057*5e023e7eSJernej Skrabec case sunxi_monitor_hdmi: 1058*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI 1059*5e023e7eSJernej Skrabec sunxi_composer_mode_set(mode, address); 1060*5e023e7eSJernej Skrabec sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0); 1061*5e023e7eSJernej Skrabec sunxi_hdmi_mode_set(mode, clk_div, clk_double); 1062*5e023e7eSJernej Skrabec sunxi_composer_enable(); 1063*5e023e7eSJernej Skrabec lcdc_enable(lcdc, sunxi_display.depth); 1064*5e023e7eSJernej Skrabec sunxi_hdmi_enable(); 1065*5e023e7eSJernej Skrabec #endif 1066*5e023e7eSJernej Skrabec break; 1067*5e023e7eSJernej Skrabec case sunxi_monitor_lcd: 1068*5e023e7eSJernej Skrabec sunxi_lcdc_panel_enable(); 1069*5e023e7eSJernej Skrabec if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804)) { 1070*5e023e7eSJernej Skrabec /* 1071*5e023e7eSJernej Skrabec * The anx9804 needs 1.8V from eldo3, we do this here 1072*5e023e7eSJernej Skrabec * and not via CONFIG_AXP_ELDO3_VOLT from board_init() 1073*5e023e7eSJernej Skrabec * to avoid turning this on when using hdmi output. 1074*5e023e7eSJernej Skrabec */ 1075*5e023e7eSJernej Skrabec axp_set_eldo(3, 1800); 1076*5e023e7eSJernej Skrabec anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4, 1077*5e023e7eSJernej Skrabec ANX9804_DATA_RATE_1620M, 1078*5e023e7eSJernej Skrabec sunxi_display.depth); 1079*5e023e7eSJernej Skrabec } 1080*5e023e7eSJernej Skrabec if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) { 1081*5e023e7eSJernej Skrabec mdelay(50); /* Wait for lcd controller power on */ 1082*5e023e7eSJernej Skrabec hitachi_tx18d42vm_init(); 1083*5e023e7eSJernej Skrabec } 1084*5e023e7eSJernej Skrabec if (IS_ENABLED(CONFIG_VIDEO_LCD_TL059WV5C0)) { 1085*5e023e7eSJernej Skrabec unsigned int orig_i2c_bus = i2c_get_bus_num(); 1086*5e023e7eSJernej Skrabec i2c_set_bus_num(CONFIG_VIDEO_LCD_I2C_BUS); 1087*5e023e7eSJernej Skrabec i2c_reg_write(0x5c, 0x04, 0x42); /* Turn on the LCD */ 1088*5e023e7eSJernej Skrabec i2c_set_bus_num(orig_i2c_bus); 1089*5e023e7eSJernej Skrabec } 1090*5e023e7eSJernej Skrabec sunxi_composer_mode_set(mode, address); 1091*5e023e7eSJernej Skrabec sunxi_lcdc_tcon0_mode_set(mode, false); 1092*5e023e7eSJernej Skrabec sunxi_composer_enable(); 1093*5e023e7eSJernej Skrabec lcdc_enable(lcdc, sunxi_display.depth); 1094*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_LCD_SSD2828 1095*5e023e7eSJernej Skrabec sunxi_ssd2828_init(mode); 1096*5e023e7eSJernej Skrabec #endif 1097*5e023e7eSJernej Skrabec sunxi_lcdc_backlight_enable(); 1098*5e023e7eSJernej Skrabec break; 1099*5e023e7eSJernej Skrabec case sunxi_monitor_vga: 1100*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_VGA 1101*5e023e7eSJernej Skrabec sunxi_composer_mode_set(mode, address); 1102*5e023e7eSJernej Skrabec sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1); 1103*5e023e7eSJernej Skrabec sunxi_tvencoder_mode_set(); 1104*5e023e7eSJernej Skrabec sunxi_composer_enable(); 1105*5e023e7eSJernej Skrabec lcdc_enable(lcdc, sunxi_display.depth); 1106*5e023e7eSJernej Skrabec sunxi_tvencoder_enable(); 1107*5e023e7eSJernej Skrabec #elif defined CONFIG_VIDEO_VGA_VIA_LCD 1108*5e023e7eSJernej Skrabec sunxi_composer_mode_set(mode, address); 1109*5e023e7eSJernej Skrabec sunxi_lcdc_tcon0_mode_set(mode, true); 1110*5e023e7eSJernej Skrabec sunxi_composer_enable(); 1111*5e023e7eSJernej Skrabec lcdc_enable(lcdc, sunxi_display.depth); 1112*5e023e7eSJernej Skrabec sunxi_vga_external_dac_enable(); 1113*5e023e7eSJernej Skrabec #endif 1114*5e023e7eSJernej Skrabec break; 1115*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal: 1116*5e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc: 1117*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m: 1118*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc: 1119*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_COMPOSITE 1120*5e023e7eSJernej Skrabec sunxi_composer_mode_set(mode, address); 1121*5e023e7eSJernej Skrabec sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0); 1122*5e023e7eSJernej Skrabec sunxi_tvencoder_mode_set(); 1123*5e023e7eSJernej Skrabec sunxi_composer_enable(); 1124*5e023e7eSJernej Skrabec lcdc_enable(lcdc, sunxi_display.depth); 1125*5e023e7eSJernej Skrabec sunxi_tvencoder_enable(); 1126*5e023e7eSJernej Skrabec #endif 1127*5e023e7eSJernej Skrabec break; 1128*5e023e7eSJernej Skrabec } 1129*5e023e7eSJernej Skrabec } 1130*5e023e7eSJernej Skrabec 1131*5e023e7eSJernej Skrabec static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor) 1132*5e023e7eSJernej Skrabec { 1133*5e023e7eSJernej Skrabec switch (monitor) { 1134*5e023e7eSJernej Skrabec case sunxi_monitor_none: return "none"; 1135*5e023e7eSJernej Skrabec case sunxi_monitor_dvi: return "dvi"; 1136*5e023e7eSJernej Skrabec case sunxi_monitor_hdmi: return "hdmi"; 1137*5e023e7eSJernej Skrabec case sunxi_monitor_lcd: return "lcd"; 1138*5e023e7eSJernej Skrabec case sunxi_monitor_vga: return "vga"; 1139*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal: return "composite-pal"; 1140*5e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc: return "composite-ntsc"; 1141*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m: return "composite-pal-m"; 1142*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc: return "composite-pal-nc"; 1143*5e023e7eSJernej Skrabec } 1144*5e023e7eSJernej Skrabec return NULL; /* never reached */ 1145*5e023e7eSJernej Skrabec } 1146*5e023e7eSJernej Skrabec 1147*5e023e7eSJernej Skrabec ulong board_get_usable_ram_top(ulong total_size) 1148*5e023e7eSJernej Skrabec { 1149*5e023e7eSJernej Skrabec return gd->ram_top - CONFIG_SUNXI_MAX_FB_SIZE; 1150*5e023e7eSJernej Skrabec } 1151*5e023e7eSJernej Skrabec 1152*5e023e7eSJernej Skrabec static bool sunxi_has_hdmi(void) 1153*5e023e7eSJernej Skrabec { 1154*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI 1155*5e023e7eSJernej Skrabec return true; 1156*5e023e7eSJernej Skrabec #else 1157*5e023e7eSJernej Skrabec return false; 1158*5e023e7eSJernej Skrabec #endif 1159*5e023e7eSJernej Skrabec } 1160*5e023e7eSJernej Skrabec 1161*5e023e7eSJernej Skrabec static bool sunxi_has_lcd(void) 1162*5e023e7eSJernej Skrabec { 1163*5e023e7eSJernej Skrabec char *lcd_mode = CONFIG_VIDEO_LCD_MODE; 1164*5e023e7eSJernej Skrabec 1165*5e023e7eSJernej Skrabec return lcd_mode[0] != 0; 1166*5e023e7eSJernej Skrabec } 1167*5e023e7eSJernej Skrabec 1168*5e023e7eSJernej Skrabec static bool sunxi_has_vga(void) 1169*5e023e7eSJernej Skrabec { 1170*5e023e7eSJernej Skrabec #if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_VGA_VIA_LCD 1171*5e023e7eSJernej Skrabec return true; 1172*5e023e7eSJernej Skrabec #else 1173*5e023e7eSJernej Skrabec return false; 1174*5e023e7eSJernej Skrabec #endif 1175*5e023e7eSJernej Skrabec } 1176*5e023e7eSJernej Skrabec 1177*5e023e7eSJernej Skrabec static bool sunxi_has_composite(void) 1178*5e023e7eSJernej Skrabec { 1179*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_COMPOSITE 1180*5e023e7eSJernej Skrabec return true; 1181*5e023e7eSJernej Skrabec #else 1182*5e023e7eSJernej Skrabec return false; 1183*5e023e7eSJernej Skrabec #endif 1184*5e023e7eSJernej Skrabec } 1185*5e023e7eSJernej Skrabec 1186*5e023e7eSJernej Skrabec static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi) 1187*5e023e7eSJernej Skrabec { 1188*5e023e7eSJernej Skrabec if (allow_hdmi && sunxi_has_hdmi()) 1189*5e023e7eSJernej Skrabec return sunxi_monitor_dvi; 1190*5e023e7eSJernej Skrabec else if (sunxi_has_lcd()) 1191*5e023e7eSJernej Skrabec return sunxi_monitor_lcd; 1192*5e023e7eSJernej Skrabec else if (sunxi_has_vga()) 1193*5e023e7eSJernej Skrabec return sunxi_monitor_vga; 1194*5e023e7eSJernej Skrabec else if (sunxi_has_composite()) 1195*5e023e7eSJernej Skrabec return sunxi_monitor_composite_pal; 1196*5e023e7eSJernej Skrabec else 1197*5e023e7eSJernej Skrabec return sunxi_monitor_none; 1198*5e023e7eSJernej Skrabec } 1199*5e023e7eSJernej Skrabec 1200*5e023e7eSJernej Skrabec void *video_hw_init(void) 1201*5e023e7eSJernej Skrabec { 1202*5e023e7eSJernej Skrabec static GraphicDevice *graphic_device = &sunxi_display.graphic_device; 1203*5e023e7eSJernej Skrabec const struct ctfb_res_modes *mode; 1204*5e023e7eSJernej Skrabec struct ctfb_res_modes custom; 1205*5e023e7eSJernej Skrabec const char *options; 1206*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI 1207*5e023e7eSJernej Skrabec int ret, hpd, hpd_delay, edid; 1208*5e023e7eSJernej Skrabec #endif 1209*5e023e7eSJernej Skrabec int i, overscan_offset, overscan_x, overscan_y; 1210*5e023e7eSJernej Skrabec unsigned int fb_dma_addr; 1211*5e023e7eSJernej Skrabec char mon[16]; 1212*5e023e7eSJernej Skrabec char *lcd_mode = CONFIG_VIDEO_LCD_MODE; 1213*5e023e7eSJernej Skrabec 1214*5e023e7eSJernej Skrabec memset(&sunxi_display, 0, sizeof(struct sunxi_display)); 1215*5e023e7eSJernej Skrabec 1216*5e023e7eSJernej Skrabec video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode, 1217*5e023e7eSJernej Skrabec &sunxi_display.depth, &options); 1218*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI 1219*5e023e7eSJernej Skrabec hpd = video_get_option_int(options, "hpd", 1); 1220*5e023e7eSJernej Skrabec hpd_delay = video_get_option_int(options, "hpd_delay", 500); 1221*5e023e7eSJernej Skrabec edid = video_get_option_int(options, "edid", 1); 1222*5e023e7eSJernej Skrabec #endif 1223*5e023e7eSJernej Skrabec overscan_x = video_get_option_int(options, "overscan_x", -1); 1224*5e023e7eSJernej Skrabec overscan_y = video_get_option_int(options, "overscan_y", -1); 1225*5e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_get_default_mon(true); 1226*5e023e7eSJernej Skrabec video_get_option_string(options, "monitor", mon, sizeof(mon), 1227*5e023e7eSJernej Skrabec sunxi_get_mon_desc(sunxi_display.monitor)); 1228*5e023e7eSJernej Skrabec for (i = 0; i <= SUNXI_MONITOR_LAST; i++) { 1229*5e023e7eSJernej Skrabec if (strcmp(mon, sunxi_get_mon_desc(i)) == 0) { 1230*5e023e7eSJernej Skrabec sunxi_display.monitor = i; 1231*5e023e7eSJernej Skrabec break; 1232*5e023e7eSJernej Skrabec } 1233*5e023e7eSJernej Skrabec } 1234*5e023e7eSJernej Skrabec if (i > SUNXI_MONITOR_LAST) 1235*5e023e7eSJernej Skrabec printf("Unknown monitor: '%s', falling back to '%s'\n", 1236*5e023e7eSJernej Skrabec mon, sunxi_get_mon_desc(sunxi_display.monitor)); 1237*5e023e7eSJernej Skrabec 1238*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_HDMI 1239*5e023e7eSJernej Skrabec /* If HDMI/DVI is selected do HPD & EDID, and handle fallback */ 1240*5e023e7eSJernej Skrabec if (sunxi_display.monitor == sunxi_monitor_dvi || 1241*5e023e7eSJernej Skrabec sunxi_display.monitor == sunxi_monitor_hdmi) { 1242*5e023e7eSJernej Skrabec /* Always call hdp_detect, as it also enables clocks, etc. */ 1243*5e023e7eSJernej Skrabec ret = sunxi_hdmi_hpd_detect(hpd_delay); 1244*5e023e7eSJernej Skrabec if (ret) { 1245*5e023e7eSJernej Skrabec printf("HDMI connected: "); 1246*5e023e7eSJernej Skrabec if (edid && sunxi_hdmi_edid_get_mode(&custom) == 0) 1247*5e023e7eSJernej Skrabec mode = &custom; 1248*5e023e7eSJernej Skrabec } else if (hpd) { 1249*5e023e7eSJernej Skrabec sunxi_hdmi_shutdown(); 1250*5e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_get_default_mon(false); 1251*5e023e7eSJernej Skrabec } /* else continue with hdmi/dvi without a cable connected */ 1252*5e023e7eSJernej Skrabec } 1253*5e023e7eSJernej Skrabec #endif 1254*5e023e7eSJernej Skrabec 1255*5e023e7eSJernej Skrabec switch (sunxi_display.monitor) { 1256*5e023e7eSJernej Skrabec case sunxi_monitor_none: 1257*5e023e7eSJernej Skrabec return NULL; 1258*5e023e7eSJernej Skrabec case sunxi_monitor_dvi: 1259*5e023e7eSJernej Skrabec case sunxi_monitor_hdmi: 1260*5e023e7eSJernej Skrabec if (!sunxi_has_hdmi()) { 1261*5e023e7eSJernej Skrabec printf("HDMI/DVI not supported on this board\n"); 1262*5e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_none; 1263*5e023e7eSJernej Skrabec return NULL; 1264*5e023e7eSJernej Skrabec } 1265*5e023e7eSJernej Skrabec break; 1266*5e023e7eSJernej Skrabec case sunxi_monitor_lcd: 1267*5e023e7eSJernej Skrabec if (!sunxi_has_lcd()) { 1268*5e023e7eSJernej Skrabec printf("LCD not supported on this board\n"); 1269*5e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_none; 1270*5e023e7eSJernej Skrabec return NULL; 1271*5e023e7eSJernej Skrabec } 1272*5e023e7eSJernej Skrabec sunxi_display.depth = video_get_params(&custom, lcd_mode); 1273*5e023e7eSJernej Skrabec mode = &custom; 1274*5e023e7eSJernej Skrabec break; 1275*5e023e7eSJernej Skrabec case sunxi_monitor_vga: 1276*5e023e7eSJernej Skrabec if (!sunxi_has_vga()) { 1277*5e023e7eSJernej Skrabec printf("VGA not supported on this board\n"); 1278*5e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_none; 1279*5e023e7eSJernej Skrabec return NULL; 1280*5e023e7eSJernej Skrabec } 1281*5e023e7eSJernej Skrabec sunxi_display.depth = 18; 1282*5e023e7eSJernej Skrabec break; 1283*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal: 1284*5e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc: 1285*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m: 1286*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc: 1287*5e023e7eSJernej Skrabec if (!sunxi_has_composite()) { 1288*5e023e7eSJernej Skrabec printf("Composite video not supported on this board\n"); 1289*5e023e7eSJernej Skrabec sunxi_display.monitor = sunxi_monitor_none; 1290*5e023e7eSJernej Skrabec return NULL; 1291*5e023e7eSJernej Skrabec } 1292*5e023e7eSJernej Skrabec if (sunxi_display.monitor == sunxi_monitor_composite_pal || 1293*5e023e7eSJernej Skrabec sunxi_display.monitor == sunxi_monitor_composite_pal_nc) 1294*5e023e7eSJernej Skrabec mode = &composite_video_modes[0]; 1295*5e023e7eSJernej Skrabec else 1296*5e023e7eSJernej Skrabec mode = &composite_video_modes[1]; 1297*5e023e7eSJernej Skrabec sunxi_display.depth = 24; 1298*5e023e7eSJernej Skrabec break; 1299*5e023e7eSJernej Skrabec } 1300*5e023e7eSJernej Skrabec 1301*5e023e7eSJernej Skrabec /* Yes these defaults are quite high, overscan on composite sucks... */ 1302*5e023e7eSJernej Skrabec if (overscan_x == -1) 1303*5e023e7eSJernej Skrabec overscan_x = sunxi_is_composite() ? 32 : 0; 1304*5e023e7eSJernej Skrabec if (overscan_y == -1) 1305*5e023e7eSJernej Skrabec overscan_y = sunxi_is_composite() ? 20 : 0; 1306*5e023e7eSJernej Skrabec 1307*5e023e7eSJernej Skrabec sunxi_display.fb_size = 1308*5e023e7eSJernej Skrabec (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff; 1309*5e023e7eSJernej Skrabec overscan_offset = (overscan_y * mode->xres + overscan_x) * 4; 1310*5e023e7eSJernej Skrabec /* We want to keep the fb_base for simplefb page aligned, where as 1311*5e023e7eSJernej Skrabec * the sunxi dma engines will happily accept an unaligned address. */ 1312*5e023e7eSJernej Skrabec if (overscan_offset) 1313*5e023e7eSJernej Skrabec sunxi_display.fb_size += 0x1000; 1314*5e023e7eSJernej Skrabec 1315*5e023e7eSJernej Skrabec if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) { 1316*5e023e7eSJernej Skrabec printf("Error need %dkB for fb, but only %dkB is reserved\n", 1317*5e023e7eSJernej Skrabec sunxi_display.fb_size >> 10, 1318*5e023e7eSJernej Skrabec CONFIG_SUNXI_MAX_FB_SIZE >> 10); 1319*5e023e7eSJernej Skrabec return NULL; 1320*5e023e7eSJernej Skrabec } 1321*5e023e7eSJernej Skrabec 1322*5e023e7eSJernej Skrabec printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n", 1323*5e023e7eSJernej Skrabec mode->xres, mode->yres, 1324*5e023e7eSJernej Skrabec (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "", 1325*5e023e7eSJernej Skrabec sunxi_get_mon_desc(sunxi_display.monitor), 1326*5e023e7eSJernej Skrabec overscan_x, overscan_y); 1327*5e023e7eSJernej Skrabec 1328*5e023e7eSJernej Skrabec gd->fb_base = gd->bd->bi_dram[0].start + 1329*5e023e7eSJernej Skrabec gd->bd->bi_dram[0].size - sunxi_display.fb_size; 1330*5e023e7eSJernej Skrabec sunxi_engines_init(); 1331*5e023e7eSJernej Skrabec 1332*5e023e7eSJernej Skrabec fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE; 1333*5e023e7eSJernej Skrabec sunxi_display.fb_addr = gd->fb_base; 1334*5e023e7eSJernej Skrabec if (overscan_offset) { 1335*5e023e7eSJernej Skrabec fb_dma_addr += 0x1000 - (overscan_offset & 0xfff); 1336*5e023e7eSJernej Skrabec sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff; 1337*5e023e7eSJernej Skrabec memset((void *)gd->fb_base, 0, sunxi_display.fb_size); 1338*5e023e7eSJernej Skrabec flush_cache(gd->fb_base, sunxi_display.fb_size); 1339*5e023e7eSJernej Skrabec } 1340*5e023e7eSJernej Skrabec sunxi_mode_set(mode, fb_dma_addr); 1341*5e023e7eSJernej Skrabec 1342*5e023e7eSJernej Skrabec /* 1343*5e023e7eSJernej Skrabec * These are the only members of this structure that are used. All the 1344*5e023e7eSJernej Skrabec * others are driver specific. The pitch is stored in plnSizeX. 1345*5e023e7eSJernej Skrabec */ 1346*5e023e7eSJernej Skrabec graphic_device->frameAdrs = sunxi_display.fb_addr; 1347*5e023e7eSJernej Skrabec graphic_device->gdfIndex = GDF_32BIT_X888RGB; 1348*5e023e7eSJernej Skrabec graphic_device->gdfBytesPP = 4; 1349*5e023e7eSJernej Skrabec graphic_device->winSizeX = mode->xres - 2 * overscan_x; 1350*5e023e7eSJernej Skrabec graphic_device->winSizeY = mode->yres - 2 * overscan_y; 1351*5e023e7eSJernej Skrabec graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP; 1352*5e023e7eSJernej Skrabec 1353*5e023e7eSJernej Skrabec return graphic_device; 1354*5e023e7eSJernej Skrabec } 1355*5e023e7eSJernej Skrabec 1356*5e023e7eSJernej Skrabec /* 1357*5e023e7eSJernej Skrabec * Simplefb support. 1358*5e023e7eSJernej Skrabec */ 1359*5e023e7eSJernej Skrabec #if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_VIDEO_DT_SIMPLEFB) 1360*5e023e7eSJernej Skrabec int sunxi_simplefb_setup(void *blob) 1361*5e023e7eSJernej Skrabec { 1362*5e023e7eSJernej Skrabec static GraphicDevice *graphic_device = &sunxi_display.graphic_device; 1363*5e023e7eSJernej Skrabec int offset, ret; 1364*5e023e7eSJernej Skrabec u64 start, size; 1365*5e023e7eSJernej Skrabec const char *pipeline = NULL; 1366*5e023e7eSJernej Skrabec 1367*5e023e7eSJernej Skrabec #ifdef CONFIG_MACH_SUN4I 1368*5e023e7eSJernej Skrabec #define PIPELINE_PREFIX "de_fe0-" 1369*5e023e7eSJernej Skrabec #else 1370*5e023e7eSJernej Skrabec #define PIPELINE_PREFIX 1371*5e023e7eSJernej Skrabec #endif 1372*5e023e7eSJernej Skrabec 1373*5e023e7eSJernej Skrabec switch (sunxi_display.monitor) { 1374*5e023e7eSJernej Skrabec case sunxi_monitor_none: 1375*5e023e7eSJernej Skrabec return 0; 1376*5e023e7eSJernej Skrabec case sunxi_monitor_dvi: 1377*5e023e7eSJernej Skrabec case sunxi_monitor_hdmi: 1378*5e023e7eSJernej Skrabec pipeline = PIPELINE_PREFIX "de_be0-lcd0-hdmi"; 1379*5e023e7eSJernej Skrabec break; 1380*5e023e7eSJernej Skrabec case sunxi_monitor_lcd: 1381*5e023e7eSJernej Skrabec pipeline = PIPELINE_PREFIX "de_be0-lcd0"; 1382*5e023e7eSJernej Skrabec break; 1383*5e023e7eSJernej Skrabec case sunxi_monitor_vga: 1384*5e023e7eSJernej Skrabec #ifdef CONFIG_VIDEO_VGA 1385*5e023e7eSJernej Skrabec pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0"; 1386*5e023e7eSJernej Skrabec #elif defined CONFIG_VIDEO_VGA_VIA_LCD 1387*5e023e7eSJernej Skrabec pipeline = PIPELINE_PREFIX "de_be0-lcd0"; 1388*5e023e7eSJernej Skrabec #endif 1389*5e023e7eSJernej Skrabec break; 1390*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal: 1391*5e023e7eSJernej Skrabec case sunxi_monitor_composite_ntsc: 1392*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_m: 1393*5e023e7eSJernej Skrabec case sunxi_monitor_composite_pal_nc: 1394*5e023e7eSJernej Skrabec pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0"; 1395*5e023e7eSJernej Skrabec break; 1396*5e023e7eSJernej Skrabec } 1397*5e023e7eSJernej Skrabec 1398*5e023e7eSJernej Skrabec /* Find a prefilled simpefb node, matching out pipeline config */ 1399*5e023e7eSJernej Skrabec offset = fdt_node_offset_by_compatible(blob, -1, 1400*5e023e7eSJernej Skrabec "allwinner,simple-framebuffer"); 1401*5e023e7eSJernej Skrabec while (offset >= 0) { 1402*5e023e7eSJernej Skrabec ret = fdt_stringlist_search(blob, offset, "allwinner,pipeline", 1403*5e023e7eSJernej Skrabec pipeline); 1404*5e023e7eSJernej Skrabec if (ret == 0) 1405*5e023e7eSJernej Skrabec break; 1406*5e023e7eSJernej Skrabec offset = fdt_node_offset_by_compatible(blob, offset, 1407*5e023e7eSJernej Skrabec "allwinner,simple-framebuffer"); 1408*5e023e7eSJernej Skrabec } 1409*5e023e7eSJernej Skrabec if (offset < 0) { 1410*5e023e7eSJernej Skrabec eprintf("Cannot setup simplefb: node not found\n"); 1411*5e023e7eSJernej Skrabec return 0; /* Keep older kernels working */ 1412*5e023e7eSJernej Skrabec } 1413*5e023e7eSJernej Skrabec 1414*5e023e7eSJernej Skrabec /* 1415*5e023e7eSJernej Skrabec * Do not report the framebuffer as free RAM to the OS, note we cannot 1416*5e023e7eSJernej Skrabec * use fdt_add_mem_rsv() here, because then it is still seen as RAM, 1417*5e023e7eSJernej Skrabec * and e.g. Linux refuses to iomap RAM on ARM, see: 1418*5e023e7eSJernej Skrabec * linux/arch/arm/mm/ioremap.c around line 301. 1419*5e023e7eSJernej Skrabec */ 1420*5e023e7eSJernej Skrabec start = gd->bd->bi_dram[0].start; 1421*5e023e7eSJernej Skrabec size = gd->bd->bi_dram[0].size - sunxi_display.fb_size; 1422*5e023e7eSJernej Skrabec ret = fdt_fixup_memory_banks(blob, &start, &size, 1); 1423*5e023e7eSJernej Skrabec if (ret) { 1424*5e023e7eSJernej Skrabec eprintf("Cannot setup simplefb: Error reserving memory\n"); 1425*5e023e7eSJernej Skrabec return ret; 1426*5e023e7eSJernej Skrabec } 1427*5e023e7eSJernej Skrabec 1428*5e023e7eSJernej Skrabec ret = fdt_setup_simplefb_node(blob, offset, sunxi_display.fb_addr, 1429*5e023e7eSJernej Skrabec graphic_device->winSizeX, graphic_device->winSizeY, 1430*5e023e7eSJernej Skrabec graphic_device->plnSizeX, "x8r8g8b8"); 1431*5e023e7eSJernej Skrabec if (ret) 1432*5e023e7eSJernej Skrabec eprintf("Cannot setup simplefb: Error setting properties\n"); 1433*5e023e7eSJernej Skrabec 1434*5e023e7eSJernej Skrabec return ret; 1435*5e023e7eSJernej Skrabec } 1436*5e023e7eSJernej Skrabec #endif /* CONFIG_OF_BOARD_SETUP && CONFIG_VIDEO_DT_SIMPLEFB */ 1437