summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2012-05-19 16:09:46 +0200
committerAmaury Pouly <amaury.pouly@gmail.com>2012-05-19 16:10:52 +0200
commit12c64a4b79e043d8e8d77a278b340310f867a588 (patch)
treeb34b56131d6eb0e06340a8a6d7e32843027d0f59 /firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
parente09c1e3d43665d04ae0ce2f288ba695b5abc3622 (diff)
downloadrockbox-12c64a4b79e043d8e8d77a278b340310f867a588.tar.gz
rockbox-12c64a4b79e043d8e8d77a278b340310f867a588.zip
Initial commit for the Creative ZEN X-Fi2 and X-Fi3 ports
These are really similar devices so one commit for both is ok. Change-Id: I8bd1d3fef1eb6d00aaadfb7af56c771f62d0c9c3
Diffstat (limited to 'firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c')
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c490
1 files changed, 490 insertions, 0 deletions
diff --git a/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c b/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
new file mode 100644
index 0000000000..3579f8306e
--- /dev/null
+++ b/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
@@ -0,0 +1,490 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (c) 2012 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_16_BIT);
41 imx233_lcdif_set_word_length(HW_LCDIF_CTRL__WORD_LENGTH_16_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 void setup_lcdif_clock(void)
91{
92 /* the LCD seems to work at 24Mhz, so use the xtal clock with no divider */
93 imx233_clkctrl_enable_clock(CLK_PIX, false);
94 imx233_clkctrl_set_clock_divisor(CLK_PIX, 1);
95 imx233_clkctrl_set_bypass_pll(CLK_PIX, true); /* use XTAL */
96 imx233_clkctrl_enable_clock(CLK_PIX, true);
97}
98
99static void lcd_write_reg(uint32_t reg, uint32_t data)
100{
101 imx233_lcdif_pio_send(false, 2, &reg);
102 if(reg != 0x22)
103 imx233_lcdif_pio_send(true, 2, &data);
104}
105
106#define REG_UDELAY 0xffffffff
107struct lcd_sequence_entry_t
108{
109 uint32_t reg, data;
110};
111
112static void lcd_send_sequence(struct lcd_sequence_entry_t *seq, unsigned count)
113{
114 for(;count-- > 0; seq++)
115 {
116 if(seq->reg == REG_UDELAY)
117 udelay(seq->data);
118 else
119 lcd_write_reg(seq->reg, seq->data);
120 }
121}
122
123#define _begin_seq() static struct lcd_sequence_entry_t __seq[] = {
124#define _udelay(a) {REG_UDELAY, a},
125#define _lcd_write_reg(a, b) {a, b},
126#define _end_seq() }; lcd_send_sequence(__seq, sizeof(__seq) / sizeof(__seq[0]));
127
128static void lcd_init_seq(void)
129{
130 _begin_seq()
131 _lcd_write_reg(1, 0x11c)
132 _lcd_write_reg(2, 0x100)
133 _lcd_write_reg(3, 0x1030)
134 _lcd_write_reg(8, 0x808)
135 _lcd_write_reg(0xc, 0)
136 _lcd_write_reg(0xf, 0xc01)
137 _lcd_write_reg(0x20, 0)
138 _lcd_write_reg(0x21, 0)
139 _udelay(30)
140 _lcd_write_reg(0x10, 0xa00)
141 _lcd_write_reg(0x11, 0x1038)
142 _udelay(30)
143 _lcd_write_reg(0x12, 0x1010)
144 _lcd_write_reg(0x13, 0x50)
145 _lcd_write_reg(0x14, 0x4f58)
146 _lcd_write_reg(0x30, 0)
147 _lcd_write_reg(0x31, 0xdb)
148 _lcd_write_reg(0x32, 0)
149 _lcd_write_reg(0x33, 0)
150 _lcd_write_reg(0x34, 0xdb)
151 _lcd_write_reg(0x35, 0)
152 _lcd_write_reg(0x36, 0xaf)
153 _lcd_write_reg(0x37, 0)
154 _lcd_write_reg(0x38, 0xdb)
155 _lcd_write_reg(0x39, 0)
156 _lcd_write_reg(0x50, 0)
157 _lcd_write_reg(0x51, 0x705)
158 _lcd_write_reg(0x52, 0xe0a)
159 _lcd_write_reg(0x53, 0x300)
160 _lcd_write_reg(0x54, 0xa0e)
161 _lcd_write_reg(0x55, 0x507)
162 _lcd_write_reg(0x56, 0)
163 _lcd_write_reg(0x57, 3)
164 _lcd_write_reg(0x58, 0x90a)
165 _lcd_write_reg(0x59, 0xa09)
166 _udelay(30)
167 _lcd_write_reg(7, 0x1017)
168 _udelay(40)
169 _end_seq()
170}
171
172void lcd_init_device(void)
173{
174 setup_lcdif();
175 setup_lcdif_clock();
176
177 // reset device
178 __REG_SET(HW_LCDIF_CTRL1) = HW_LCDIF_CTRL1__RESET;
179 mdelay(50);
180 __REG_CLR(HW_LCDIF_CTRL1) = HW_LCDIF_CTRL1__RESET;
181 mdelay(10);
182 __REG_SET(HW_LCDIF_CTRL1) = HW_LCDIF_CTRL1__RESET;
183
184 lcd_init_seq();
185#ifdef HAVE_LCD_ENABLE
186 lcd_on = true;
187#endif
188}
189
190#ifdef HAVE_LCD_ENABLE
191bool lcd_active(void)
192{
193 return lcd_on;
194}
195
196static void lcd_enable_seq(bool enable)
197{
198 if(!enable)
199 {
200 _begin_seq()
201 _end_seq()
202 }
203 else
204 {
205 _begin_seq()
206 _end_seq()
207 }
208}
209
210void lcd_enable(bool enable)
211{
212 if(lcd_on == enable)
213 return;
214
215 lcd_on = enable;
216
217 if(enable)
218 common_lcd_enable(true);
219 lcd_enable_seq(enable);
220 if(!enable)
221 common_lcd_enable(false);
222}
223#endif
224
225void lcd_update(void)
226{
227 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
228}
229
230void lcd_update_rect(int x, int y, int w, int h)
231{
232#ifdef HAVE_LCD_ENABLE
233 if(!lcd_on)
234 return;
235#endif
236 /* make sure the rectangle is included in the screen */
237 x = MIN(x, LCD_WIDTH);
238 y = MIN(y, LCD_HEIGHT);
239 w = MIN(w, LCD_WIDTH - x);
240 h = MIN(h, LCD_HEIGHT - y);
241
242 imx233_lcdif_wait_ready();
243 lcd_write_reg(0x37, x);
244 lcd_write_reg(0x36, x + w - 1);
245 lcd_write_reg(0x39, y);
246 lcd_write_reg(0x38, y + h - 1);
247 lcd_write_reg(0x20, x);
248 lcd_write_reg(0x21, y);
249 lcd_write_reg(0x22, 0);
250
251 imx233_lcdif_wait_ready();
252 imx233_lcdif_set_word_length(HW_LCDIF_CTRL__WORD_LENGTH_16_BIT);
253 imx233_lcdif_set_byte_packing_format(0xf); /* two pixels per 32-bit word */
254 imx233_lcdif_set_data_format(false, false, false); /* RGB565, don't care, don't care */
255
256 /* there are two cases here:
257 * - either width = LCD_WIDTH and we can directly memcopy a part of lcd_framebuffer to FRAME
258 * and send it
259 * - either width != LCD_WIDTH and we have to build a contiguous copy of the rectangular area
260 * into FRAME before sending it (which is slower and doesn't use the hardware)
261 * In all cases, FRAME just acts as a temporary buffer.
262 * NOTE It's more interesting to do a copy to FRAME in all cases since in system mode
263 * the clock runs at 24MHz which provides barely 10MB/s bandwidth compared to >100MB/s
264 * for memcopy operations
265 */
266 if(w == LCD_WIDTH)
267 {
268 memcpy((void *)FRAME, FBADDR(x,y), w * h * sizeof(fb_data));
269 }
270 else
271 {
272 for(int i = 0; i < h; i++)
273 memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
274 }
275 /* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
276 * (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
277 * field must be a multiple of 2. Furthermore, it seems the lcd controller doesn't
278 * really like when both w and h are even, probably because the writes to the GRAM
279 * are done on several words and the controller requires dummy writes.
280 * The workaround is to always make sure that we send a number of pixels which is
281 * a multiple of 4 so that both the lcdif and the controller are happy. If any
282 * of w or h is odd, we will send a copy of the first pixels as dummy writes. We will
283 * send at most 3 bytes. We then send (w * h + 3) / 4 x 4 bytes.
284 */
285 if(w % 2 == 1 || h % 2 == 1)
286 {
287 /* copy three pixel after the last one */
288 for(int i = 0; i < 3; i++)
289 *((fb_data *)FRAME + w * h + i) = *((fb_data *)FRAME + i);
290 /* WARNING we need to update w and h to reflect the pixel count BUT it
291 * has no relation to w * h (it can even be 2 * prime). Hopefully, w <= 240 and
292 * h <= 320 so w * h <= 76800 and (w * h + 3) / 4 <= 38400 which fits into
293 * a 16-bit integer (horizontal count). */
294 h = (w * h + 3) / 4;
295 w = 4;
296 }
297 imx233_lcdif_dma_send((void *)FRAME_PHYS_ADDR, w, h);
298}
299
300void lcd_yuv_set_options(unsigned options)
301{
302 lcd_yuv_options = options;
303}
304
305#define YFAC (74)
306#define RVFAC (101)
307#define GUFAC (-24)
308#define GVFAC (-51)
309#define BUFAC (128)
310
311static inline int clamp(int val, int min, int max)
312{
313 if (val < min)
314 val = min;
315 else if (val > max)
316 val = max;
317 return val;
318}
319
320void lcd_blit_yuv(unsigned char * const src[3],
321 int src_x, int src_y, int stride,
322 int x, int y, int width, int height)
323{
324 const unsigned char *ysrc, *usrc, *vsrc;
325 int linecounter;
326 fb_data *dst, *row_end;
327 long z;
328
329 /* width and height must be >= 2 and an even number */
330 width &= ~1;
331 linecounter = height >> 1;
332
333 #if LCD_WIDTH >= LCD_HEIGHT
334 dst = FBADDR(x,y);
335 row_end = dst + width;
336 #else
337 dst = FBADDR(LCD_WIDTH - y - 1,x);
338 row_end = dst + LCD_WIDTH * width;
339 #endif
340
341 z = stride * src_y;
342 ysrc = src[0] + z + src_x;
343 usrc = src[1] + (z >> 2) + (src_x >> 1);
344 vsrc = src[2] + (usrc - src[1]);
345
346 /* stride => amount to jump from end of last row to start of next */
347 stride -= width;
348
349 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
350
351 do
352 {
353 do
354 {
355 int y, cb, cr, rv, guv, bu, r, g, b;
356
357 y = YFAC*(*ysrc++ - 16);
358 cb = *usrc++ - 128;
359 cr = *vsrc++ - 128;
360
361 rv = RVFAC*cr;
362 guv = GUFAC*cb + GVFAC*cr;
363 bu = BUFAC*cb;
364
365 r = y + rv;
366 g = y + guv;
367 b = y + bu;
368
369 if ((unsigned)(r | g | b) > 64*256-1)
370 {
371 r = clamp(r, 0, 64*256-1);
372 g = clamp(g, 0, 64*256-1);
373 b = clamp(b, 0, 64*256-1);
374 }
375
376 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
377
378 #if LCD_WIDTH >= LCD_HEIGHT
379 dst++;
380 #else
381 dst += LCD_WIDTH;
382 #endif
383
384 y = YFAC*(*ysrc++ - 16);
385 r = y + rv;
386 g = y + guv;
387 b = y + bu;
388
389 if ((unsigned)(r | g | b) > 64*256-1)
390 {
391 r = clamp(r, 0, 64*256-1);
392 g = clamp(g, 0, 64*256-1);
393 b = clamp(b, 0, 64*256-1);
394 }
395
396 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
397
398 #if LCD_WIDTH >= LCD_HEIGHT
399 dst++;
400 #else
401 dst += LCD_WIDTH;
402 #endif
403 }
404 while (dst < row_end);
405
406 ysrc += stride;
407 usrc -= width >> 1;
408 vsrc -= width >> 1;
409
410 #if LCD_WIDTH >= LCD_HEIGHT
411 row_end += LCD_WIDTH;
412 dst += LCD_WIDTH - width;
413 #else
414 row_end -= 1;
415 dst -= LCD_WIDTH*width + 1;
416 #endif
417
418 do
419 {
420 int y, cb, cr, rv, guv, bu, r, g, b;
421
422 y = YFAC*(*ysrc++ - 16);
423 cb = *usrc++ - 128;
424 cr = *vsrc++ - 128;
425
426 rv = RVFAC*cr;
427 guv = GUFAC*cb + GVFAC*cr;
428 bu = BUFAC*cb;
429
430 r = y + rv;
431 g = y + guv;
432 b = y + bu;
433
434 if ((unsigned)(r | g | b) > 64*256-1)
435 {
436 r = clamp(r, 0, 64*256-1);
437 g = clamp(g, 0, 64*256-1);
438 b = clamp(b, 0, 64*256-1);
439 }
440
441 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
442
443 #if LCD_WIDTH >= LCD_HEIGHT
444 dst++;
445 #else
446 dst += LCD_WIDTH;
447 #endif
448
449 y = YFAC*(*ysrc++ - 16);
450 r = y + rv;
451 g = y + guv;
452 b = y + bu;
453
454 if ((unsigned)(r | g | b) > 64*256-1)
455 {
456 r = clamp(r, 0, 64*256-1);
457 g = clamp(g, 0, 64*256-1);
458 b = clamp(b, 0, 64*256-1);
459 }
460
461 *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9);
462
463 #if LCD_WIDTH >= LCD_HEIGHT
464 dst++;
465 #else
466 dst += LCD_WIDTH;
467 #endif
468 }
469 while (dst < row_end);
470
471 ysrc += stride;
472 usrc += stride >> 1;
473 vsrc += stride >> 1;
474
475 #if LCD_WIDTH >= LCD_HEIGHT
476 row_end += LCD_WIDTH;
477 dst += LCD_WIDTH - width;
478 #else
479 row_end -= 1;
480 dst -= LCD_WIDTH*width + 1;
481 #endif
482 }
483 while (--linecounter > 0);
484
485 #if LCD_WIDTH >= LCD_HEIGHT
486 lcd_update_rect(x, y, width, height);
487 #else
488 lcd_update_rect(LCD_WIDTH - y - height, x, height, width);
489 #endif
490}