summaryrefslogtreecommitdiff
path: root/firmware/target/arm/s5l8702/ipod6g/lcd-6g.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/s5l8702/ipod6g/lcd-6g.c')
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/lcd-6g.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/firmware/target/arm/s5l8702/ipod6g/lcd-6g.c b/firmware/target/arm/s5l8702/ipod6g/lcd-6g.c
new file mode 100644
index 0000000000..14647a5697
--- /dev/null
+++ b/firmware/target/arm/s5l8702/ipod6g/lcd-6g.c
@@ -0,0 +1,578 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: lcd-nano2g.c 28868 2010-12-21 06:59:17Z Buschel $
9 *
10 * Copyright (C) 2009 by Dave Chapman
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 "config.h"
22
23#include "hwcompat.h"
24#include "kernel.h"
25#include "lcd.h"
26#include "system.h"
27#include "cpu.h"
28#include "pmu-target.h"
29#include "power.h"
30#include "string.h"
31#include "dma-s5l8702.h"
32
33
34#define R_HORIZ_GRAM_ADDR_SET 0x200
35#define R_VERT_GRAM_ADDR_SET 0x201
36#define R_WRITE_DATA_TO_GRAM 0x202
37#define R_HORIZ_ADDR_START_POS 0x210
38#define R_HORIZ_ADDR_END_POS 0x211
39#define R_VERT_ADDR_START_POS 0x212
40#define R_VERT_ADDR_END_POS 0x213
41
42
43/* LCD type 1 register defines */
44
45#define R_COLUMN_ADDR_SET 0x2a
46#define R_ROW_ADDR_SET 0x2b
47#define R_MEMORY_WRITE 0x2c
48
49
50/** globals **/
51
52int lcd_type; /* also needed in debug-s5l8702.c */
53static struct mutex lcd_mutex;
54static uint16_t lcd_dblbuf[LCD_HEIGHT][LCD_WIDTH] CACHEALIGN_ATTR;
55static bool lcd_ispowered;
56
57#define CMD 0 /* send command with N data */
58#define MREG 1 /* write multiple registers */
59#define SLEEP 2
60#define END 0xff
61
62#define CMD16(len) (CMD | ((len) << 8))
63#define MREG16(len) (MREG | ((len) << 8))
64#define SLEEP16(t) (SLEEP | ((t) << 8))
65
66/* powersave sequences */
67
68static const unsigned char lcd_sleep_seq_01[] =
69{
70 CMD, 0x28, 0, /* Display Off */
71 SLEEP, 5, /* 50 ms */
72 CMD, 0x10, 0, /* Sleep In Mode */
73 SLEEP, 5, /* 50 ms */
74 END
75};
76
77static const unsigned short lcd_enter_deepstby_seq_23[] =
78{
79 /* Display Off */
80 MREG16(1), 0x007, 0x0172,
81 MREG16(1), 0x030, 0x03ff,
82 SLEEP16(9),
83 MREG16(1), 0x007, 0x0120,
84 MREG16(1), 0x030, 0x0000,
85 MREG16(1), 0x100, 0x0780,
86 MREG16(1), 0x007, 0x0000,
87 MREG16(1), 0x101, 0x0260,
88 MREG16(1), 0x102, 0x00a9,
89 SLEEP16(3),
90 MREG16(1), 0x100, 0x0700,
91
92 /* Deep Standby Mode */
93 MREG16(1), 0x100, 0x0704,
94 SLEEP16(5),
95 END
96};
97
98/* init sequences */
99
100#ifdef HAVE_LCD_SLEEP
101static const unsigned char lcd_awake_seq_01[] =
102{
103 CMD, 0x11, 0, /* Sleep Out Mode */
104 SLEEP, 6, /* 60 ms */
105 CMD, 0x29, 0, /* Display On */
106 END
107};
108#endif
109
110#if defined(HAVE_LCD_SLEEP) || defined(BOOTLOADER)
111static const unsigned short lcd_init_seq_23[] =
112{
113 /* Display settings */
114 MREG16(1), 0x008, 0x0808,
115 MREG16(7), 0x010, 0x0013, 0x0300, 0x0101, 0x0a03, 0x0a0e, 0x0a19, 0x2402,
116 MREG16(1), 0x018, 0x0001,
117 MREG16(1), 0x090, 0x0021,
118
119 /* Gamma settings */
120 MREG16(14), 0x300, 0x0307, 0x0003, 0x0402, 0x0303, 0x0300, 0x0407, 0x1c04,
121 0x0307, 0x0003, 0x0402, 0x0303, 0x0300, 0x0407, 0x1c04,
122 MREG16(14), 0x310, 0x0707, 0x0407, 0x0306, 0x0303, 0x0300, 0x0407, 0x1c01,
123 0x0707, 0x0407, 0x0306, 0x0303, 0x0300, 0x0407, 0x1c01,
124 MREG16(14), 0x320, 0x0206, 0x0102, 0x0404, 0x0303, 0x0300, 0x0407, 0x1c1f,
125 0x0206, 0x0102, 0x0404, 0x0303, 0x0300, 0x0407, 0x1c1f,
126
127 /* GRAM and Base Imagen settings (ili9326ds) */
128 MREG16(2), 0x400, 0x001d, 0x0001,
129 MREG16(1), 0x205, 0x0060,
130
131 /* Power settings */
132 MREG16(1), 0x007, 0x0001,
133 MREG16(1), 0x031, 0x0071,
134 MREG16(1), 0x110, 0x0001,
135 MREG16(6), 0x100, 0x17b0, 0x0220, 0x00bd, 0x1500, 0x0103, 0x0105,
136
137 /* Display On */
138 MREG16(1), 0x007, 0x0021,
139 MREG16(1), 0x001, 0x0110,
140 MREG16(1), 0x003, 0x0230,
141 MREG16(1), 0x002, 0x0500,
142 MREG16(1), 0x007, 0x0031,
143 MREG16(1), 0x030, 0x0007,
144 SLEEP16(3),
145 MREG16(1), 0x030, 0x03ff,
146 SLEEP16(6),
147 MREG16(1), 0x007, 0x0072,
148 SLEEP16(15),
149 MREG16(1), 0x007, 0x0173,
150 END
151};
152#endif /* HAVE_LCD_SLEEP || BOOTLOADER */
153
154#ifdef BOOTLOADER
155static const unsigned char lcd_init_seq_0[] =
156{
157 CMD, 0x11, 0, /* Sleep Out Mode */
158 SLEEP, 0x03, /* 30 ms */
159 CMD, 0x35, 1, 0x00, /* TEON (TBC) */
160 CMD, 0x3a, 1, 0x06, /* COLMOD (TBC) */
161 CMD, 0x36, 1, 0x00, /* MADCTR (TBC) */
162 CMD, 0x13, 0, /* NORON: Normal Mode On (Partial
163 Mode Off, Scroll Mode Off) */
164 CMD, 0x29, 0, /* Display On */
165 END
166};
167
168static const unsigned char lcd_init_seq_1[] =
169{
170 CMD, 0xb0, 21, 0x3a, 0x3a, 0x80, 0x80, 0x0a, 0x0a, 0x0a, 0x0a,
171 0x0a, 0x0a, 0x0a, 0x0a, 0x3c, 0x30, 0x0f, 0x00,
172 0x01, 0x54, 0x06, 0x66, 0x66,
173 CMD, 0xb8, 1, 0xd8,
174 CMD, 0xb1, 30, 0x14, 0x59, 0x00, 0x15, 0x57, 0x27, 0x04, 0x85,
175 0x14, 0x59, 0x00, 0x15, 0x57, 0x27, 0x04, 0x85,
176 0x14, 0x09, 0x15, 0x57, 0x27, 0x04, 0x05,
177 0x14, 0x09, 0x15, 0x57, 0x27, 0x04, 0x05,
178 CMD, 0xd2, 1, 0x01,
179
180 /* Gamma settings (TBC) */
181 CMD, 0xe0, 13, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x12, 0x16, 0x1f,
182 0x25, 0x22, 0x24, 0x29, 0x1c,
183 CMD, 0xe1, 13, 0x08, 0x01, 0x01, 0x06, 0x0b, 0x11, 0x15, 0x1f,
184 0x27, 0x26, 0x29, 0x2f, 0x1e,
185 CMD, 0xe2, 13, 0x07, 0x01, 0x01, 0x05, 0x09, 0x0f, 0x13, 0x1e,
186 0x26, 0x25, 0x28, 0x2e, 0x1e,
187 CMD, 0xe3, 13, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x12, 0x16, 0x1f,
188 0x25, 0x22, 0x24, 0x29, 0x1c,
189 CMD, 0xe4, 13, 0x08, 0x01, 0x01, 0x06, 0x0b, 0x11, 0x15, 0x1f,
190 0x27, 0x26, 0x29, 0x2f, 0x1e,
191 CMD, 0xe5, 13, 0x07, 0x01, 0x01, 0x05, 0x09, 0x0f, 0x13, 0x1e,
192 0x26, 0x25, 0x28, 0x2e, 0x1e,
193
194 CMD, 0x3a, 1, 0x06, /* COLMOD (TBC) */
195 CMD, 0xc2, 1, 0x00, /* Power Control 3 (TBC) */
196 CMD, 0x35, 1, 0x00, /* TEON (TBC) */
197 CMD, 0x11, 0, /* Sleep Out Mode */
198 SLEEP, 0x06, /* 60 ms */
199 CMD, 0x13, 0, /* NORON: Normal Mode On (Partial
200 Mode Off, Scroll Mode Off) */
201 CMD, 0x29, 0, /* Display On */
202 END
203};
204#endif
205
206/* DMA configuration */
207
208/* one single transfer at once, needed LLIs:
209 * screen_size / (DMAC_LLI_MAX_COUNT << swidth) =
210 * (320*240*2) / (4095*2) = 19
211 */
212#define LCD_DMA_TSKBUF_SZ 1 /* N tasks, MUST be pow2 */
213#define LCD_DMA_LLIBUF_SZ 32 /* N LLIs, MUST be pow2 */
214
215static struct dmac_tsk lcd_dma_tskbuf[LCD_DMA_TSKBUF_SZ];
216static struct dmac_lli volatile \
217 lcd_dma_llibuf[LCD_DMA_LLIBUF_SZ] CACHEALIGN_ATTR;
218
219static struct dmac_ch lcd_dma_ch = {
220 .dmac = &s5l8702_dmac0,
221 .prio = DMAC_CH_PRIO(4),
222 .cb_fn = NULL,
223
224 .tskbuf = lcd_dma_tskbuf,
225 .tskbuf_mask = LCD_DMA_TSKBUF_SZ - 1,
226 .queue_mode = QUEUE_NORMAL,
227
228 .llibuf = lcd_dma_llibuf,
229 .llibuf_mask = LCD_DMA_LLIBUF_SZ - 1,
230 .llibuf_bus = DMAC_MASTER_AHB1,
231};
232
233static struct dmac_ch_cfg lcd_dma_ch_cfg = {
234 .srcperi = S5L8702_DMAC0_PERI_MEM,
235 .dstperi = S5L8702_DMAC0_PERI_LCD_WR,
236 .sbsize = DMACCxCONTROL_BSIZE_1,
237 .dbsize = DMACCxCONTROL_BSIZE_1,
238 .swidth = DMACCxCONTROL_WIDTH_16,
239 .dwidth = DMACCxCONTROL_WIDTH_16,
240 .sbus = DMAC_MASTER_AHB1,
241 .dbus = DMAC_MASTER_AHB1,
242 .sinc = DMACCxCONTROL_INC_ENABLE,
243 .dinc = DMACCxCONTROL_INC_DISABLE,
244 .prot = DMAC_PROT_CACH | DMAC_PROT_BUFF | DMAC_PROT_PRIV,
245 .lli_xfer_max_count = DMAC_LLI_MAX_COUNT,
246};
247
248static inline void s5l_lcd_write_reg(int cmd, unsigned int data)
249{
250 while (LCD_STATUS & 0x10);
251 LCD_WCMD = cmd;
252
253 while (LCD_STATUS & 0x10);
254 /* 16-bit/1-transfer data format (ili9320ds s7.2.2) */
255 LCD_WDATA = (data & 0x78ff) |
256 ((data & 0x0300) << 1) | ((data & 0x0400) << 5);
257}
258
259static inline void s5l_lcd_write_cmd(unsigned short cmd)
260{
261 while (LCD_STATUS & 0x10);
262 LCD_WCMD = cmd;
263}
264
265static inline void s5l_lcd_write_data(unsigned short data)
266{
267 while (LCD_STATUS & 0x10);
268 LCD_WDATA = data;
269}
270
271static void lcd_run_seq8(const unsigned char *seq)
272{
273 int n;
274
275 while (1) switch (*seq++)
276 {
277 case CMD:
278 s5l_lcd_write_cmd(*seq++);
279 n = *seq++;
280 while (n--)
281 s5l_lcd_write_data(*seq++);
282 break;
283 case SLEEP:
284 sleep(*seq++);
285 break;
286 case END:
287 default:
288 /* bye */
289 return;
290 }
291}
292
293static void lcd_run_seq16(const unsigned short *seq)
294{
295 int action, param;
296 unsigned short reg;
297
298 while (1)
299 {
300 action = *seq & 0xff;
301 param = *seq++ >> 8;
302
303 switch (action)
304 {
305 case MREG:
306 reg = *seq++;
307 while (param--)
308 s5l_lcd_write_reg(reg++, *seq++);
309 break;
310 case SLEEP:
311 sleep(param);
312 break;
313 case END:
314 default:
315 /* bye */
316 return;
317 }
318 }
319}
320
321/*** hardware configuration ***/
322
323int lcd_default_contrast(void)
324{
325 return 0x1f;
326}
327
328void lcd_set_contrast(int val)
329{
330 (void)val;
331}
332
333void lcd_set_invert_display(bool yesno)
334{
335 (void)yesno;
336}
337
338void lcd_set_flip(bool yesno)
339{
340 (void)yesno;
341}
342
343bool lcd_active(void)
344{
345 return lcd_ispowered;
346}
347
348void lcd_powersave(void)
349{
350 mutex_lock(&lcd_mutex);
351
352 if (lcd_type & 2)
353 lcd_run_seq16(lcd_enter_deepstby_seq_23);
354 else
355 lcd_run_seq8(lcd_sleep_seq_01);
356
357 /* mask lcd controller clock gate */
358 PWRCON(0) |= (1 << CLOCKGATE_LCD);
359
360 lcd_ispowered = false;
361
362 mutex_unlock(&lcd_mutex);
363}
364
365void lcd_shutdown(void)
366{
367 pmu_write(0x2b, 0); /* Kill the backlight, instantly. */
368 pmu_write(0x29, 0);
369
370 lcd_powersave();
371}
372
373#ifdef HAVE_LCD_SLEEP
374void lcd_sleep(void)
375{
376 lcd_powersave();
377}
378
379void lcd_awake(void)
380{
381 mutex_lock(&lcd_mutex);
382
383 /* unmask lcd controller clock gate */
384 PWRCON(0) &= ~(1 << CLOCKGATE_LCD);
385
386 if (lcd_type & 2) {
387 /* release from deep standby mode (ili9320ds s12.3) */
388 for (int i = 0; i < 6; i++) {
389 s5l_lcd_write_cmd(0x000);
390 udelay(1000);
391 }
392
393 lcd_run_seq16(lcd_init_seq_23);
394 }
395 else
396 lcd_run_seq8(lcd_awake_seq_01);
397
398 lcd_ispowered = true;
399
400 mutex_unlock(&lcd_mutex);
401
402 send_event(LCD_EVENT_ACTIVATION, NULL);
403}
404#endif
405
406/* LCD init */
407void lcd_init_device(void)
408{
409 mutex_init(&lcd_mutex);
410
411 /* unmask lcd controller clock gate */
412 PWRCON(0) &= ~(1 << CLOCKGATE_LCD);
413
414 /* Detect lcd type */
415 lcd_type = (PDAT6 & 0x30) >> 4;
416
417 while (!(LCD_STATUS & 0x2));
418 LCD_CONFIG = 0x80100db0;
419 LCD_PHTIME = 0x33;
420
421 /* Configure DMA channel */
422 dmac_ch_init(&lcd_dma_ch, &lcd_dma_ch_cfg);
423
424#ifdef BOOTLOADER
425 switch (lcd_type) {
426 case 0: lcd_run_seq8(lcd_init_seq_0); break;
427 case 1: lcd_run_seq8(lcd_init_seq_1); break;
428 default: lcd_run_seq16(lcd_init_seq_23); break;
429 }
430#endif
431
432 lcd_ispowered = true;
433}
434
435/*** Update functions ***/
436
437static inline void lcd_write_pixel(fb_data pixel)
438{
439 mutex_lock(&lcd_mutex);
440 LCD_WDATA = pixel;
441 mutex_unlock(&lcd_mutex);
442}
443
444/* Update the display.
445 This must be called after all other LCD functions that change the display. */
446void lcd_update(void) ICODE_ATTR;
447void lcd_update(void)
448{
449 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
450}
451
452/* Line write helper function. */
453extern void lcd_write_line(const fb_data *addr,
454 int pixelcount,
455 const unsigned int lcd_base_addr);
456
457static void displaylcd_setup(int x, int y, int width, int height) ICODE_ATTR;
458static void displaylcd_setup(int x, int y, int width, int height)
459{
460 /* TODO: ISR()->panicf()->lcd_update() blocks forever */
461 mutex_lock(&lcd_mutex);
462 while (dmac_ch_running(&lcd_dma_ch))
463 yield();
464
465 int xe = (x + width) - 1; /* max horiz */
466 int ye = (y + height) - 1; /* max vert */
467
468 if (lcd_type & 2) {
469 s5l_lcd_write_reg(R_HORIZ_ADDR_START_POS, x);
470 s5l_lcd_write_reg(R_HORIZ_ADDR_END_POS, xe);
471 s5l_lcd_write_reg(R_VERT_ADDR_START_POS, y);
472 s5l_lcd_write_reg(R_VERT_ADDR_END_POS, ye);
473
474 s5l_lcd_write_reg(R_HORIZ_GRAM_ADDR_SET, x);
475 s5l_lcd_write_reg(R_VERT_GRAM_ADDR_SET, y);
476
477 s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM);
478 } else {
479 s5l_lcd_write_cmd(R_COLUMN_ADDR_SET);
480 s5l_lcd_write_data(x >> 8);
481 s5l_lcd_write_data(x & 0xff);
482 s5l_lcd_write_data(xe >> 8);
483 s5l_lcd_write_data(xe & 0xff);
484
485 s5l_lcd_write_cmd(R_ROW_ADDR_SET);
486 s5l_lcd_write_data(y >> 8);
487 s5l_lcd_write_data(y & 0xff);
488 s5l_lcd_write_data(ye >> 8);
489 s5l_lcd_write_data(ye & 0xff);
490
491 s5l_lcd_write_cmd(R_MEMORY_WRITE);
492 }
493}
494
495static void displaylcd_dma(int pixels) ICODE_ATTR;
496static void displaylcd_dma(int pixels)
497{
498 commit_dcache();
499 dmac_ch_queue(&lcd_dma_ch, lcd_dblbuf,
500 (void*)S5L8702_DADDR_PERI_LCD_WR, pixels*2, NULL);
501 mutex_unlock(&lcd_mutex);
502}
503
504/* Update a fraction of the display. */
505void lcd_update_rect(int, int, int, int) ICODE_ATTR;
506void lcd_update_rect(int x, int y, int width, int height)
507{
508 int pixels = width * height;
509 fb_data* p = FBADDR(x,y);
510 uint16_t* out = lcd_dblbuf[0];
511
512#ifdef HAVE_LCD_SLEEP
513 if (!lcd_active()) return;
514#endif
515
516 displaylcd_setup(x, y, width, height);
517
518 /* Copy display bitmap to hardware */
519 if (LCD_WIDTH == width) {
520 /* Write all lines at once */
521 memcpy(out, p, pixels * 2);
522 } else {
523 do {
524 /* Write a single line */
525 memcpy(out, p, width * 2);
526 p += LCD_WIDTH;
527 out += width;
528 } while (--height);
529 }
530
531 displaylcd_dma(pixels);
532}
533
534/* Line write helper function for lcd_yuv_blit. Writes two lines of yuv420. */
535extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
536 uint16_t* outbuf,
537 int width,
538 int stride);
539
540/* Blit a YUV bitmap directly to the LCD */
541void lcd_blit_yuv(unsigned char * const src[3],
542 int src_x, int src_y, int stride,
543 int x, int y, int width, int height) ICODE_ATTR;
544void lcd_blit_yuv(unsigned char * const src[3],
545 int src_x, int src_y, int stride,
546 int x, int y, int width, int height)
547{
548 unsigned int z;
549 unsigned char const * yuv_src[3];
550
551#ifdef HAVE_LCD_SLEEP
552 if (!lcd_active()) return;
553#endif
554
555 width = (width + 1) & ~1; /* ensure width is even */
556
557 int pixels = width * height;
558 uint16_t* out = lcd_dblbuf[0];
559
560 z = stride * src_y;
561 yuv_src[0] = src[0] + z + src_x;
562 yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
563 yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
564
565 displaylcd_setup(x, y, width, height);
566
567 height >>= 1;
568
569 do {
570 lcd_write_yuv420_lines(yuv_src, out, width, stride);
571 yuv_src[0] += stride << 1;
572 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
573 yuv_src[2] += stride >> 1;
574 out += width << 1;
575 } while (--height);
576
577 displaylcd_dma(pixels);
578}