]>
Commit | Line | Data |
---|---|---|
f6b690e6 BS |
1 | /* |
2 | * Driver for AT91/AT32 MULTI LAYER LCD Controller | |
3 | * | |
4 | * Copyright (C) 2012 Atmel Corporation | |
5 | * | |
1a459660 | 6 | * SPDX-License-Identifier: GPL-2.0+ |
f6b690e6 BS |
7 | */ |
8 | ||
9 | #include <common.h> | |
10 | #include <asm/io.h> | |
11 | #include <asm/arch/gpio.h> | |
12 | #include <asm/arch/clk.h> | |
13 | #include <lcd.h> | |
14 | #include <atmel_hlcdc.h> | |
15 | ||
f6b690e6 BS |
16 | /* configurable parameters */ |
17 | #define ATMEL_LCDC_CVAL_DEFAULT 0xc8 | |
18 | #define ATMEL_LCDC_DMA_BURST_LEN 8 | |
19 | #ifndef ATMEL_LCDC_GUARD_TIME | |
20 | #define ATMEL_LCDC_GUARD_TIME 1 | |
21 | #endif | |
22 | ||
23 | #define ATMEL_LCDC_FIFO_SIZE 512 | |
24 | ||
25 | #define lcdc_readl(reg) __raw_readl((reg)) | |
26 | #define lcdc_writel(reg, val) __raw_writel((val), (reg)) | |
27 | ||
cfcd1c03 BS |
28 | /* |
29 | * the CLUT register map as following | |
30 | * RCLUT(24 ~ 16), GCLUT(15 ~ 8), BCLUT(7 ~ 0) | |
31 | */ | |
32 | void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) | |
33 | { | |
34 | lcdc_writel(((red << LCDC_BASECLUT_RCLUT_Pos) & LCDC_BASECLUT_RCLUT_Msk) | |
35 | | ((green << LCDC_BASECLUT_GCLUT_Pos) & LCDC_BASECLUT_GCLUT_Msk) | |
36 | | ((blue << LCDC_BASECLUT_BCLUT_Pos) & LCDC_BASECLUT_BCLUT_Msk), | |
37 | panel_info.mmio + ATMEL_LCDC_LUT(regno)); | |
38 | } | |
39 | ||
f6b690e6 BS |
40 | void lcd_ctrl_init(void *lcdbase) |
41 | { | |
42 | unsigned long value; | |
43 | struct lcd_dma_desc *desc; | |
44 | struct atmel_hlcd_regs *regs; | |
45 | ||
46 | if (!has_lcdc()) | |
47 | return; /* No lcdc */ | |
48 | ||
49 | regs = (struct atmel_hlcd_regs *)panel_info.mmio; | |
50 | ||
51 | /* Disable DISP signal */ | |
52 | lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_DISPDIS); | |
53 | while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_DISPSTS)) | |
54 | udelay(1); | |
55 | /* Disable synchronization */ | |
56 | lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_SYNCDIS); | |
57 | while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_LCDSTS)) | |
58 | udelay(1); | |
59 | /* Disable pixel clock */ | |
60 | lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_CLKDIS); | |
61 | while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_CLKSTS)) | |
62 | udelay(1); | |
63 | /* Disable PWM */ | |
64 | lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_PWMDIS); | |
65 | while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_PWMSTS)) | |
66 | udelay(1); | |
67 | ||
68 | /* Set pixel clock */ | |
69 | value = get_lcdc_clk_rate(0) / panel_info.vl_clk; | |
70 | if (get_lcdc_clk_rate(0) % panel_info.vl_clk) | |
71 | value++; | |
72 | ||
73 | if (value < 1) { | |
74 | /* Using system clock as pixel clock */ | |
75 | lcdc_writel(®s->lcdc_lcdcfg0, | |
76 | LCDC_LCDCFG0_CLKDIV(0) | |
77 | | LCDC_LCDCFG0_CGDISHCR | |
78 | | LCDC_LCDCFG0_CGDISHEO | |
79 | | LCDC_LCDCFG0_CGDISOVR1 | |
80 | | LCDC_LCDCFG0_CGDISBASE | |
81 | | panel_info.vl_clk_pol | |
82 | | LCDC_LCDCFG0_CLKSEL); | |
83 | ||
84 | } else { | |
85 | lcdc_writel(®s->lcdc_lcdcfg0, | |
86 | LCDC_LCDCFG0_CLKDIV(value - 2) | |
87 | | LCDC_LCDCFG0_CGDISHCR | |
88 | | LCDC_LCDCFG0_CGDISHEO | |
89 | | LCDC_LCDCFG0_CGDISOVR1 | |
90 | | LCDC_LCDCFG0_CGDISBASE | |
91 | | panel_info.vl_clk_pol); | |
92 | } | |
93 | ||
94 | /* Initialize control register 5 */ | |
95 | value = 0; | |
96 | ||
97 | value |= panel_info.vl_sync; | |
98 | ||
99 | #ifndef LCD_OUTPUT_BPP | |
100 | /* Output is 24bpp */ | |
101 | value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP; | |
102 | #else | |
103 | switch (LCD_OUTPUT_BPP) { | |
104 | case 12: | |
105 | value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP; | |
106 | break; | |
107 | case 16: | |
108 | value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP; | |
109 | break; | |
110 | case 18: | |
111 | value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP; | |
112 | break; | |
113 | case 24: | |
114 | value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP; | |
115 | break; | |
116 | default: | |
117 | BUG(); | |
118 | break; | |
119 | } | |
120 | #endif | |
121 | ||
122 | value |= LCDC_LCDCFG5_GUARDTIME(ATMEL_LCDC_GUARD_TIME); | |
123 | value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS); | |
124 | lcdc_writel(®s->lcdc_lcdcfg5, value); | |
125 | ||
126 | /* Vertical & Horizontal Timing */ | |
127 | value = LCDC_LCDCFG1_VSPW(panel_info.vl_vsync_len - 1); | |
128 | value |= LCDC_LCDCFG1_HSPW(panel_info.vl_hsync_len - 1); | |
129 | lcdc_writel(®s->lcdc_lcdcfg1, value); | |
130 | ||
131 | value = LCDC_LCDCFG2_VBPW(panel_info.vl_lower_margin); | |
132 | value |= LCDC_LCDCFG2_VFPW(panel_info.vl_upper_margin - 1); | |
133 | lcdc_writel(®s->lcdc_lcdcfg2, value); | |
134 | ||
135 | value = LCDC_LCDCFG3_HBPW(panel_info.vl_right_margin - 1); | |
136 | value |= LCDC_LCDCFG3_HFPW(panel_info.vl_left_margin - 1); | |
137 | lcdc_writel(®s->lcdc_lcdcfg3, value); | |
138 | ||
139 | /* Display size */ | |
140 | value = LCDC_LCDCFG4_RPF(panel_info.vl_row - 1); | |
141 | value |= LCDC_LCDCFG4_PPL(panel_info.vl_col - 1); | |
142 | lcdc_writel(®s->lcdc_lcdcfg4, value); | |
143 | ||
144 | lcdc_writel(®s->lcdc_basecfg0, | |
145 | LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO); | |
146 | ||
147 | switch (NBITS(panel_info.vl_bpix)) { | |
148 | case 16: | |
149 | lcdc_writel(®s->lcdc_basecfg1, | |
150 | LCDC_BASECFG1_RGBMODE_16BPP_RGB_565); | |
151 | break; | |
152 | default: | |
153 | BUG(); | |
154 | break; | |
155 | } | |
156 | ||
157 | lcdc_writel(®s->lcdc_basecfg2, LCDC_BASECFG2_XSTRIDE(0)); | |
158 | lcdc_writel(®s->lcdc_basecfg3, 0); | |
159 | lcdc_writel(®s->lcdc_basecfg4, LCDC_BASECFG4_DMA); | |
160 | ||
161 | /* Disable all interrupts */ | |
162 | lcdc_writel(®s->lcdc_lcdidr, ~0UL); | |
163 | lcdc_writel(®s->lcdc_baseidr, ~0UL); | |
164 | ||
165 | /* Setup the DMA descriptor, this descriptor will loop to itself */ | |
166 | desc = (struct lcd_dma_desc *)(lcdbase - 16); | |
167 | ||
168 | desc->address = (u32)lcdbase; | |
169 | /* Disable DMA transfer interrupt & descriptor loaded interrupt. */ | |
170 | desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN | |
171 | | LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH; | |
172 | desc->next = (u32)desc; | |
173 | ||
174 | lcdc_writel(®s->lcdc_baseaddr, desc->address); | |
175 | lcdc_writel(®s->lcdc_basectrl, desc->control); | |
176 | lcdc_writel(®s->lcdc_basenext, desc->next); | |
177 | lcdc_writel(®s->lcdc_basecher, LCDC_BASECHER_CHEN | | |
178 | LCDC_BASECHER_UPDATEEN); | |
179 | ||
180 | /* Enable LCD */ | |
181 | value = lcdc_readl(®s->lcdc_lcden); | |
182 | lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_CLKEN); | |
183 | while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_CLKSTS)) | |
184 | udelay(1); | |
185 | value = lcdc_readl(®s->lcdc_lcden); | |
186 | lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_SYNCEN); | |
187 | while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_LCDSTS)) | |
188 | udelay(1); | |
189 | value = lcdc_readl(®s->lcdc_lcden); | |
190 | lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_DISPEN); | |
191 | while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_DISPSTS)) | |
192 | udelay(1); | |
193 | value = lcdc_readl(®s->lcdc_lcden); | |
194 | lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_PWMEN); | |
195 | while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_PWMSTS)) | |
196 | udelay(1); | |
197 | } |