Fix a few possible problems discovered in -O0 / eabi experiments.
[kugel-rb.git] / firmware / target / arm / as3525 / sansa-fuze / lcd-fuze.c
blobc9dd69d47bae8418a15b2ba0ceafeb23d1d0ac3f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2008 by Dave Chapman
12 * LCD driver for the Sansa Fuze - controller unknown
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "config.h"
25 #include "cpu.h"
26 #include "lcd.h"
27 #include "file.h"
28 #include "debug.h"
29 #include "system.h"
30 #include "clock-target.h"
32 /* The controller is unknown, but some registers appear to be the same as the
33 HD66789R */
35 #define R_ENTRY_MODE 0x03
36 #define R_RAM_ADDR_SET 0x21
37 #define R_WRITE_DATA_2_GRAM 0x22
39 #define R_ENTRY_MODE_HORZ 0x7030
40 #define R_ENTRY_MODE_VERT 0x7038
42 static unsigned lcd_yuv_options = 0;
43 static bool display_on = false; /* is the display turned on? */
44 static bool display_flipped = false;
45 static int xoffset = 20; /* needed for flip */
46 /* we need to write a red pixel for correct button reads
47 * (see lcd_button_support()),but that must not happen while the lcd is updating
48 * so block lcd_button_support the during updates */
49 static volatile int lcd_busy = false;
51 static inline void lcd_delay(int x)
53 do {
54 asm volatile ("nop\n");
55 } while (x--);
58 static void as3525_dbop_init(void)
60 CGU_DBOP = (1<<3) | AS3525_DBOP_DIV;
62 DBOP_TIMPOL_01 = 0xe167e167;
63 DBOP_TIMPOL_23 = 0xe167006e;
65 /* short count: 16 | output data width: 16 | readstrobe line */
66 DBOP_CTRL = (1<<18|1<<12|1<<3);
68 GPIOB_AFSEL = 0xfc;
69 GPIOC_AFSEL = 0xff;
71 DBOP_TIMPOL_23 = 0x6000e;
72 /* short count: 16|enable write|output data width: 16|read strobe line */
73 DBOP_CTRL = (1<<18|1<<16|1<<12|1<<3);
74 DBOP_TIMPOL_01 = 0x6e167;
75 DBOP_TIMPOL_23 = 0xa167e06f;
77 /* TODO: The OF calls some other functions here, but maybe not important */
80 static void lcd_write_value16(unsigned short value)
82 DBOP_CTRL &= ~(1<<14|1<<13);
83 lcd_delay(10);
84 DBOP_DOUT16 = value;
85 while ((DBOP_STAT & (1<<10)) == 0);
88 static void lcd_write_cmd(int cmd)
90 /* Write register */
91 DBOP_TIMPOL_23 = 0xa167006e;
92 lcd_write_value16(cmd);
94 /* Wait for fifo to empty */
95 while ((DBOP_STAT & (1<<10)) == 0);
97 /* This loop is unique to the Fuze */
98 lcd_delay(4);
100 DBOP_TIMPOL_23 = 0xa167e06f;
103 void lcd_write_data(const fb_data* p_bytes, int count)
105 const long *data;
106 if ((int)p_bytes & 0x3)
107 { /* need to do a single 16bit write beforehand if the address is
108 * not word aligned*/
109 lcd_write_value16(*p_bytes);
110 count--;p_bytes++;
112 /* from here, 32bit transfers are save */
113 /* set it to transfer 4*(outputwidth) units at a time,
114 * if bit 12 is set it only does 2 halfwords though */
115 DBOP_CTRL |= (1<<13|1<<14);
116 lcd_delay(10);
117 data = (long*)p_bytes;
118 while (count > 1)
120 DBOP_DOUT32 = *data++;
121 count -= 2;
123 /* Wait if push fifo is full */
124 while ((DBOP_STAT & (1<<6)) != 0);
126 /* While push fifo is not empty */
127 while ((DBOP_STAT & (1<<10)) == 0);
129 /* due to the 32bit alignment requirement or uneven count,
130 * we possibly need to do a 16bit transfer at the end also */
131 if (count > 0)
132 lcd_write_value16(*(unsigned short*)data);
135 static void lcd_write_reg(int reg, int value)
137 unsigned short data = value;
139 lcd_write_cmd(reg);
140 lcd_write_value16(data);
143 /* turn the display upside down (call lcd_update() afterwards) */
144 void lcd_set_flip(bool yesno)
146 display_flipped = yesno;
147 xoffset = yesno ? 0 : 20; /* TODO: Implement flipped mode */
149 /* TODO */
153 static void _display_on(void)
155 /* Initialise in the same way as the original firmare */
157 lcd_write_reg(0x07, 0);
158 lcd_write_reg(0x13, 0);
160 lcd_write_reg(0x11, 0x3704);
161 lcd_write_reg(0x14, 0x1a1b);
162 lcd_write_reg(0x10, 0x3860);
163 lcd_write_reg(0x13, 0x40);
165 lcd_write_reg(0x13, 0x60);
167 lcd_write_reg(0x13, 0x70);
168 lcd_write_reg(0x01, 277);
169 lcd_write_reg(0x02, (7<<8));
170 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ);
171 lcd_write_reg(0x08, 0x01);
172 lcd_write_reg(0x0b, (1<<10));
173 lcd_write_reg(0x0c, 0);
175 lcd_write_reg(0x30, 0x40);
176 lcd_write_reg(0x31, 0x0687);
177 lcd_write_reg(0x32, 0x0306);
178 lcd_write_reg(0x33, 0x104);
179 lcd_write_reg(0x34, 0x0585);
180 lcd_write_reg(0x35, 255+66);
181 lcd_write_reg(0x36, 0x0687+128);
182 lcd_write_reg(0x37, 259);
183 lcd_write_reg(0x38, 0);
184 lcd_write_reg(0x39, 0);
186 lcd_write_reg(0x42, (LCD_WIDTH - 1));
187 lcd_write_reg(0x43, 0);
188 lcd_write_reg(0x44, (LCD_WIDTH - 1));
189 lcd_write_reg(0x45, 0);
190 lcd_write_reg(0x46, (((LCD_WIDTH - 1) + xoffset) << 8) | xoffset);
191 lcd_write_reg(0x47, (LCD_HEIGHT - 1));
192 lcd_write_reg(0x48, 0x0);
194 lcd_write_reg(0x07, 0x11);
195 lcd_write_reg(0x07, 0x17);
197 display_on = true; /* must be done before calling lcd_update() */
198 lcd_update();
201 #if defined(HAVE_LCD_ENABLE)
202 void lcd_enable(bool on)
204 if (display_on == on)
205 return; /* nothing to do */
206 if(on)
208 lcd_write_reg(0, 1);
209 lcd_write_reg(0x10, 0);
210 lcd_write_reg(0x11, 0x3704);
211 lcd_write_reg(0x14, 0x1a1b);
212 lcd_write_reg(0x10, 0x3860);
213 lcd_write_reg(0x13, 0x40);
214 lcd_write_reg(0x13, 0x60);
215 lcd_write_reg(0x13, 112);
216 lcd_write_reg(0x07, 0x11);
217 lcd_write_reg(0x07, 0x17);
218 display_on = true;
219 lcd_update(); /* Resync display */
220 send_event(LCD_EVENT_ACTIVATION, NULL);
221 sleep(0);
224 else
226 lcd_write_reg(0x07, 0x22);
227 lcd_write_reg(0x07, 0);
228 lcd_write_reg(0x10, 1);
229 display_on = false;
232 #endif
234 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
235 bool lcd_active(void)
237 return display_on;
239 #endif
241 /*** update functions ***/
243 /* Set horizontal window addresses */
244 static void lcd_window_x(int xmin, int xmax)
246 xmin += xoffset;
247 xmax += xoffset;
248 lcd_write_reg(0x46, (xmax << 8) | xmin);
249 lcd_write_reg(0x20, xmin);
252 /* Set vertical window addresses */
253 static void lcd_window_y(int ymin, int ymax)
255 lcd_write_reg(0x47, ymax);
256 lcd_write_reg(0x48, ymin);
257 lcd_write_reg(0x21, ymin);
260 void lcd_yuv_set_options(unsigned options)
262 lcd_yuv_options = options;
265 /* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */
266 extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
267 int width,
268 int stride);
269 extern void lcd_write_yuv420_lines_odither(unsigned char const * const src[3],
270 int width,
271 int stride,
272 int x_screen, /* To align dither */
273 int y_screen); /* pattern */
274 /* Performance function to blit a YUV bitmap directly to the LCD */
275 void lcd_blit_yuv(unsigned char * const src[3],
276 int src_x, int src_y, int stride,
277 int x, int y, int width, int height)
279 unsigned char const * yuv_src[3];
280 off_t z;
282 lcd_busy = true;
284 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_VERT);
286 /* Sorry, but width and height must be >= 2 or else */
287 width &= ~1;
288 height >>= 1;
290 z = stride*src_y;
291 yuv_src[0] = src[0] + z + src_x;
292 yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
293 yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
295 lcd_window_x(x, x + width - 1);
297 if (lcd_yuv_options & LCD_YUV_DITHER)
301 lcd_window_y(y, y + 1);
302 /* Start write to GRAM */
303 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
305 lcd_write_yuv420_lines_odither(yuv_src, width, stride, x, y);
306 yuv_src[0] += stride << 1; /* Skip down two luma lines */
307 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
308 yuv_src[2] += stride >> 1;
309 y += 2;
311 while (--height > 0);
313 else
317 lcd_window_y(y, y + 1);
318 /* Start write to GRAM */
319 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
321 lcd_write_yuv420_lines(yuv_src, width, stride);
322 yuv_src[0] += stride << 1; /* Skip down two luma lines */
323 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
324 yuv_src[2] += stride >> 1;
325 y += 2;
327 while (--height > 0);
330 lcd_busy = false;
333 void lcd_init_device()
335 as3525_dbop_init();
337 GPIOA_DIR |= (1<<5|1<<4|1<<3);
338 GPIOA_PIN(5) = 0;
339 GPIOA_PIN(3) = (1<<3);
340 GPIOA_PIN(4) = 0;
341 GPIOA_PIN(5) = (1<<5);
343 _display_on();
346 /* Update the display.
347 This must be called after all other LCD functions that change the display. */
348 void lcd_update(void)
350 if (!display_on)
351 return;
353 lcd_busy = true;
355 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ);
357 lcd_window_x(0, LCD_WIDTH - 1);
358 lcd_window_y(0, LCD_HEIGHT - 1);
360 /* Start write to GRAM */
361 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
363 /* Write data */
364 lcd_write_data((unsigned short *)lcd_framebuffer, LCD_WIDTH*LCD_HEIGHT);
365 lcd_busy = false;
368 /* Update a fraction of the display. */
369 void lcd_update_rect(int x, int y, int width, int height)
371 int xmax, ymax;
372 const fb_data *ptr;
374 if (!display_on)
375 return;
378 xmax = x + width;
379 if (xmax >= LCD_WIDTH)
380 xmax = LCD_WIDTH - 1; /* Clip right */
381 if (x < 0)
382 x = 0; /* Clip left */
383 if (x >= xmax)
384 return; /* nothing left to do */
386 width = xmax - x + 1; /* Fix width */
388 ymax = y + height;
389 if (ymax >= LCD_HEIGHT)
390 ymax = LCD_HEIGHT - 1; /* Clip bottom */
391 if (y < 0)
392 y = 0; /* Clip top */
393 if (y >= ymax)
394 return; /* nothing left to do */
396 lcd_busy = true;
398 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ);
400 lcd_window_x(x, xmax);
401 lcd_window_y(y, ymax);
403 /* Start write to GRAM */
404 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
406 ptr = &lcd_framebuffer[y][x];
408 height = ymax - y; /* fix height */
411 lcd_write_data(ptr, width);
412 ptr += LCD_WIDTH;
414 while (--height >= 0);
415 lcd_busy = false;
418 /* writes one read pixel outside the visible area, needed for correct dbop reads */
419 bool lcd_button_support(void)
421 fb_data data = 0xf<<12;
422 if (lcd_busy)
423 return false;
424 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ);
425 /* Set start position and window */
427 lcd_window_x(-1, 0);
428 lcd_window_y(-1, 0);
429 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
431 lcd_write_value16(data);
433 return true;