]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
3c5fabd1 | 2 | /* |
e880a5e2 HS |
3 | * Copyright (C) 2013-2018 Hannes Schmelzer <[email protected]> |
4 | * B&R Industrial Automation GmbH - http://www.br-automation.com | |
96b109ba | 5 | * Copyright (C) 2020 Dario Binacchi <[email protected]> |
3c5fabd1 HP |
6 | * |
7 | * minimal framebuffer driver for TI's AM335x SoC to be compatible with | |
8 | * Wolfgang Denk's LCD-Framework (CONFIG_LCD, common/lcd.c) | |
9 | * | |
7d045170 | 10 | * - supporting 16/24/32bit RGB/TFT raster Mode (not using palette) |
3c5fabd1 HP |
11 | * - sets up LCD controller as in 'am335x_lcdpanel' struct given |
12 | * - starts output DMA from gd->fb_base buffer | |
3c5fabd1 HP |
13 | */ |
14 | #include <common.h> | |
96b109ba | 15 | #include <dm.h> |
295ab882 DB |
16 | #include <lcd.h> |
17 | #include <video.h> | |
18 | #include <asm/arch/clock.h> | |
3c5fabd1 | 19 | #include <asm/arch/hardware.h> |
8a094f50 | 20 | #include <asm/arch/omap.h> |
8a094f50 | 21 | #include <asm/arch/sys_proto.h> |
295ab882 | 22 | #include <asm/io.h> |
96b109ba | 23 | #include <asm/utils.h> |
a9df3c50 | 24 | #include <linux/err.h> |
3c5fabd1 HP |
25 | #include "am335x-fb.h" |
26 | ||
8a094f50 | 27 | #define LCDC_FMAX 200000000 |
3c5fabd1 HP |
28 | |
29 | /* LCD Control Register */ | |
a9df3c50 | 30 | #define LCDC_CTRL_CLK_DIVISOR_MASK GENMASK(15, 8) |
41f76a01 DB |
31 | #define LCDC_CTRL_RASTER_MODE BIT(0) |
32 | #define LCDC_CTRL_CLK_DIVISOR(x) (((x) & GENMASK(7, 0)) << 8) | |
3c5fabd1 | 33 | /* LCD Clock Enable Register */ |
41f76a01 DB |
34 | #define LCDC_CLKC_ENABLE_CORECLKEN BIT(0) |
35 | #define LCDC_CLKC_ENABLE_LIDDCLKEN BIT(1) | |
36 | #define LCDC_CLKC_ENABLE_DMACLKEN BIT(2) | |
3c5fabd1 | 37 | /* LCD DMA Control Register */ |
41f76a01 DB |
38 | #define LCDC_DMA_CTRL_BURST_SIZE(x) (((x) & GENMASK(2, 0)) << 4) |
39 | #define LCDC_DMA_CTRL_BURST_1 0x0 | |
40 | #define LCDC_DMA_CTRL_BURST_2 0x1 | |
41 | #define LCDC_DMA_CTRL_BURST_4 0x2 | |
42 | #define LCDC_DMA_CTRL_BURST_8 0x3 | |
43 | #define LCDC_DMA_CTRL_BURST_16 0x4 | |
96b109ba | 44 | #define LCDC_DMA_CTRL_FIFO_TH(x) (((x) & GENMASK(2, 0)) << 8) |
3c5fabd1 | 45 | /* LCD Timing_0 Register */ |
0aff8e26 | 46 | #define LCDC_RASTER_TIMING_0_HORMSB(x) ((((x) - 1) & BIT(10)) >> 7) |
41f76a01 DB |
47 | #define LCDC_RASTER_TIMING_0_HORLSB(x) (((((x) >> 4) - 1) & GENMASK(5, 0)) << 4) |
48 | #define LCDC_RASTER_TIMING_0_HSWLSB(x) ((((x) - 1) & GENMASK(5, 0)) << 10) | |
49 | #define LCDC_RASTER_TIMING_0_HFPLSB(x) ((((x) - 1) & GENMASK(7, 0)) << 16) | |
50 | #define LCDC_RASTER_TIMING_0_HBPLSB(x) ((((x) - 1) & GENMASK(7, 0)) << 24) | |
3c5fabd1 | 51 | /* LCD Timing_1 Register */ |
41f76a01 DB |
52 | #define LCDC_RASTER_TIMING_1_VERLSB(x) (((x) - 1) & GENMASK(9, 0)) |
53 | #define LCDC_RASTER_TIMING_1_VSW(x) ((((x) - 1) & GENMASK(5, 0)) << 10) | |
54 | #define LCDC_RASTER_TIMING_1_VFP(x) (((x) & GENMASK(7, 0)) << 16) | |
55 | #define LCDC_RASTER_TIMING_1_VBP(x) (((x) & GENMASK(7, 0)) << 24) | |
3c5fabd1 | 56 | /* LCD Timing_2 Register */ |
41f76a01 DB |
57 | #define LCDC_RASTER_TIMING_2_HFPMSB(x) ((((x) - 1) & GENMASK(9, 8)) >> 8) |
58 | #define LCDC_RASTER_TIMING_2_HBPMSB(x) ((((x) - 1) & GENMASK(9, 8)) >> 4) | |
96b109ba DB |
59 | #define LCDC_RASTER_TIMING_2_ACB(x) (((x) & GENMASK(7, 0)) << 8) |
60 | #define LCDC_RASTER_TIMING_2_ACBI(x) (((x) & GENMASK(3, 0)) << 16) | |
61 | #define LCDC_RASTER_TIMING_2_VSYNC_INVERT BIT(20) | |
62 | #define LCDC_RASTER_TIMING_2_HSYNC_INVERT BIT(21) | |
63 | #define LCDC_RASTER_TIMING_2_PXCLK_INVERT BIT(22) | |
64 | #define LCDC_RASTER_TIMING_2_DE_INVERT BIT(23) | |
65 | #define LCDC_RASTER_TIMING_2_HSVS_RISEFALL BIT(24) | |
66 | #define LCDC_RASTER_TIMING_2_HSVS_CONTROL BIT(25) | |
41f76a01 DB |
67 | #define LCDC_RASTER_TIMING_2_VERMSB(x) ((((x) - 1) & BIT(10)) << 16) |
68 | #define LCDC_RASTER_TIMING_2_HSWMSB(x) ((((x) - 1) & GENMASK(9, 6)) << 21) | |
3c5fabd1 | 69 | /* LCD Raster Ctrl Register */ |
41f76a01 DB |
70 | #define LCDC_RASTER_CTRL_ENABLE BIT(0) |
71 | #define LCDC_RASTER_CTRL_TFT_MODE BIT(7) | |
96b109ba DB |
72 | #define LCDC_RASTER_CTRL_DATA_ORDER BIT(8) |
73 | #define LCDC_RASTER_CTRL_REQDLY(x) (((x) & GENMASK(7, 0)) << 12) | |
41f76a01 | 74 | #define LCDC_RASTER_CTRL_PALMODE_RAWDATA (0x02 << 20) |
96b109ba | 75 | #define LCDC_RASTER_CTRL_TFT_ALT_ENABLE BIT(23) |
41f76a01 DB |
76 | #define LCDC_RASTER_CTRL_TFT_24BPP_MODE BIT(25) |
77 | #define LCDC_RASTER_CTRL_TFT_24BPP_UNPACK BIT(26) | |
3c5fabd1 | 78 | |
3c5fabd1 HP |
79 | struct am335x_lcdhw { |
80 | unsigned int pid; /* 0x00 */ | |
81 | unsigned int ctrl; /* 0x04 */ | |
82 | unsigned int gap0; /* 0x08 */ | |
83 | unsigned int lidd_ctrl; /* 0x0C */ | |
84 | unsigned int lidd_cs0_conf; /* 0x10 */ | |
85 | unsigned int lidd_cs0_addr; /* 0x14 */ | |
86 | unsigned int lidd_cs0_data; /* 0x18 */ | |
87 | unsigned int lidd_cs1_conf; /* 0x1C */ | |
88 | unsigned int lidd_cs1_addr; /* 0x20 */ | |
89 | unsigned int lidd_cs1_data; /* 0x24 */ | |
90 | unsigned int raster_ctrl; /* 0x28 */ | |
91 | unsigned int raster_timing0; /* 0x2C */ | |
92 | unsigned int raster_timing1; /* 0x30 */ | |
93 | unsigned int raster_timing2; /* 0x34 */ | |
94 | unsigned int raster_subpanel; /* 0x38 */ | |
95 | unsigned int raster_subpanel2; /* 0x3C */ | |
96 | unsigned int lcddma_ctrl; /* 0x40 */ | |
97 | unsigned int lcddma_fb0_base; /* 0x44 */ | |
98 | unsigned int lcddma_fb0_ceiling; /* 0x48 */ | |
99 | unsigned int lcddma_fb1_base; /* 0x4C */ | |
100 | unsigned int lcddma_fb1_ceiling; /* 0x50 */ | |
101 | unsigned int sysconfig; /* 0x54 */ | |
102 | unsigned int irqstatus_raw; /* 0x58 */ | |
103 | unsigned int irqstatus; /* 0x5C */ | |
104 | unsigned int irqenable_set; /* 0x60 */ | |
105 | unsigned int irqenable_clear; /* 0x64 */ | |
106 | unsigned int gap1; /* 0x68 */ | |
107 | unsigned int clkc_enable; /* 0x6C */ | |
108 | unsigned int clkc_reset; /* 0x70 */ | |
109 | }; | |
110 | ||
a9df3c50 DB |
111 | struct dpll_data { |
112 | unsigned long rounded_rate; | |
113 | u16 rounded_m; | |
114 | u8 rounded_n; | |
115 | u8 rounded_div; | |
116 | }; | |
117 | ||
3c5fabd1 HP |
118 | DECLARE_GLOBAL_DATA_PTR; |
119 | ||
a9df3c50 DB |
120 | /** |
121 | * am335x_dpll_round_rate() - Round a target rate for an OMAP DPLL | |
122 | * | |
123 | * @dpll_data: struct dpll_data pointer for the DPLL | |
124 | * @rate: New DPLL clock rate | |
125 | * @return rounded rate and the computed m, n and div values in the dpll_data | |
126 | * structure, or -ve error code. | |
127 | */ | |
128 | static ulong am335x_dpll_round_rate(struct dpll_data *dd, ulong rate) | |
129 | { | |
130 | unsigned int m, n, d; | |
131 | unsigned long rounded_rate; | |
132 | int err, err_r; | |
133 | ||
134 | dd->rounded_rate = -EFAULT; | |
135 | err = rate; | |
136 | err_r = err; | |
137 | ||
138 | for (d = 2; err && d < 255; d++) { | |
139 | for (m = 2; m < 2047; m++) { | |
140 | if ((V_OSCK * m) < (rate * d)) | |
141 | continue; | |
142 | ||
143 | n = (V_OSCK * m) / (rate * d); | |
144 | if (n > 127) | |
145 | break; | |
146 | ||
147 | if (((V_OSCK * m) / n) > LCDC_FMAX) | |
148 | break; | |
149 | ||
150 | rounded_rate = (V_OSCK * m) / n / d; | |
151 | err = abs(rounded_rate - rate); | |
152 | if (err < err_r) { | |
153 | err_r = err; | |
154 | dd->rounded_rate = rounded_rate; | |
155 | dd->rounded_m = m; | |
156 | dd->rounded_n = n; | |
157 | dd->rounded_div = d; | |
158 | if (err == 0) | |
159 | break; | |
160 | } | |
161 | } | |
162 | } | |
163 | ||
164 | debug("DPLL display: best error %d Hz (M %d, N %d, DIV %d)\n", | |
165 | err_r, dd->rounded_m, dd->rounded_n, dd->rounded_div); | |
166 | ||
167 | return dd->rounded_rate; | |
168 | } | |
169 | ||
170 | /** | |
171 | * am335x_fb_set_pixel_clk_rate() - Set pixel clock rate. | |
172 | * | |
173 | * @am335x_lcdhw: Base address of the LCD controller registers. | |
174 | * @rate: New clock rate in Hz. | |
175 | * @return new rate, or -ve error code. | |
176 | */ | |
177 | static ulong am335x_fb_set_pixel_clk_rate(struct am335x_lcdhw *regs, ulong rate) | |
178 | { | |
179 | struct dpll_params dpll_disp = { 1, 0, 1, -1, -1, -1, -1 }; | |
180 | struct dpll_data dd; | |
181 | ulong round_rate; | |
182 | u32 reg; | |
183 | ||
184 | round_rate = am335x_dpll_round_rate(&dd, rate); | |
185 | if (IS_ERR_VALUE(round_rate)) | |
186 | return round_rate; | |
187 | ||
188 | dpll_disp.m = dd.rounded_m; | |
189 | dpll_disp.n = dd.rounded_n; | |
190 | do_setup_dpll(&dpll_disp_regs, &dpll_disp); | |
191 | ||
192 | reg = readl(®s->ctrl) & ~LCDC_CTRL_CLK_DIVISOR_MASK; | |
193 | reg |= LCDC_CTRL_CLK_DIVISOR(dd.rounded_div); | |
194 | writel(reg, ®s->ctrl); | |
195 | return round_rate; | |
196 | } | |
197 | ||
96b109ba DB |
198 | #if !CONFIG_IS_ENABLED(DM_VIDEO) |
199 | ||
200 | #if !defined(LCD_CNTL_BASE) | |
201 | #error "hw-base address of LCD-Controller (LCD_CNTL_BASE) not defined!" | |
202 | #endif | |
203 | ||
204 | /* Macro definitions */ | |
205 | #define FBSIZE(x) (((x)->hactive * (x)->vactive * (x)->bpp) >> 3) | |
206 | ||
207 | #define LCDC_RASTER_TIMING_2_INVMASK(x) ((x) & GENMASK(25, 20)) | |
208 | ||
209 | static struct am335x_lcdhw *lcdhw = (void *)LCD_CNTL_BASE; | |
210 | ||
3c5fabd1 HP |
211 | int lcd_get_size(int *line_length) |
212 | { | |
213 | *line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8; | |
214 | return *line_length * panel_info.vl_row + 0x20; | |
215 | } | |
216 | ||
217 | int am335xfb_init(struct am335x_lcdpanel *panel) | |
218 | { | |
7d045170 | 219 | u32 raster_ctrl = 0; |
8a094f50 | 220 | struct cm_dpll *const cmdpll = (struct cm_dpll *)CM_DPLL; |
a9df3c50 DB |
221 | ulong rate; |
222 | u32 reg; | |
8a094f50 | 223 | |
0d8a7d6f | 224 | if (gd->fb_base == 0) { |
3c5fabd1 HP |
225 | printf("ERROR: no valid fb_base stored in GLOBAL_DATA_PTR!\n"); |
226 | return -1; | |
227 | } | |
0d8a7d6f | 228 | if (panel == NULL) { |
3c5fabd1 HP |
229 | printf("ERROR: missing ptr to am335x_lcdpanel!\n"); |
230 | return -1; | |
231 | } | |
232 | ||
7d045170 MP |
233 | /* We can already set the bits for the raster_ctrl in this check */ |
234 | switch (panel->bpp) { | |
235 | case 16: | |
236 | break; | |
237 | case 32: | |
41f76a01 | 238 | raster_ctrl |= LCDC_RASTER_CTRL_TFT_24BPP_UNPACK; |
7d045170 MP |
239 | /* fallthrough */ |
240 | case 24: | |
41f76a01 | 241 | raster_ctrl |= LCDC_RASTER_CTRL_TFT_24BPP_MODE; |
7d045170 MP |
242 | break; |
243 | default: | |
9b643e31 | 244 | pr_err("am335x-fb: invalid bpp value: %d\n", panel->bpp); |
7d045170 MP |
245 | return -1; |
246 | } | |
247 | ||
8a094f50 HS |
248 | /* check given clock-frequency */ |
249 | if (panel->pxl_clk > (LCDC_FMAX / 2)) { | |
250 | pr_err("am335x-fb: requested pxl-clk: %d not supported!\n", | |
251 | panel->pxl_clk); | |
252 | return -1; | |
253 | } | |
254 | ||
3c5fabd1 HP |
255 | debug("setting up LCD-Controller for %dx%dx%d (hfp=%d,hbp=%d,hsw=%d / ", |
256 | panel->hactive, panel->vactive, panel->bpp, | |
257 | panel->hfp, panel->hbp, panel->hsw); | |
8a094f50 HS |
258 | debug("vfp=%d,vbp=%d,vsw=%d / clk=%d)\n", |
259 | panel->vfp, panel->vfp, panel->vsw, panel->pxl_clk); | |
3c5fabd1 HP |
260 | debug("using frambuffer at 0x%08x with size %d.\n", |
261 | (unsigned int)gd->fb_base, FBSIZE(panel)); | |
262 | ||
a9df3c50 DB |
263 | rate = am335x_fb_set_pixel_clk_rate(lcdhw, panel->pxl_clk); |
264 | if (IS_ERR_VALUE(rate)) | |
265 | return rate; | |
8a094f50 HS |
266 | |
267 | /* clock source for LCDC from dispPLL M2 */ | |
268 | writel(0x0, &cmdpll->clklcdcpixelclk); | |
269 | ||
3c5fabd1 HP |
270 | /* palette default entry */ |
271 | memset((void *)gd->fb_base, 0, 0x20); | |
272 | *(unsigned int *)gd->fb_base = 0x4000; | |
3d47b2d7 MP |
273 | /* point fb behind palette */ |
274 | gd->fb_base += 0x20; | |
3c5fabd1 | 275 | |
3b4e16eb | 276 | /* turn ON display through powercontrol function if accessible */ |
0d8a7d6f | 277 | if (panel->panel_power_ctrl != NULL) |
3b4e16eb HP |
278 | panel->panel_power_ctrl(1); |
279 | ||
280 | debug("am335x-fb: wait for stable power ...\n"); | |
281 | mdelay(panel->pup_delay); | |
41f76a01 DB |
282 | lcdhw->clkc_enable = LCDC_CLKC_ENABLE_CORECLKEN | |
283 | LCDC_CLKC_ENABLE_LIDDCLKEN | LCDC_CLKC_ENABLE_DMACLKEN; | |
3c5fabd1 | 284 | lcdhw->raster_ctrl = 0; |
a9df3c50 DB |
285 | |
286 | reg = lcdhw->ctrl & LCDC_CTRL_CLK_DIVISOR_MASK; | |
287 | reg |= LCDC_CTRL_RASTER_MODE; | |
288 | lcdhw->ctrl = reg; | |
289 | ||
3c5fabd1 | 290 | lcdhw->lcddma_fb0_base = gd->fb_base; |
3d47b2d7 | 291 | lcdhw->lcddma_fb0_ceiling = gd->fb_base + FBSIZE(panel); |
3c5fabd1 | 292 | lcdhw->lcddma_fb1_base = gd->fb_base; |
3d47b2d7 | 293 | lcdhw->lcddma_fb1_ceiling = gd->fb_base + FBSIZE(panel); |
41f76a01 DB |
294 | lcdhw->lcddma_ctrl = LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_16); |
295 | ||
296 | lcdhw->raster_timing0 = LCDC_RASTER_TIMING_0_HORLSB(panel->hactive) | | |
297 | LCDC_RASTER_TIMING_0_HORMSB(panel->hactive) | | |
298 | LCDC_RASTER_TIMING_0_HFPLSB(panel->hfp) | | |
299 | LCDC_RASTER_TIMING_0_HBPLSB(panel->hbp) | | |
300 | LCDC_RASTER_TIMING_0_HSWLSB(panel->hsw); | |
301 | lcdhw->raster_timing1 = LCDC_RASTER_TIMING_1_VBP(panel->vbp) | | |
302 | LCDC_RASTER_TIMING_1_VFP(panel->vfp) | | |
303 | LCDC_RASTER_TIMING_1_VSW(panel->vsw) | | |
304 | LCDC_RASTER_TIMING_1_VERLSB(panel->vactive); | |
305 | lcdhw->raster_timing2 = LCDC_RASTER_TIMING_2_HSWMSB(panel->hsw) | | |
306 | LCDC_RASTER_TIMING_2_VERMSB(panel->vactive) | | |
307 | LCDC_RASTER_TIMING_2_INVMASK(panel->pol) | | |
308 | LCDC_RASTER_TIMING_2_HBPMSB(panel->hbp) | | |
309 | LCDC_RASTER_TIMING_2_HFPMSB(panel->hfp) | | |
3c5fabd1 | 310 | 0x0000FF00; /* clk cycles for ac-bias */ |
7d045170 | 311 | lcdhw->raster_ctrl = raster_ctrl | |
41f76a01 DB |
312 | LCDC_RASTER_CTRL_PALMODE_RAWDATA | |
313 | LCDC_RASTER_CTRL_TFT_MODE | | |
314 | LCDC_RASTER_CTRL_ENABLE; | |
3c5fabd1 | 315 | |
3b4e16eb HP |
316 | debug("am335x-fb: waiting picture to be stable.\n."); |
317 | mdelay(panel->pon_delay); | |
3c5fabd1 HP |
318 | |
319 | return 0; | |
320 | } | |
96b109ba DB |
321 | |
322 | #else /* CONFIG_DM_VIDEO */ | |
323 | ||
324 | #define FBSIZE(t, p) (((t)->hactive.typ * (t)->vactive.typ * (p)->bpp) >> 3) | |
325 | ||
326 | enum { | |
327 | LCD_MAX_WIDTH = 2048, | |
328 | LCD_MAX_HEIGHT = 2048, | |
329 | LCD_MAX_LOG2_BPP = VIDEO_BPP32, | |
330 | }; | |
331 | ||
332 | /** | |
333 | * tilcdc_panel_info: Panel parameters | |
334 | * | |
335 | * @ac_bias: AC Bias Pin Frequency | |
336 | * @ac_bias_intrpt: AC Bias Pin Transitions per Interrupt | |
337 | * @dma_burst_sz: DMA burst size | |
338 | * @bpp: Bits per pixel | |
339 | * @fdd: FIFO DMA Request Delay | |
340 | * @tft_alt_mode: TFT Alternative Signal Mapping (Only for active) | |
341 | * @invert_pxl_clk: Invert pixel clock | |
342 | * @sync_edge: Horizontal and Vertical Sync Edge: 0=rising 1=falling | |
343 | * @sync_ctrl: Horizontal and Vertical Sync: Control: 0=ignore | |
344 | * @raster_order: Raster Data Order Select: 1=Most-to-least 0=Least-to-most | |
345 | * @fifo_th: DMA FIFO threshold | |
346 | */ | |
347 | struct tilcdc_panel_info { | |
348 | u32 ac_bias; | |
349 | u32 ac_bias_intrpt; | |
350 | u32 dma_burst_sz; | |
351 | u32 bpp; | |
352 | u32 fdd; | |
353 | bool tft_alt_mode; | |
354 | bool invert_pxl_clk; | |
355 | u32 sync_edge; | |
356 | u32 sync_ctrl; | |
357 | u32 raster_order; | |
358 | u32 fifo_th; | |
359 | }; | |
360 | ||
361 | struct am335x_fb_priv { | |
362 | struct am335x_lcdhw *regs; | |
363 | struct tilcdc_panel_info panel; | |
364 | struct display_timing timing; | |
365 | }; | |
366 | ||
367 | static int am335x_fb_remove(struct udevice *dev) | |
368 | { | |
369 | struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev); | |
370 | ||
371 | uc_plat->base -= 0x20; | |
372 | uc_plat->size += 0x20; | |
373 | return 0; | |
374 | } | |
375 | ||
376 | static int am335x_fb_probe(struct udevice *dev) | |
377 | { | |
378 | struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev); | |
379 | struct video_priv *uc_priv = dev_get_uclass_priv(dev); | |
380 | struct am335x_fb_priv *priv = dev_get_priv(dev); | |
381 | struct am335x_lcdhw *regs = priv->regs; | |
382 | struct tilcdc_panel_info *panel = &priv->panel; | |
383 | struct display_timing *timing = &priv->timing; | |
384 | struct cm_dpll *const cmdpll = (struct cm_dpll *)CM_DPLL; | |
385 | u32 reg; | |
386 | ||
387 | /* Before relocation we don't need to do anything */ | |
388 | if (!(gd->flags & GD_FLG_RELOC)) | |
389 | return 0; | |
390 | ||
391 | am335x_fb_set_pixel_clk_rate(regs, timing->pixelclock.typ); | |
392 | ||
393 | /* clock source for LCDC from dispPLL M2 */ | |
394 | writel(0, &cmdpll->clklcdcpixelclk); | |
395 | ||
396 | /* palette default entry */ | |
397 | memset((void *)uc_plat->base, 0, 0x20); | |
398 | *(unsigned int *)uc_plat->base = 0x4000; | |
399 | /* point fb behind palette */ | |
400 | uc_plat->base += 0x20; | |
401 | uc_plat->size -= 0x20; | |
402 | ||
403 | writel(LCDC_CLKC_ENABLE_CORECLKEN | LCDC_CLKC_ENABLE_LIDDCLKEN | | |
404 | LCDC_CLKC_ENABLE_DMACLKEN, ®s->clkc_enable); | |
405 | writel(0, ®s->raster_ctrl); | |
406 | ||
407 | reg = readl(®s->ctrl) & LCDC_CTRL_CLK_DIVISOR_MASK; | |
408 | reg |= LCDC_CTRL_RASTER_MODE; | |
409 | writel(reg, ®s->ctrl); | |
410 | ||
411 | writel(uc_plat->base, ®s->lcddma_fb0_base); | |
412 | writel(uc_plat->base + FBSIZE(timing, panel), | |
413 | ®s->lcddma_fb0_ceiling); | |
414 | writel(uc_plat->base, ®s->lcddma_fb1_base); | |
415 | writel(uc_plat->base + FBSIZE(timing, panel), | |
416 | ®s->lcddma_fb1_ceiling); | |
417 | ||
418 | reg = LCDC_DMA_CTRL_FIFO_TH(panel->fifo_th); | |
419 | switch (panel->dma_burst_sz) { | |
420 | case 1: | |
421 | reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_1); | |
422 | break; | |
423 | case 2: | |
424 | reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_2); | |
425 | break; | |
426 | case 4: | |
427 | reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_4); | |
428 | break; | |
429 | case 8: | |
430 | reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_8); | |
431 | break; | |
432 | case 16: | |
433 | reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_16); | |
434 | break; | |
435 | } | |
436 | ||
437 | writel(reg, ®s->lcddma_ctrl); | |
438 | ||
439 | writel(LCDC_RASTER_TIMING_0_HORLSB(timing->hactive.typ) | | |
440 | LCDC_RASTER_TIMING_0_HORMSB(timing->hactive.typ) | | |
441 | LCDC_RASTER_TIMING_0_HFPLSB(timing->hfront_porch.typ) | | |
442 | LCDC_RASTER_TIMING_0_HBPLSB(timing->hback_porch.typ) | | |
443 | LCDC_RASTER_TIMING_0_HSWLSB(timing->hsync_len.typ), | |
444 | ®s->raster_timing0); | |
445 | ||
446 | writel(LCDC_RASTER_TIMING_1_VBP(timing->vback_porch.typ) | | |
447 | LCDC_RASTER_TIMING_1_VFP(timing->vfront_porch.typ) | | |
448 | LCDC_RASTER_TIMING_1_VSW(timing->vsync_len.typ) | | |
449 | LCDC_RASTER_TIMING_1_VERLSB(timing->vactive.typ), | |
450 | ®s->raster_timing1); | |
451 | ||
452 | reg = LCDC_RASTER_TIMING_2_ACB(panel->ac_bias) | | |
453 | LCDC_RASTER_TIMING_2_ACBI(panel->ac_bias_intrpt) | | |
454 | LCDC_RASTER_TIMING_2_HSWMSB(timing->hsync_len.typ) | | |
455 | LCDC_RASTER_TIMING_2_VERMSB(timing->vactive.typ) | | |
456 | LCDC_RASTER_TIMING_2_HBPMSB(timing->hback_porch.typ) | | |
457 | LCDC_RASTER_TIMING_2_HFPMSB(timing->hfront_porch.typ); | |
458 | ||
459 | if (timing->flags & DISPLAY_FLAGS_VSYNC_LOW) | |
460 | reg |= LCDC_RASTER_TIMING_2_VSYNC_INVERT; | |
461 | ||
462 | if (timing->flags & DISPLAY_FLAGS_HSYNC_LOW) | |
463 | reg |= LCDC_RASTER_TIMING_2_HSYNC_INVERT; | |
464 | ||
465 | if (panel->invert_pxl_clk) | |
466 | reg |= LCDC_RASTER_TIMING_2_PXCLK_INVERT; | |
467 | ||
468 | if (panel->sync_edge) | |
469 | reg |= LCDC_RASTER_TIMING_2_HSVS_RISEFALL; | |
470 | ||
471 | if (panel->sync_ctrl) | |
472 | reg |= LCDC_RASTER_TIMING_2_HSVS_CONTROL; | |
473 | ||
474 | writel(reg, ®s->raster_timing2); | |
475 | ||
476 | reg = LCDC_RASTER_CTRL_PALMODE_RAWDATA | LCDC_RASTER_CTRL_TFT_MODE | | |
477 | LCDC_RASTER_CTRL_ENABLE | LCDC_RASTER_CTRL_REQDLY(panel->fdd); | |
478 | ||
479 | if (panel->tft_alt_mode) | |
480 | reg |= LCDC_RASTER_CTRL_TFT_ALT_ENABLE; | |
481 | ||
482 | if (panel->bpp == 24) | |
483 | reg |= LCDC_RASTER_CTRL_TFT_24BPP_MODE; | |
484 | else if (panel->bpp == 32) | |
485 | reg |= LCDC_RASTER_CTRL_TFT_24BPP_MODE | | |
486 | LCDC_RASTER_CTRL_TFT_24BPP_UNPACK; | |
487 | ||
488 | if (panel->raster_order) | |
489 | reg |= LCDC_RASTER_CTRL_DATA_ORDER; | |
490 | ||
491 | writel(reg, ®s->raster_ctrl); | |
492 | ||
493 | uc_priv->xsize = timing->hactive.typ; | |
494 | uc_priv->ysize = timing->vactive.typ; | |
495 | uc_priv->bpix = log_2_n_round_up(panel->bpp); | |
496 | return 0; | |
497 | } | |
498 | ||
499 | static int am335x_fb_ofdata_to_platdata(struct udevice *dev) | |
500 | { | |
501 | struct am335x_fb_priv *priv = dev_get_priv(dev); | |
502 | struct tilcdc_panel_info *panel = &priv->panel; | |
503 | struct display_timing *timing = &priv->timing; | |
504 | ofnode node; | |
505 | int err; | |
506 | ||
507 | node = ofnode_by_compatible(ofnode_null(), "ti,am33xx-tilcdc"); | |
508 | if (!ofnode_valid(node)) { | |
509 | dev_err(dev, "missing 'ti,am33xx-tilcdc' node\n"); | |
510 | return -ENXIO; | |
511 | } | |
512 | ||
513 | priv->regs = (struct am335x_lcdhw *)ofnode_get_addr(node); | |
514 | dev_dbg(dev, "LCD: base address=0x%x\n", (unsigned int)priv->regs); | |
515 | ||
516 | err = ofnode_decode_display_timing(dev_ofnode(dev), 0, timing); | |
517 | if (err) { | |
518 | dev_err(dev, "failed to get display timing\n"); | |
519 | return err; | |
520 | } | |
521 | ||
522 | if (timing->pixelclock.typ > (LCDC_FMAX / 2)) { | |
523 | dev_err(dev, "invalid display clock-frequency: %d Hz\n", | |
524 | timing->pixelclock.typ); | |
525 | return -EINVAL; | |
526 | } | |
527 | ||
528 | if (timing->hactive.typ > LCD_MAX_WIDTH) | |
529 | timing->hactive.typ = LCD_MAX_WIDTH; | |
530 | ||
531 | if (timing->vactive.typ > LCD_MAX_HEIGHT) | |
532 | timing->vactive.typ = LCD_MAX_HEIGHT; | |
533 | ||
534 | node = ofnode_find_subnode(dev_ofnode(dev), "panel-info"); | |
535 | if (!ofnode_valid(node)) { | |
536 | dev_err(dev, "missing 'panel-info' node\n"); | |
537 | return -ENXIO; | |
538 | } | |
539 | ||
540 | err |= ofnode_read_u32(node, "ac-bias", &panel->ac_bias); | |
541 | err |= ofnode_read_u32(node, "ac-bias-intrpt", &panel->ac_bias_intrpt); | |
542 | err |= ofnode_read_u32(node, "dma-burst-sz", &panel->dma_burst_sz); | |
543 | err |= ofnode_read_u32(node, "bpp", &panel->bpp); | |
544 | err |= ofnode_read_u32(node, "fdd", &panel->fdd); | |
545 | err |= ofnode_read_u32(node, "sync-edge", &panel->sync_edge); | |
546 | err |= ofnode_read_u32(node, "sync-ctrl", &panel->sync_ctrl); | |
547 | err |= ofnode_read_u32(node, "raster-order", &panel->raster_order); | |
548 | err |= ofnode_read_u32(node, "fifo-th", &panel->fifo_th); | |
549 | if (err) { | |
550 | dev_err(dev, "failed to get panel info\n"); | |
551 | return err; | |
552 | } | |
553 | ||
554 | switch (panel->bpp) { | |
555 | case 16: | |
556 | case 24: | |
557 | case 32: | |
558 | break; | |
559 | default: | |
560 | dev_err(dev, "invalid seting, bpp: %d\n", panel->bpp); | |
561 | return -EINVAL; | |
562 | } | |
563 | ||
564 | switch (panel->dma_burst_sz) { | |
565 | case 1: | |
566 | case 2: | |
567 | case 4: | |
568 | case 8: | |
569 | case 16: | |
570 | break; | |
571 | default: | |
572 | dev_err(dev, "invalid setting, dma-burst-sz: %d\n", | |
573 | panel->dma_burst_sz); | |
574 | return -EINVAL; | |
575 | } | |
576 | ||
577 | /* optional */ | |
578 | panel->tft_alt_mode = ofnode_read_bool(node, "tft-alt-mode"); | |
579 | panel->invert_pxl_clk = ofnode_read_bool(node, "invert-pxl-clk"); | |
580 | ||
581 | dev_dbg(dev, "LCD: %dx%d, bpp=%d, clk=%d Hz\n", timing->hactive.typ, | |
582 | timing->vactive.typ, panel->bpp, timing->pixelclock.typ); | |
583 | dev_dbg(dev, " hbp=%d, hfp=%d, hsw=%d\n", timing->hback_porch.typ, | |
584 | timing->hfront_porch.typ, timing->hsync_len.typ); | |
585 | dev_dbg(dev, " vbp=%d, vfp=%d, vsw=%d\n", timing->vback_porch.typ, | |
586 | timing->vfront_porch.typ, timing->vsync_len.typ); | |
587 | ||
588 | return 0; | |
589 | } | |
590 | ||
591 | static int am335x_fb_bind(struct udevice *dev) | |
592 | { | |
593 | struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev); | |
594 | ||
595 | uc_plat->size = ((LCD_MAX_WIDTH * LCD_MAX_HEIGHT * | |
596 | (1 << LCD_MAX_LOG2_BPP)) >> 3) + 0x20; | |
597 | ||
598 | dev_dbg(dev, "frame buffer size 0x%x\n", uc_plat->size); | |
599 | return 0; | |
600 | } | |
601 | ||
602 | static const struct udevice_id am335x_fb_ids[] = { | |
603 | { .compatible = "ti,tilcdc,panel" }, | |
604 | { } | |
605 | }; | |
606 | ||
607 | U_BOOT_DRIVER(am335x_fb) = { | |
608 | .name = "am335x_fb", | |
609 | .id = UCLASS_VIDEO, | |
610 | .of_match = am335x_fb_ids, | |
611 | .bind = am335x_fb_bind, | |
612 | .ofdata_to_platdata = am335x_fb_ofdata_to_platdata, | |
613 | .probe = am335x_fb_probe, | |
614 | .remove = am335x_fb_remove, | |
615 | .priv_auto_alloc_size = sizeof(struct am335x_fb_priv), | |
616 | }; | |
617 | ||
618 | #endif /* CONFIG_DM_VIDEO */ |