summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c')
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c512
1 files changed, 512 insertions, 0 deletions
diff --git a/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c b/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
new file mode 100644
index 0000000000..277c7120e1
--- /dev/null
+++ b/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
@@ -0,0 +1,512 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (c) 2011 by Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <sys/types.h> /* off_t */
22#include <string.h>
23#include "cpu.h"
24#include "system.h"
25#include "backlight-target.h"
26#include "lcd.h"
27#include "lcdif-imx233.h"
28#include "clkctrl-imx233.h"
29#include "pinctrl-imx233.h"
30#include "logf.h"
31
32#ifdef HAVE_LCD_ENABLE
33static bool lcd_on;
34#endif
35static unsigned lcd_yuv_options = 0;
36
37static void setup_parameters(void)
38{
39 imx233_lcdif_reset();
40 imx233_lcdif_set_lcd_databus_width(HW_LCDIF_CTRL__LCD_DATABUS_WIDTH_18_BIT);
41 imx233_lcdif_set_word_length(HW_LCDIF_CTRL__WORD_LENGTH_18_BIT);
42 imx233_lcdif_set_timings(2, 2, 3, 3);
43}
44
45static void setup_lcd_pins(bool use_lcdif)
46{
47 if(use_lcdif)
48 {
49 imx233_set_pin_function(1, 25, PINCTRL_FUNCTION_MAIN); /* lcd_vsync */
50 imx233_set_pin_function(1, 21, PINCTRL_FUNCTION_MAIN); /* lcd_cs */
51 imx233_set_pin_function(1, 22, PINCTRL_FUNCTION_MAIN); /* lcd_dotclk */
52 imx233_set_pin_function(1, 23, PINCTRL_FUNCTION_MAIN); /* lcd_enable */
53 imx233_set_pin_function(1, 24, PINCTRL_FUNCTION_MAIN); /* lcd_hsync */
54 imx233_set_pin_function(1, 18, PINCTRL_FUNCTION_MAIN); /* lcd_reset */
55 imx233_set_pin_function(1, 19, PINCTRL_FUNCTION_MAIN); /* lcd_rs */
56 imx233_set_pin_function(1, 16, PINCTRL_FUNCTION_MAIN); /* lcd_d16 */
57 imx233_set_pin_function(1, 17, PINCTRL_FUNCTION_MAIN); /* lcd_d17 */
58 imx233_set_pin_function(1, 20, PINCTRL_FUNCTION_MAIN); /* lcd_wr */
59 __REG_CLR(HW_PINCTRL_MUXSEL(2)) = 0xffffffff; /* lcd_d{0-15} */
60 }
61 else
62 {
63 __REG_SET(HW_PINCTRL_MUXSEL(2)) = 0xffffffff; /* lcd_d{0-15} */
64 imx233_enable_gpio_output_mask(1, 0x3ffffff, false); /* lcd_{d{0-17},reset,rs,wr,cs,dotclk,enable,hsync,vsync} */
65 imx233_set_pin_function(1, 16, PINCTRL_FUNCTION_GPIO); /* lcd_d16 */
66 imx233_set_pin_function(1, 17, PINCTRL_FUNCTION_GPIO); /* lcd_d17 */
67 imx233_set_pin_function(1, 19, PINCTRL_FUNCTION_GPIO); /* lcd_rs */
68 imx233_set_pin_function(1, 20, PINCTRL_FUNCTION_GPIO); /* lcd_wr */
69 imx233_set_pin_function(1, 21, PINCTRL_FUNCTION_GPIO); /* lcd_cs */
70 imx233_set_pin_function(1, 22, PINCTRL_FUNCTION_GPIO); /* lcd_dotclk */
71 imx233_set_pin_function(1, 23, PINCTRL_FUNCTION_GPIO); /* lcd_enable */
72 imx233_set_pin_function(1, 24, PINCTRL_FUNCTION_GPIO); /* lcd_hsync */
73 imx233_set_pin_function(1, 25, PINCTRL_FUNCTION_GPIO); /* lcd_vsync */
74 }
75}
76
77static void common_lcd_enable(bool enable)
78{
79 imx233_lcdif_enable(enable);
80 setup_lcd_pins(enable); /* use GPIO pins when disable */
81}
82
83static void setup_lcdif(void)
84{
85 setup_parameters();
86 common_lcd_enable(true);
87 imx233_lcdif_enable_bus_master(true);
88}
89
90static inline uint32_t encode_16_to_18(uint32_t a)
91{
92 return ((a & 0xff) << 1) | (((a >> 8) & 0xff) << 10);
93}
94
95static inline uint32_t decode_18_to_16(uint32_t a)
96{
97 return ((a >> 1) & 0xff) | ((a >> 2) & 0xff00);
98}
99
100static void setup_lcdif_clock(void)
101{
102 /* the LCD seems to work at 24Mhz, so use the xtal clock with no divider */
103 imx233_clkctrl_enable_clock(CLK_PIX, false);
104 imx233_clkctrl_set_clock_divisor(CLK_PIX, 1);
105 imx233_clkctrl_set_bypass_pll(CLK_PIX, true); /* use XTAL */
106 imx233_clkctrl_enable_clock(CLK_PIX, true);
107}
108
109static void lcd_write_reg(uint32_t reg, uint32_t data)
110{
111 uint32_t old_reg = reg;
112 /* get back to 18-bit word length */
113 imx233_lcdif_set_word_length(HW_LCDIF_CTRL__WORD_LENGTH_18_BIT);
114 reg = encode_16_to_18(reg);
115 data = encode_16_to_18(data);
116
117 imx233_lcdif_pio_send(false, 2, &reg);
118 if(old_reg != 0 && old_reg != 0x202)
119 imx233_lcdif_pio_send(true, 2, &data);
120}
121
122#define REG_MDELAY 0xffffffff
123struct lcd_sequence_entry_t
124{
125 uint32_t reg, data;
126};
127
128static void lcd_send_sequence(struct lcd_sequence_entry_t *seq, unsigned count)
129{
130 for(;count-- > 0; seq++)
131 {
132 if(seq->reg == REG_MDELAY)
133 mdelay(seq->data);
134 else
135 lcd_write_reg(seq->reg, seq->data);
136 }
137}
138
139#define _begin_seq() static struct lcd_sequence_entry_t __seq[] = {
140#define _mdelay(a) {REG_MDELAY, a},
141#define _lcd_write_reg(a, b) {a, b},
142#define _end_seq() }; lcd_send_sequence(__seq, sizeof(__seq) / sizeof(__seq[0]));
143
144static void lcd_init_seq(void)
145{
146 _begin_seq()
147 _lcd_write_reg(0, 0)
148 _lcd_write_reg(0, 0)
149 _lcd_write_reg(0, 0)
150 _lcd_write_reg(0, 0)
151 _mdelay(100)
152 _lcd_write_reg(8, 0x808)
153 _lcd_write_reg(0x10, 0x10)
154 _lcd_write_reg(0x400, 0x6200)
155 _lcd_write_reg(0x300, 0xc0a)
156 _lcd_write_reg(0x301, 0x4c11)
157 _lcd_write_reg(0x302, 0x906)
158 _lcd_write_reg(0x303, 0x1417)
159 _lcd_write_reg(0x304, 0x3333)
160 _lcd_write_reg(0x305, 0x150d)
161 _lcd_write_reg(0x306, 0x740a)
162 _lcd_write_reg(0x307, 0x100c)
163 _lcd_write_reg(0x308, 0x60c)
164 _lcd_write_reg(0x309, 0)
165 _lcd_write_reg(0x100, 0x730)
166 _lcd_write_reg(0x101, 0x237)
167 _lcd_write_reg(0x103, 0x2b00)
168 _lcd_write_reg(0x280, 0x4000)
169 _lcd_write_reg(0x102, 0x81b0)
170 _mdelay(400)
171 _lcd_write_reg(1, 0x100)
172 _lcd_write_reg(2, 0x100)
173 _lcd_write_reg(3, 0x5028)
174 _lcd_write_reg(9, 1)
175 _lcd_write_reg(0xc, 0)
176 _lcd_write_reg(0x11, 0x202)
177 _lcd_write_reg(0x12, 0x101)
178 _lcd_write_reg(0x13, 1)
179 _lcd_write_reg(0x90, 0x8000)
180 _lcd_write_reg(0x210, 0)
181 _lcd_write_reg(0x211, 0xef)
182 _lcd_write_reg(0x212, 0)
183 _lcd_write_reg(0x213, 0x18f)
184 _lcd_write_reg(0x200, 0)
185 _lcd_write_reg(0x201, 0)
186 _lcd_write_reg(0x401, 1)
187 _lcd_write_reg(0x404, 0)
188 _mdelay(400)
189 _lcd_write_reg(7, 0x100)
190 _mdelay(400)
191 _lcd_write_reg(3, 0x1030)
192 _end_seq()
193}
194
195void lcd_init_device(void)
196{
197 setup_lcdif();
198 setup_lcdif_clock();
199
200 // reset device
201 __REG_SET(HW_LCDIF_CTRL1) = HW_LCDIF_CTRL1__RESET;
202 mdelay(50);
203 __REG_CLR(HW_LCDIF_CTRL1) = HW_LCDIF_CTRL1__RESET;
204 mdelay(10);
205 __REG_SET(HW_LCDIF_CTRL1) = HW_LCDIF_CTRL1__RESET;
206
207 lcd_init_seq();
208#ifdef HAVE_LCD_ENABLE
209 lcd_on = true;
210#endif
211}
212
213#ifdef HAVE_LCD_ENABLE
214bool lcd_active(void)
215{
216 return lcd_on;
217}
218
219static void lcd_enable_seq(bool enable)
220{
221 if(!enable)
222 {
223 _begin_seq()
224 _end_seq()
225 }
226 else
227 {
228 _begin_seq()
229 _end_seq()
230 }
231}
232
233void lcd_enable(bool enable)
234{
235 if(lcd_on == enable)
236 return;
237
238 lcd_on = enable;
239
240 if(enable)
241 common_lcd_enable(true);
242 lcd_enable_seq(enable);
243 if(!enable)
244 common_lcd_enable(false);
245}
246#endif
247
248void lcd_update(void)
249{
250 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
251}
252
253void lcd_update_rect(int x, int y, int w, int h)
254{
255#ifdef HAVE_LCD_ENABLE
256 if(!lcd_on)
257 return;
258#endif
259 /* make sure the rectangle is included in the screen */
260 x = MIN(x, LCD_WIDTH);
261 y = MIN(y, LCD_HEIGHT);
262 w = MIN(w, LCD_WIDTH - x);
263 h = MIN(h, LCD_HEIGHT - y);
264
265 imx233_lcdif_wait_ready();
266 lcd_write_reg(0x210, x);
267 lcd_write_reg(0x211, x + w - 1);
268 lcd_write_reg(0x212, y);
269 lcd_write_reg(0x213, y + h - 1);
270 lcd_write_reg(0x200, 0);
271 lcd_write_reg(0x201, 0);
272 lcd_write_reg(0x202, 0);
273 imx233_lcdif_wait_ready();
274 imx233_lcdif_set_word_length(HW_LCDIF_CTRL__WORD_LENGTH_16_BIT);
275 imx233_lcdif_set_byte_packing_format(0xf); /* two pixels per 32-bit word */
276 imx233_lcdif_set_data_format(false, false, false); /* RGB565, don't care, don't care */
277
278 /* there are two cases here:
279 * - either width = LCD_WIDTH and we can directly memcopy a part of lcd_framebuffer to FRAME
280 * and send it
281 * - either width != LCD_WIDTH and we have to build a contiguous copy of the rectangular area
282 * into FRAME before sending it (which is slower and doesn't use the hardware)
283 * In all cases, FRAME just acts as a temporary buffer.
284 * NOTE It's more interesting to do a copy to FRAME in all cases since in system mode
285 * the clock runs at 24MHz which provides barely 10MB/s bandwidth compared to >100MB/s
286 * for memcopy operations
287 */
288 if(w == LCD_WIDTH)
289 {
290 memcpy((void *)FRAME, FBADDR(x,y), w * h * sizeof(fb_data));
291 }
292 else
293 {
294 for(int i = 0; i < h; i++)
295 memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
296 }
297 /* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
298 * (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
299 * field must be a multiple of 2. Furthermore, it seems the lcd controller doesn't
300 * really like when both w and h are even, probably because the writes to the GRAM
301 * are done on several words and the controller requires dummy writes.
302 * The workaround is to always make sure that we send a number of pixels which is
303 * a multiple of 4 so that both the lcdif and the controller are happy. If any
304 * of w or h is odd, we will send a copy of the first pixels as dummy writes. We will
305 * send at most 3 bytes. We then send (w * h + 3) / 4 x 4 bytes.
306 */
307 if(w % 2 == 1 || h % 2 == 1)
308 {
309 /* copy three pixel after the last one */
310 for(int i = 0; i < 3; i++)
311 *((fb_data *)FRAME + w * h + i) = *((fb_data *)FRAME + i);
312 /* WARNING we need to update w and h to reflect the pixel count BUT it
313 * has no relation to w * h (it can even be 2 * prime). Hopefully, w <= 240 and
314 * h <= 320 so w * h <= 76800 and (w * h + 3) / 4 <= 38400 which fits into
315 * a 16-bit integer (horizontal count). */
316 h = (w * h + 3) / 4;
317 w = 4;
318 }
319 imx233_lcdif_dma_send((void *)FRAME_PHYS_ADDR, w, h);
320}
321
322void lcd_yuv_set_options(unsigned options)
323{
324 lcd_yuv_options = options;
325}
326
327#define YFAC (74)
328#define RVFAC (101)
329#define GUFAC (-24)
330#define GVFAC (-51)
331#define BUFAC (128)
332
333static inline int clamp(int val, int min, int max)
334{
335 if (val < min)
336 val = min;
337 else if (val > max)
338 val = max;
339 return val;
340}
341
342void lcd_blit_yuv(unsigned char * const src[3],
343 int src_x, int src_y, int stride,
344 int x, int y, int width, int height)
345{
346 const unsigned char *ysrc, *usrc, *vsrc;
347 int linecounter;
348 fb_data *dst, *row_end;
349 long z;
350
351 /* width and height must be >= 2 and an even number */
352 width &= ~1;
353 linecounter = height >> 1;
354
355 #if LCD_WIDTH >= LCD_HEIGHT
356 dst = FBADDR(x,y);
357 row_end = dst + width;
358 #else
359 dst = FBADDR(LCD_WIDTH - y - 1,x);
360 row_end = dst + LCD_WIDTH * width;
361 #endif
362
363 z = stride * src_y;
364 ysrc = src[0] + z + src_x;
365 usrc = src[1] + (z >> 2) + (src_x >> 1);
366 vsrc = src[2] + (usrc - src[1]);
367
368 /* stride => amount to jump from end of last row to start of next */
369 stride -= width;
370
371 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
372
373 do
374 {
375 do
376 {
377 int y, cb, cr, rv, guv, bu, r, g, b;
378
379 y = YFAC*(*ysrc++ - 16);
380 cb = *usrc++ - 128;
381 cr = *vsrc++ - 128;
382
383 rv = RVFAC*cr;
384 guv = GUFAC*cb + GVFAC*cr;
385 bu = BUFAC*cb;
386
387 r = y + rv;
388 g = y + guv;
389 b = y + bu;
390
391 if ((unsigned)(r | g | b) > 64*256-1)
392 {
393 r = clamp(r, 0, 64*256-1);
394 g = clamp(g, 0, 64*256-1);
395 b = clamp(b, 0, 64*256-1);
396 }
397
398 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
399
400 #if LCD_WIDTH >= LCD_HEIGHT
401 dst++;
402 #else
403 dst += LCD_WIDTH;
404 #endif
405
406 y = YFAC*(*ysrc++ - 16);
407 r = y + rv;
408 g = y + guv;
409 b = y + bu;
410
411 if ((unsigned)(r | g | b) > 64*256-1)
412 {
413 r = clamp(r, 0, 64*256-1);
414 g = clamp(g, 0, 64*256-1);
415 b = clamp(b, 0, 64*256-1);
416 }
417
418 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
419
420 #if LCD_WIDTH >= LCD_HEIGHT
421 dst++;
422 #else
423 dst += LCD_WIDTH;
424 #endif
425 }
426 while (dst < row_end);
427
428 ysrc += stride;
429 usrc -= width >> 1;
430 vsrc -= width >> 1;
431
432 #if LCD_WIDTH >= LCD_HEIGHT
433 row_end += LCD_WIDTH;
434 dst += LCD_WIDTH - width;
435 #else
436 row_end -= 1;
437 dst -= LCD_WIDTH*width + 1;
438 #endif
439
440 do
441 {
442 int y, cb, cr, rv, guv, bu, r, g, b;
443
444 y = YFAC*(*ysrc++ - 16);
445 cb = *usrc++ - 128;
446 cr = *vsrc++ - 128;
447
448 rv = RVFAC*cr;
449 guv = GUFAC*cb + GVFAC*cr;
450 bu = BUFAC*cb;
451
452 r = y + rv;
453 g = y + guv;
454 b = y + bu;
455
456 if ((unsigned)(r | g | b) > 64*256-1)
457 {
458 r = clamp(r, 0, 64*256-1);
459 g = clamp(g, 0, 64*256-1);
460 b = clamp(b, 0, 64*256-1);
461 }
462
463 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
464
465 #if LCD_WIDTH >= LCD_HEIGHT
466 dst++;
467 #else
468 dst += LCD_WIDTH;
469 #endif
470
471 y = YFAC*(*ysrc++ - 16);
472 r = y + rv;
473 g = y + guv;
474 b = y + bu;
475
476 if ((unsigned)(r | g | b) > 64*256-1)
477 {
478 r = clamp(r, 0, 64*256-1);
479 g = clamp(g, 0, 64*256-1);
480 b = clamp(b, 0, 64*256-1);
481 }
482
483 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
484
485 #if LCD_WIDTH >= LCD_HEIGHT
486 dst++;
487 #else
488 dst += LCD_WIDTH;
489 #endif
490 }
491 while (dst < row_end);
492
493 ysrc += stride;
494 usrc += stride >> 1;
495 vsrc += stride >> 1;
496
497 #if LCD_WIDTH >= LCD_HEIGHT
498 row_end += LCD_WIDTH;
499 dst += LCD_WIDTH - width;
500 #else
501 row_end -= 1;
502 dst -= LCD_WIDTH*width + 1;
503 #endif
504 }
505 while (--linecounter > 0);
506
507 #if LCD_WIDTH >= LCD_HEIGHT
508 lcd_update_rect(x, y, width, height);
509 #else
510 lcd_update_rect(LCD_WIDTH - y - height, x, height, width);
511 #endif
512}