Fix FS#10486 - "fuze screen not drawn correctly on backlight turn-on if playlist...
[kugel-rb.git] / firmware / target / arm / as3525 / sansa-fuze / lcd-fuze.c
blob7291a5e748ffcfd2c3219997e9e790745badd479
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 bool lcd_busy = false;
51 static void as3525_dbop_init(void)
53 CGU_DBOP = (1<<3) | AS3525_DBOP_DIV;
55 DBOP_TIMPOL_01 = 0xe167e167;
56 DBOP_TIMPOL_23 = 0xe167006e;
57 DBOP_CTRL = 0x41008;
59 GPIOB_AFSEL = 0xfc;
60 GPIOC_AFSEL = 0xff;
62 DBOP_TIMPOL_23 = 0x6000e;
63 DBOP_CTRL = 0x51008;
64 DBOP_TIMPOL_01 = 0x6e167;
65 DBOP_TIMPOL_23 = 0xa167e06f;
67 /* TODO: The OF calls some other functions here, but maybe not important */
70 static void lcd_write_cmd(int cmd)
72 int x;
74 /* Write register */
75 DBOP_CTRL &= ~(1<<14);
77 DBOP_TIMPOL_23 = 0xa167006e;
79 DBOP_DOUT = cmd;
81 /* Wait for fifo to empty */
82 while ((DBOP_STAT & (1<<10)) == 0);
84 /* This loop is unique to the Fuze */
85 x = 0;
86 do {
87 asm volatile ("nop\n");
88 } while (x++ < 4);
91 DBOP_TIMPOL_23 = 0xa167e06f;
94 void lcd_write_data(const fb_data* p_bytes, int count)
96 while (count--)
98 DBOP_DOUT = *p_bytes++;
100 /* Wait for fifo to empty */
101 while ((DBOP_STAT & (1<<10)) == 0);
105 static void lcd_write_reg(int reg, int value)
107 unsigned short data = value;
109 lcd_write_cmd(reg);
110 lcd_write_data(&data, 1);
113 /* turn the display upside down (call lcd_update() afterwards) */
114 void lcd_set_flip(bool yesno)
116 display_flipped = yesno;
117 xoffset = yesno ? 0 : 20; /* TODO: Implement flipped mode */
119 /* TODO */
123 static void _display_on(void)
125 /* Initialise in the same way as the original firmare */
127 lcd_write_reg(0x07, 0);
128 lcd_write_reg(0x13, 0);
130 lcd_write_reg(0x11, 0x3704);
131 lcd_write_reg(0x14, 0x1a1b);
132 lcd_write_reg(0x10, 0x3860);
133 lcd_write_reg(0x13, 0x40);
135 lcd_write_reg(0x13, 0x60);
137 lcd_write_reg(0x13, 0x70);
138 lcd_write_reg(0x01, 277);
139 lcd_write_reg(0x02, (7<<8));
140 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ);
141 lcd_write_reg(0x08, 0x01);
142 lcd_write_reg(0x0b, (1<<10));
143 lcd_write_reg(0x0c, 0);
145 lcd_write_reg(0x30, 0x40);
146 lcd_write_reg(0x31, 0x0687);
147 lcd_write_reg(0x32, 0x0306);
148 lcd_write_reg(0x33, 0x104);
149 lcd_write_reg(0x34, 0x0585);
150 lcd_write_reg(0x35, 255+66);
151 lcd_write_reg(0x36, 0x0687+128);
152 lcd_write_reg(0x37, 259);
153 lcd_write_reg(0x38, 0);
154 lcd_write_reg(0x39, 0);
156 lcd_write_reg(0x42, (LCD_WIDTH - 1));
157 lcd_write_reg(0x43, 0);
158 lcd_write_reg(0x44, (LCD_WIDTH - 1));
159 lcd_write_reg(0x45, 0);
160 lcd_write_reg(0x46, (((LCD_WIDTH - 1) + xoffset) << 8) | xoffset);
161 lcd_write_reg(0x47, (LCD_HEIGHT - 1));
162 lcd_write_reg(0x48, 0x0);
164 lcd_write_reg(0x07, 0x11);
165 lcd_write_reg(0x07, 0x17);
167 display_on = true; /* must be done before calling lcd_update() */
168 lcd_update();
171 #if defined(HAVE_LCD_ENABLE)
172 void lcd_enable(bool on)
174 if (display_on == on)
175 return; /* nothing to do */
176 if(on)
178 lcd_write_reg(0, 1);
179 lcd_write_reg(0x10, 0);
180 lcd_write_reg(0x11, 0x3704);
181 lcd_write_reg(0x14, 0x1a1b);
182 lcd_write_reg(0x10, 0x3860);
183 lcd_write_reg(0x13, 0x40);
184 lcd_write_reg(0x13, 0x60);
185 lcd_write_reg(0x13, 112);
186 lcd_write_reg(0x07, 0x11);
187 lcd_write_reg(0x07, 0x17);
188 display_on = true;
189 /* a bit of delay before returning to
190 * avoid irritating flash on backlight on */
191 lcd_update(); /* Resync display */
192 lcd_activation_call_hook();
193 sleep(0);
196 else
198 lcd_write_reg(0x07, 0x22);
199 lcd_write_reg(0x07, 0);
200 lcd_write_reg(0x10, 1);
201 display_on = false;
204 #endif
206 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
207 bool lcd_active(void)
209 return display_on;
211 #endif
213 /*** update functions ***/
215 /* Set horizontal window addresses */
216 static void lcd_window_x(int xmin, int xmax)
218 xmin += xoffset;
219 xmax += xoffset;
220 lcd_write_reg(0x46, (xmax << 8) | xmin);
221 lcd_write_reg(0x20, xmin);
224 /* Set vertical window addresses */
225 static void lcd_window_y(int ymin, int ymax)
227 lcd_write_reg(0x47, ymax);
228 lcd_write_reg(0x48, ymin);
229 lcd_write_reg(0x21, ymin);
232 void lcd_yuv_set_options(unsigned options)
234 lcd_yuv_options = options;
237 /* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */
238 extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
239 int width,
240 int stride);
241 extern void lcd_write_yuv420_lines_odither(unsigned char const * const src[3],
242 int width,
243 int stride,
244 int x_screen, /* To align dither pattern */
245 int y_screen);
246 /* Performance function to blit a YUV bitmap directly to the LCD */
247 void lcd_blit_yuv(unsigned char * const src[3],
248 int src_x, int src_y, int stride,
249 int x, int y, int width, int height)
251 unsigned char const * yuv_src[3];
252 off_t z;
254 lcd_busy = true;
256 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_VERT);
258 /* Sorry, but width and height must be >= 2 or else */
259 width &= ~1;
260 height >>= 1;
262 z = stride*src_y;
263 yuv_src[0] = src[0] + z + src_x;
264 yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
265 yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
267 lcd_window_x(x, x + width - 1);
269 if (lcd_yuv_options & LCD_YUV_DITHER)
273 lcd_window_y(y, y + 1);
274 /* Start write to GRAM */
275 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
277 lcd_write_yuv420_lines_odither(yuv_src, width, stride, x, y);
278 yuv_src[0] += stride << 1; /* Skip down two luma lines */
279 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
280 yuv_src[2] += stride >> 1;
281 y += 2;
283 while (--height > 0);
285 else
289 lcd_window_y(y, y + 1);
290 /* Start write to GRAM */
291 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
293 lcd_write_yuv420_lines(yuv_src, width, stride);
294 yuv_src[0] += stride << 1; /* Skip down two luma lines */
295 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
296 yuv_src[2] += stride >> 1;
297 y += 2;
299 while (--height > 0);
302 lcd_busy = false;
305 void lcd_init_device()
307 as3525_dbop_init();
309 GPIOA_DIR |= (1<<5|1<<4|1<<3);
310 GPIOA_PIN(5) = 0;
311 GPIOA_PIN(3) = (1<<3);
312 GPIOA_PIN(4) = 0;
313 GPIOA_PIN(5) = (1<<5);
315 _display_on();
318 /* Update the display.
319 This must be called after all other LCD functions that change the display. */
320 void lcd_update(void)
322 if (!display_on)
323 return;
325 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ);
327 lcd_busy = true;
328 lcd_window_x(0, LCD_WIDTH - 1);
329 lcd_window_y(0, LCD_HEIGHT - 1);
331 /* Start write to GRAM */
332 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
334 /* Write data */
335 lcd_write_data((unsigned short *)lcd_framebuffer, LCD_WIDTH*LCD_HEIGHT);
336 lcd_busy = false;
339 /* Update a fraction of the display. */
340 void lcd_update_rect(int x, int y, int width, int height)
342 int xmax, ymax;
343 const unsigned short *ptr;
345 if (!display_on)
346 return;
348 xmax = x + width;
349 if (xmax >= LCD_WIDTH)
350 xmax = LCD_WIDTH - 1; /* Clip right */
351 if (x < 0)
352 x = 0; /* Clip left */
353 if (x >= xmax)
354 return; /* nothing left to do */
356 width = xmax - x + 1; /* Fix width */
358 ymax = y + height;
359 if (ymax >= LCD_HEIGHT)
360 ymax = LCD_HEIGHT - 1; /* Clip bottom */
361 if (y < 0)
362 y = 0; /* Clip top */
363 if (y >= ymax)
364 return; /* nothing left to do */
366 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ);
368 lcd_busy = true;
369 lcd_window_x(x, xmax);
370 lcd_window_y(y, ymax);
372 /* Start write to GRAM */
373 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
375 ptr = (unsigned short *)&lcd_framebuffer[y][x];
379 lcd_write_data(ptr, width);
380 ptr += LCD_WIDTH;
382 while (++y <= ymax);
383 lcd_busy = false;
386 /* writes one read pixel outside the visible area, needed for correct dbop reads */
387 bool lcd_button_support(void)
389 fb_data data = 0xf<<12;
390 if (lcd_busy)
391 return false;
392 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ);
393 /* Set start position and window */
395 lcd_window_x(-1, 0);
396 lcd_window_y(-1, 0);
397 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
399 lcd_write_data(&data, 1);
401 return true;