1f6b690e6SBo Shen /* 2f6b690e6SBo Shen * Driver for AT91/AT32 MULTI LAYER LCD Controller 3f6b690e6SBo Shen * 4f6b690e6SBo Shen * Copyright (C) 2012 Atmel Corporation 5f6b690e6SBo Shen * 6f6b690e6SBo Shen * See file CREDITS for list of people who contributed to this 7f6b690e6SBo Shen * project. 8f6b690e6SBo Shen * 9f6b690e6SBo Shen * This program is free software; you can redistribute it and/or 10f6b690e6SBo Shen * modify it under the terms of the GNU General Public License as 11f6b690e6SBo Shen * published by the Free Software Foundation; either version 2 of 12f6b690e6SBo Shen * the License, or (at your option) any later version. 13f6b690e6SBo Shen * 14f6b690e6SBo Shen * This program is distributed in the hope that it will be useful, 15f6b690e6SBo Shen * but WITHOUT ANY WARRANTY; without even the implied warranty of 16f6b690e6SBo Shen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17f6b690e6SBo Shen * GNU General Public License for more details. 18f6b690e6SBo Shen * 19f6b690e6SBo Shen * You should have received a copy of the GNU General Public License 20f6b690e6SBo Shen * along with this program; if not, write to the Free Software 21f6b690e6SBo Shen * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 22f6b690e6SBo Shen * MA 02111-1307 USA 23f6b690e6SBo Shen */ 24f6b690e6SBo Shen 25f6b690e6SBo Shen #include <common.h> 26f6b690e6SBo Shen #include <asm/io.h> 27f6b690e6SBo Shen #include <asm/arch/gpio.h> 28f6b690e6SBo Shen #include <asm/arch/clk.h> 29f6b690e6SBo Shen #include <lcd.h> 30f6b690e6SBo Shen #include <atmel_hlcdc.h> 31f6b690e6SBo Shen 32f6b690e6SBo Shen int lcd_line_length; 33f6b690e6SBo Shen int lcd_color_fg; 34f6b690e6SBo Shen int lcd_color_bg; 35f6b690e6SBo Shen 36f6b690e6SBo Shen void *lcd_base; /* Start of framebuffer memory */ 37f6b690e6SBo Shen void *lcd_console_address; /* Start of console buffer */ 38f6b690e6SBo Shen 39f6b690e6SBo Shen short console_col; 40f6b690e6SBo Shen short console_row; 41f6b690e6SBo Shen 42f6b690e6SBo Shen /* configurable parameters */ 43f6b690e6SBo Shen #define ATMEL_LCDC_CVAL_DEFAULT 0xc8 44f6b690e6SBo Shen #define ATMEL_LCDC_DMA_BURST_LEN 8 45f6b690e6SBo Shen #ifndef ATMEL_LCDC_GUARD_TIME 46f6b690e6SBo Shen #define ATMEL_LCDC_GUARD_TIME 1 47f6b690e6SBo Shen #endif 48f6b690e6SBo Shen 49f6b690e6SBo Shen #define ATMEL_LCDC_FIFO_SIZE 512 50f6b690e6SBo Shen 51f6b690e6SBo Shen #define lcdc_readl(reg) __raw_readl((reg)) 52f6b690e6SBo Shen #define lcdc_writel(reg, val) __raw_writel((val), (reg)) 53f6b690e6SBo Shen 54*cfcd1c03SBo Shen /* 55*cfcd1c03SBo Shen * the CLUT register map as following 56*cfcd1c03SBo Shen * RCLUT(24 ~ 16), GCLUT(15 ~ 8), BCLUT(7 ~ 0) 57*cfcd1c03SBo Shen */ 58*cfcd1c03SBo Shen void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) 59*cfcd1c03SBo Shen { 60*cfcd1c03SBo Shen lcdc_writel(((red << LCDC_BASECLUT_RCLUT_Pos) & LCDC_BASECLUT_RCLUT_Msk) 61*cfcd1c03SBo Shen | ((green << LCDC_BASECLUT_GCLUT_Pos) & LCDC_BASECLUT_GCLUT_Msk) 62*cfcd1c03SBo Shen | ((blue << LCDC_BASECLUT_BCLUT_Pos) & LCDC_BASECLUT_BCLUT_Msk), 63*cfcd1c03SBo Shen panel_info.mmio + ATMEL_LCDC_LUT(regno)); 64*cfcd1c03SBo Shen } 65*cfcd1c03SBo Shen 66f6b690e6SBo Shen void lcd_ctrl_init(void *lcdbase) 67f6b690e6SBo Shen { 68f6b690e6SBo Shen unsigned long value; 69f6b690e6SBo Shen struct lcd_dma_desc *desc; 70f6b690e6SBo Shen struct atmel_hlcd_regs *regs; 71f6b690e6SBo Shen 72f6b690e6SBo Shen if (!has_lcdc()) 73f6b690e6SBo Shen return; /* No lcdc */ 74f6b690e6SBo Shen 75f6b690e6SBo Shen regs = (struct atmel_hlcd_regs *)panel_info.mmio; 76f6b690e6SBo Shen 77f6b690e6SBo Shen /* Disable DISP signal */ 78f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_DISPDIS); 79f6b690e6SBo Shen while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_DISPSTS)) 80f6b690e6SBo Shen udelay(1); 81f6b690e6SBo Shen /* Disable synchronization */ 82f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_SYNCDIS); 83f6b690e6SBo Shen while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_LCDSTS)) 84f6b690e6SBo Shen udelay(1); 85f6b690e6SBo Shen /* Disable pixel clock */ 86f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_CLKDIS); 87f6b690e6SBo Shen while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_CLKSTS)) 88f6b690e6SBo Shen udelay(1); 89f6b690e6SBo Shen /* Disable PWM */ 90f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_PWMDIS); 91f6b690e6SBo Shen while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_PWMSTS)) 92f6b690e6SBo Shen udelay(1); 93f6b690e6SBo Shen 94f6b690e6SBo Shen /* Set pixel clock */ 95f6b690e6SBo Shen value = get_lcdc_clk_rate(0) / panel_info.vl_clk; 96f6b690e6SBo Shen if (get_lcdc_clk_rate(0) % panel_info.vl_clk) 97f6b690e6SBo Shen value++; 98f6b690e6SBo Shen 99f6b690e6SBo Shen if (value < 1) { 100f6b690e6SBo Shen /* Using system clock as pixel clock */ 101f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcdcfg0, 102f6b690e6SBo Shen LCDC_LCDCFG0_CLKDIV(0) 103f6b690e6SBo Shen | LCDC_LCDCFG0_CGDISHCR 104f6b690e6SBo Shen | LCDC_LCDCFG0_CGDISHEO 105f6b690e6SBo Shen | LCDC_LCDCFG0_CGDISOVR1 106f6b690e6SBo Shen | LCDC_LCDCFG0_CGDISBASE 107f6b690e6SBo Shen | panel_info.vl_clk_pol 108f6b690e6SBo Shen | LCDC_LCDCFG0_CLKSEL); 109f6b690e6SBo Shen 110f6b690e6SBo Shen } else { 111f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcdcfg0, 112f6b690e6SBo Shen LCDC_LCDCFG0_CLKDIV(value - 2) 113f6b690e6SBo Shen | LCDC_LCDCFG0_CGDISHCR 114f6b690e6SBo Shen | LCDC_LCDCFG0_CGDISHEO 115f6b690e6SBo Shen | LCDC_LCDCFG0_CGDISOVR1 116f6b690e6SBo Shen | LCDC_LCDCFG0_CGDISBASE 117f6b690e6SBo Shen | panel_info.vl_clk_pol); 118f6b690e6SBo Shen } 119f6b690e6SBo Shen 120f6b690e6SBo Shen /* Initialize control register 5 */ 121f6b690e6SBo Shen value = 0; 122f6b690e6SBo Shen 123f6b690e6SBo Shen value |= panel_info.vl_sync; 124f6b690e6SBo Shen 125f6b690e6SBo Shen #ifndef LCD_OUTPUT_BPP 126f6b690e6SBo Shen /* Output is 24bpp */ 127f6b690e6SBo Shen value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP; 128f6b690e6SBo Shen #else 129f6b690e6SBo Shen switch (LCD_OUTPUT_BPP) { 130f6b690e6SBo Shen case 12: 131f6b690e6SBo Shen value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP; 132f6b690e6SBo Shen break; 133f6b690e6SBo Shen case 16: 134f6b690e6SBo Shen value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP; 135f6b690e6SBo Shen break; 136f6b690e6SBo Shen case 18: 137f6b690e6SBo Shen value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP; 138f6b690e6SBo Shen break; 139f6b690e6SBo Shen case 24: 140f6b690e6SBo Shen value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP; 141f6b690e6SBo Shen break; 142f6b690e6SBo Shen default: 143f6b690e6SBo Shen BUG(); 144f6b690e6SBo Shen break; 145f6b690e6SBo Shen } 146f6b690e6SBo Shen #endif 147f6b690e6SBo Shen 148f6b690e6SBo Shen value |= LCDC_LCDCFG5_GUARDTIME(ATMEL_LCDC_GUARD_TIME); 149f6b690e6SBo Shen value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS); 150f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcdcfg5, value); 151f6b690e6SBo Shen 152f6b690e6SBo Shen /* Vertical & Horizontal Timing */ 153f6b690e6SBo Shen value = LCDC_LCDCFG1_VSPW(panel_info.vl_vsync_len - 1); 154f6b690e6SBo Shen value |= LCDC_LCDCFG1_HSPW(panel_info.vl_hsync_len - 1); 155f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcdcfg1, value); 156f6b690e6SBo Shen 157f6b690e6SBo Shen value = LCDC_LCDCFG2_VBPW(panel_info.vl_lower_margin); 158f6b690e6SBo Shen value |= LCDC_LCDCFG2_VFPW(panel_info.vl_upper_margin - 1); 159f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcdcfg2, value); 160f6b690e6SBo Shen 161f6b690e6SBo Shen value = LCDC_LCDCFG3_HBPW(panel_info.vl_right_margin - 1); 162f6b690e6SBo Shen value |= LCDC_LCDCFG3_HFPW(panel_info.vl_left_margin - 1); 163f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcdcfg3, value); 164f6b690e6SBo Shen 165f6b690e6SBo Shen /* Display size */ 166f6b690e6SBo Shen value = LCDC_LCDCFG4_RPF(panel_info.vl_row - 1); 167f6b690e6SBo Shen value |= LCDC_LCDCFG4_PPL(panel_info.vl_col - 1); 168f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcdcfg4, value); 169f6b690e6SBo Shen 170f6b690e6SBo Shen lcdc_writel(®s->lcdc_basecfg0, 171f6b690e6SBo Shen LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO); 172f6b690e6SBo Shen 173f6b690e6SBo Shen switch (NBITS(panel_info.vl_bpix)) { 174f6b690e6SBo Shen case 16: 175f6b690e6SBo Shen lcdc_writel(®s->lcdc_basecfg1, 176f6b690e6SBo Shen LCDC_BASECFG1_RGBMODE_16BPP_RGB_565); 177f6b690e6SBo Shen break; 178f6b690e6SBo Shen default: 179f6b690e6SBo Shen BUG(); 180f6b690e6SBo Shen break; 181f6b690e6SBo Shen } 182f6b690e6SBo Shen 183f6b690e6SBo Shen lcdc_writel(®s->lcdc_basecfg2, LCDC_BASECFG2_XSTRIDE(0)); 184f6b690e6SBo Shen lcdc_writel(®s->lcdc_basecfg3, 0); 185f6b690e6SBo Shen lcdc_writel(®s->lcdc_basecfg4, LCDC_BASECFG4_DMA); 186f6b690e6SBo Shen 187f6b690e6SBo Shen /* Disable all interrupts */ 188f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcdidr, ~0UL); 189f6b690e6SBo Shen lcdc_writel(®s->lcdc_baseidr, ~0UL); 190f6b690e6SBo Shen 191f6b690e6SBo Shen /* Setup the DMA descriptor, this descriptor will loop to itself */ 192f6b690e6SBo Shen desc = (struct lcd_dma_desc *)(lcdbase - 16); 193f6b690e6SBo Shen 194f6b690e6SBo Shen desc->address = (u32)lcdbase; 195f6b690e6SBo Shen /* Disable DMA transfer interrupt & descriptor loaded interrupt. */ 196f6b690e6SBo Shen desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN 197f6b690e6SBo Shen | LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH; 198f6b690e6SBo Shen desc->next = (u32)desc; 199f6b690e6SBo Shen 200f6b690e6SBo Shen lcdc_writel(®s->lcdc_baseaddr, desc->address); 201f6b690e6SBo Shen lcdc_writel(®s->lcdc_basectrl, desc->control); 202f6b690e6SBo Shen lcdc_writel(®s->lcdc_basenext, desc->next); 203f6b690e6SBo Shen lcdc_writel(®s->lcdc_basecher, LCDC_BASECHER_CHEN | 204f6b690e6SBo Shen LCDC_BASECHER_UPDATEEN); 205f6b690e6SBo Shen 206f6b690e6SBo Shen /* Enable LCD */ 207f6b690e6SBo Shen value = lcdc_readl(®s->lcdc_lcden); 208f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_CLKEN); 209f6b690e6SBo Shen while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_CLKSTS)) 210f6b690e6SBo Shen udelay(1); 211f6b690e6SBo Shen value = lcdc_readl(®s->lcdc_lcden); 212f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_SYNCEN); 213f6b690e6SBo Shen while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_LCDSTS)) 214f6b690e6SBo Shen udelay(1); 215f6b690e6SBo Shen value = lcdc_readl(®s->lcdc_lcden); 216f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_DISPEN); 217f6b690e6SBo Shen while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_DISPSTS)) 218f6b690e6SBo Shen udelay(1); 219f6b690e6SBo Shen value = lcdc_readl(®s->lcdc_lcden); 220f6b690e6SBo Shen lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_PWMEN); 221f6b690e6SBo Shen while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_PWMSTS)) 222f6b690e6SBo Shen udelay(1); 223f6b690e6SBo Shen } 224