blob: c49fe99d851c0ac1bfc7dca69245a9bb2ce429f4 [file] [log] [blame]
Daniel Stenberg86bff1c2006-08-03 20:18:31 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Barry Wardell
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * 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.
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000021#include "config.h"
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000022#include "cpu.h"
23#include "lcd.h"
24#include "kernel.h"
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000025#include "system.h"
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000026
Barry Wardella64f51e2006-08-23 09:14:28 +000027/* register defines for TL1771 */
28#define R_START_OSC 0x00
29#define R_DEVICE_CODE_READ 0x00
30#define R_DRV_OUTPUT_CONTROL 0x01
31#define R_DRV_AC_CONTROL 0x02
32#define R_ENTRY_MODE 0x03
33#define R_DISP_CONTROL1 0x07
34#define R_DISP_CONTROL2 0x08
35#define R_FRAME_CYCLE_CONTROL 0x0b
36#define R_POWER_CONTROL1 0x10
37#define R_POWER_CONTROL2 0x11
38#define R_POWER_CONTROL3 0x12
39#define R_POWER_CONTROL4 0x13
40#define R_POWER_CONTROL5 0x14
41#define R_RAM_ADDR_SET 0x21
42#define R_WRITE_DATA_2_GRAM 0x22
43#define R_GAMMA_FINE_ADJ_POS1 0x30
44#define R_GAMMA_FINE_ADJ_POS2 0x31
45#define R_GAMMA_FINE_ADJ_POS3 0x32
46#define R_GAMMA_GRAD_ADJ_POS 0x33
47#define R_GAMMA_FINE_ADJ_NEG1 0x34
48#define R_GAMMA_FINE_ADJ_NEG2 0x35
49#define R_GAMMA_FINE_ADJ_NEG3 0x36
50#define R_GAMMA_GRAD_ADJ_NEG 0x37
51#define R_POWER_CONTROL6 0x38
52#define R_GATE_SCAN_START_POS 0x40
53#define R_1ST_SCR_DRV_POS 0x42
54#define R_2ND_SCR_DRV_POS 0x43
Barry Wardell9b1dd442006-09-04 23:10:27 +000055#define R_HORIZ_RAM_ADDR_POS 0x44
56#define R_VERT_RAM_ADDR_POS 0x45
Barry Wardella64f51e2006-08-23 09:14:28 +000057
Barry Wardella396d7e2006-08-31 01:27:54 +000058static inline void lcd_wait_write(void)
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000059{
Jens Arnold8aeed2d2007-10-12 00:28:57 +000060 while (LCD2_PORT & LCD2_BUSY_MASK);
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000061}
62
Barry Wardell9272dfd2006-08-20 10:18:47 +000063/* Send command */
Jens Arnold8aeed2d2007-10-12 00:28:57 +000064static inline void lcd_send_cmd(unsigned v)
Barry Wardell9272dfd2006-08-20 10:18:47 +000065{
66 lcd_wait_write();
Jens Arnold8aeed2d2007-10-12 00:28:57 +000067 LCD2_PORT = LCD2_CMD_MASK;
68 LCD2_PORT = LCD2_CMD_MASK | v;
Barry Wardell9272dfd2006-08-20 10:18:47 +000069}
70
71/* Send 16-bit data */
Jens Arnold8aeed2d2007-10-12 00:28:57 +000072static inline void lcd_send_data(unsigned v)
Barry Wardell9272dfd2006-08-20 10:18:47 +000073{
74 lcd_wait_write();
Jens Arnold8aeed2d2007-10-12 00:28:57 +000075 LCD2_PORT = LCD2_DATA_MASK | (v >> 8); /* Send MSB first */
76 LCD2_PORT = LCD2_DATA_MASK | (v & 0xff);
Barry Wardell9272dfd2006-08-20 10:18:47 +000077}
78
Jens Arnoldd160c102007-10-12 17:09:33 +000079/* Write value to register */
80static void lcd_write_reg(int reg, int val)
81{
82 lcd_send_cmd(reg);
83 lcd_send_data(val);
84}
85
Barry Wardell9272dfd2006-08-20 10:18:47 +000086
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000087/*** hardware configuration ***/
88
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000089void lcd_set_contrast(int val)
90{
Barry Wardell9272dfd2006-08-20 10:18:47 +000091 /* TODO: Implement lcd_set_contrast() */
92 (void)val;
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000093}
94
95void lcd_set_invert_display(bool yesno)
96{
Barry Wardell9272dfd2006-08-20 10:18:47 +000097 /* TODO: Implement lcd_set_invert_display() */
98 (void)yesno;
Daniel Stenberg86bff1c2006-08-03 20:18:31 +000099}
100
101/* turn the display upside down (call lcd_update() afterwards) */
102void lcd_set_flip(bool yesno)
103{
Barry Wardell9272dfd2006-08-20 10:18:47 +0000104 /* TODO: Implement lcd_set_flip() */
105 (void)yesno;
Daniel Stenberg86bff1c2006-08-03 20:18:31 +0000106}
107
Barry Wardell9272dfd2006-08-20 10:18:47 +0000108/* LCD init */
Daniel Stenberg86bff1c2006-08-03 20:18:31 +0000109void lcd_init_device(void)
Barry Wardell9272dfd2006-08-20 10:18:47 +0000110{
Jens Arnoldbd1592e2007-07-26 15:33:25 +0000111 CLCD_CLOCK_SRC |= 0xc0000000; /* Set LCD interface clock to PLL */
Barry Wardell9272dfd2006-08-20 10:18:47 +0000112 /* H10 LCD is initialised by the bootloader */
Daniel Stenberg86bff1c2006-08-03 20:18:31 +0000113}
114
115/*** update functions ***/
116
Barry Wardella396d7e2006-08-31 01:27:54 +0000117#define CSUB_X 2
118#define CSUB_Y 2
119
120#define RYFAC (31*257)
121#define GYFAC (31*257)
122#define BYFAC (31*257)
123#define RVFAC 11170 /* 31 * 257 * 1.402 */
124#define GVFAC (-5690) /* 31 * 257 * -0.714136 */
125#define GUFAC (-2742) /* 31 * 257 * -0.344136 */
126#define BUFAC 14118 /* 31 * 257 * 1.772 */
127
128#define ROUNDOFFS (127*257)
129#define ROUNDOFFSG (63*257)
130
131/* Performance function to blit a YUV bitmap directly to the LCD */
Jens Arnold68a21682008-03-24 00:35:53 +0000132void lcd_blit_yuv(unsigned char * const src[3],
Barry Wardell18cfe432006-08-20 23:05:47 +0000133 int src_x, int src_y, int stride,
134 int x, int y, int width, int height)
135{
Barry Wardella396d7e2006-08-31 01:27:54 +0000136 int y0, x0, y1, x1;
137 int ymax;
138
139 width = (width + 1) & ~1;
140
141 /* calculate the drawing region */
142 x0 = x;
143 x1 = x + width - 1;
144 y0 = y;
145 y1 = y + height - 1;
146
Jens Arnold8aeed2d2007-10-12 00:28:57 +0000147 /* max horiz << 8 | start horiz */
Jens Arnoldd160c102007-10-12 17:09:33 +0000148 lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (x1 << 8) | x0);
149
Jens Arnold8aeed2d2007-10-12 00:28:57 +0000150 /* max vert << 8 | start vert */
Jens Arnoldd160c102007-10-12 17:09:33 +0000151 lcd_write_reg(R_VERT_RAM_ADDR_POS, (y1 << 8) | y0);
Barry Wardell9b1dd442006-09-04 23:10:27 +0000152
Jens Arnold8aeed2d2007-10-12 00:28:57 +0000153 /* start vert << 8 | start horiz */
Jens Arnoldd160c102007-10-12 17:09:33 +0000154 lcd_write_reg(R_RAM_ADDR_SET, (y0 << 8) | x0);
155
Barry Wardella396d7e2006-08-31 01:27:54 +0000156 /* start drawing */
157 lcd_send_cmd(R_WRITE_DATA_2_GRAM);
158
159 ymax = y + height - 1 ;
160
161 const int stride_div_csub_x = stride/CSUB_X;
162
163 for (; y <= ymax ; y++)
164 {
165 /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */
166 const unsigned char *ysrc = src[0] + stride * src_y + src_x;
167
168 const int uvoffset = stride_div_csub_x * (src_y/CSUB_Y) +
169 (src_x/CSUB_X);
170
171 const unsigned char *usrc = src[1] + uvoffset;
172 const unsigned char *vsrc = src[2] + uvoffset;
173 const unsigned char *row_end = ysrc + width;
174
175 int y, u, v;
176 int red1, green1, blue1;
177 int red2, green2, blue2;
178 unsigned rbits, gbits, bbits;
179
180 int rc, gc, bc;
181
182 do
183 {
184 u = *usrc++ - 128;
185 v = *vsrc++ - 128;
186 rc = RVFAC * v + ROUNDOFFS;
187 gc = GVFAC * v + GUFAC * u + ROUNDOFFSG;
188 bc = BUFAC * u + ROUNDOFFS;
189
190 /* Pixel 1 */
191 y = *ysrc++;
192
193 red1 = RYFAC * y + rc;
194 green1 = GYFAC * y + gc;
195 blue1 = BYFAC * y + bc;
196
197 /* Pixel 2 */
198 y = *ysrc++;
199 red2 = RYFAC * y + rc;
200 green2 = GYFAC * y + gc;
201 blue2 = BYFAC * y + bc;
202
203 /* Since out of bounds errors are relatively rare, we check two
204 pixels at once to see if any components are out of bounds, and
205 then fix whichever is broken. This works due to high values and
206 negative values both becoming larger than the cutoff when
207 casted to unsigned. And ORing them together checks all of them
208 simultaneously. */
209 if (((unsigned)(red1 | green1 | blue1 |
210 red2 | green2 | blue2)) > (RYFAC*255+ROUNDOFFS)) {
211 if (((unsigned)(red1 | green1 | blue1)) >
212 (RYFAC*255+ROUNDOFFS)) {
213 if ((unsigned)red1 > (RYFAC*255+ROUNDOFFS))
214 {
215 if (red1 < 0)
216 red1 = 0;
217 else
218 red1 = (RYFAC*255+ROUNDOFFS);
219 }
220 if ((unsigned)green1 > (GYFAC*255+ROUNDOFFSG))
221 {
222 if (green1 < 0)
223 green1 = 0;
224 else
225 green1 = (GYFAC*255+ROUNDOFFSG);
226 }
227 if ((unsigned)blue1 > (BYFAC*255+ROUNDOFFS))
228 {
229 if (blue1 < 0)
230 blue1 = 0;
231 else
232 blue1 = (BYFAC*255+ROUNDOFFS);
233 }
234 }
235
236 if (((unsigned)(red2 | green2 | blue2)) >
237 (RYFAC*255+ROUNDOFFS)) {
238 if ((unsigned)red2 > (RYFAC*255+ROUNDOFFS))
239 {
240 if (red2 < 0)
241 red2 = 0;
242 else
243 red2 = (RYFAC*255+ROUNDOFFS);
244 }
245 if ((unsigned)green2 > (GYFAC*255+ROUNDOFFSG))
246 {
247 if (green2 < 0)
248 green2 = 0;
249 else
250 green2 = (GYFAC*255+ROUNDOFFSG);
251 }
252 if ((unsigned)blue2 > (BYFAC*255+ROUNDOFFS))
253 {
254 if (blue2 < 0)
255 blue2 = 0;
256 else
257 blue2 = (BYFAC*255+ROUNDOFFS);
258 }
259 }
260 }
261
262 rbits = red1 >> 16 ;
263 gbits = green1 >> 15 ;
264 bbits = blue1 >> 16 ;
Jens Arnold8aeed2d2007-10-12 00:28:57 +0000265 lcd_send_data((rbits << 11) | (gbits << 5) | bbits);
Barry Wardella396d7e2006-08-31 01:27:54 +0000266
267 rbits = red2 >> 16 ;
268 gbits = green2 >> 15 ;
269 bbits = blue2 >> 16 ;
Jens Arnold8aeed2d2007-10-12 00:28:57 +0000270 lcd_send_data((rbits << 11) | (gbits << 5) | bbits);
Barry Wardella396d7e2006-08-31 01:27:54 +0000271 }
272 while (ysrc < row_end);
273
274 src_y++;
275 }
Barry Wardell18cfe432006-08-20 23:05:47 +0000276}
Barry Wardell9272dfd2006-08-20 10:18:47 +0000277
278
279/* Update a fraction of the display. */
Jens Arnold02580852008-04-02 22:45:23 +0000280void lcd_update_rect(int x0, int y0, int width, int height)
Barry Wardell9272dfd2006-08-20 10:18:47 +0000281{
Jens Arnold02580852008-04-02 22:45:23 +0000282 int x1, y1;
283 int newx,newwidth;
284 unsigned long *addr;
Barry Wardell9272dfd2006-08-20 10:18:47 +0000285
Jens Arnold02580852008-04-02 22:45:23 +0000286 /* Ensure x and width are both even - so we can read 32-bit aligned
287 data from lcd_framebuffer */
288 newx=x0&~1;
289 newwidth=width&~1;
290 if (newx+newwidth < x0+width) { newwidth+=2; }
291 x0=newx; width=newwidth;
Barry Wardell9272dfd2006-08-20 10:18:47 +0000292
Jens Arnold02580852008-04-02 22:45:23 +0000293 /* calculate the drawing region */
294 y1 = (y0 + height) - 1; /* max vert */
295 x1 = (x0 + width) - 1; /* max horiz */
296
297
298 /* swap max horiz < start horiz */
299 if (y1 < y0) {
300 int t;
301 t = y0;
302 y0 = y1;
303 y1 = t;
304 }
305
306 /* swap max vert < start vert */
307 if (x1 < x0) {
308 int t;
309 t = x0;
310 x0 = x1;
311 x1 = t;
312 }
313
314 /* max horiz << 8 | start horiz */
315 lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (x1 << 8) | x0);
316
317 /* max vert << 8 | start vert */
318 lcd_write_reg(R_VERT_RAM_ADDR_POS, (y1 << 8) | y0);
319
320 /* start vert << 8 | start horiz */
321 lcd_write_reg(R_RAM_ADDR_SET, (y0 << 8) | x0);
322
323 /* start drawing */
Barry Wardell9272dfd2006-08-20 10:18:47 +0000324 lcd_send_cmd(R_WRITE_DATA_2_GRAM);
325
Jens Arnold02580852008-04-02 22:45:23 +0000326 addr = (unsigned long*)&lcd_framebuffer[y0][x0];
Barry Wardell9272dfd2006-08-20 10:18:47 +0000327
Jens Arnold02580852008-04-02 22:45:23 +0000328 while (height > 0) {
329 int c, r;
330 int h, pixels_to_write;
331
332 pixels_to_write = (width * height) * 2;
333 h = height;
334
335 /* calculate how much we can do in one go */
336 if (pixels_to_write > 0x10000) {
337 h = (0x10000/2) / width;
338 pixels_to_write = (width * h) * 2;
Barry Wardell9272dfd2006-08-20 10:18:47 +0000339 }
Jens Arnold02580852008-04-02 22:45:23 +0000340
341 LCD2_BLOCK_CTRL = 0x10000080;
342 LCD2_BLOCK_CONFIG = 0xc0010000 | (pixels_to_write - 1);
343 LCD2_BLOCK_CTRL = 0x34000000;
344
345 /* for each row */
346 for (r = 0; r < h; r++) {
347 /* for each column */
348 for (c = 0; c < width; c += 2) {
349 while (!(LCD2_BLOCK_CTRL & LCD2_BLOCK_TXOK));
350
351 /* output 2 pixels */
352 LCD2_BLOCK_DATA = *addr++;
353 }
354 addr += (LCD_WIDTH - width)/2;
355 }
356
357 while (!(LCD2_BLOCK_CTRL & LCD2_BLOCK_READY));
358 LCD2_BLOCK_CONFIG = 0;
359
360 height -= h;
Barry Wardell9272dfd2006-08-20 10:18:47 +0000361 }
362}
363
Daniel Stenberg86bff1c2006-08-03 20:18:31 +0000364/* Update the display.
365 This must be called after all other LCD functions that change the display. */
Daniel Stenberg86bff1c2006-08-03 20:18:31 +0000366void lcd_update(void)
367{
Barry Wardell9272dfd2006-08-20 10:18:47 +0000368 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
Daniel Stenberg86bff1c2006-08-03 20:18:31 +0000369}