1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
30 #include "clock-target.h"
32 /* The controller is unknown, but some registers appear to be the same as the
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;
58 /* short count: 16 | output data width: 16 | readstrobe line */
59 DBOP_CTRL
= (1<<18|1<<12|1<<3);
64 DBOP_TIMPOL_23
= 0x6000e;
65 /* short count: 16|enable write|output data width: 16|read strobe line */
66 DBOP_CTRL
= (1<<18|1<<16|1<<12|1<<3);
67 DBOP_TIMPOL_01
= 0x6e167;
68 DBOP_TIMPOL_23
= 0xa167e06f;
70 /* TODO: The OF calls some other functions here, but maybe not important */
73 #define lcd_write_single_data16(value) do {\
74 DBOP_CTRL &= ~(1<<14|1<<13); \
75 DBOP_DOUT16 = (fb_data)(value); \
79 static void lcd_write_cmd(int cmd
)
84 DBOP_TIMPOL_23
= 0xa167006e;
85 lcd_write_single_data16(cmd
);
87 /* Wait for fifo to empty */
88 while ((DBOP_STAT
& (1<<10)) == 0);
90 /* This loop is unique to the Fuze */
93 asm volatile ("nop\n");
97 DBOP_TIMPOL_23
= 0xa167e06f;
100 void lcd_write_data(const fb_data
* p_bytes
, int count
)
103 if ((int)p_bytes
& 0x3)
104 { /* need to do a single 16bit write beforehand if the address is
106 lcd_write_single_data16(*p_bytes
);
109 /* from here, 32bit transfers are save */
110 /* set it to transfer 4*(outputwidth) units at a time,
111 * if bit 12 is set it only does 2 halfwords though */
112 DBOP_CTRL
|= (1<<13|1<<14);
113 data
= (long*)p_bytes
;
116 DBOP_DOUT32
= *data
++;
119 /* Wait for fifo to empty */
120 /* TODO: We should normally fill the fifo until it's full
121 * instead of waiting after each word,
122 * but that causes blue lines on the display */
123 while ((DBOP_STAT
& (1<<10)) == 0);
125 /* due to the 32bit alignment requirement, we possibly need to do a
126 * 16bit transfer at the end also */
128 lcd_write_single_data16(*(fb_data
*)data
);
131 static void lcd_write_reg(int reg
, int value
)
133 unsigned short data
= value
;
136 lcd_write_single_data16(data
);
139 /* turn the display upside down (call lcd_update() afterwards) */
140 void lcd_set_flip(bool yesno
)
142 display_flipped
= yesno
;
143 xoffset
= yesno
? 0 : 20; /* TODO: Implement flipped mode */
149 static void _display_on(void)
151 /* Initialise in the same way as the original firmare */
153 lcd_write_reg(0x07, 0);
154 lcd_write_reg(0x13, 0);
156 lcd_write_reg(0x11, 0x3704);
157 lcd_write_reg(0x14, 0x1a1b);
158 lcd_write_reg(0x10, 0x3860);
159 lcd_write_reg(0x13, 0x40);
161 lcd_write_reg(0x13, 0x60);
163 lcd_write_reg(0x13, 0x70);
164 lcd_write_reg(0x01, 277);
165 lcd_write_reg(0x02, (7<<8));
166 lcd_write_reg(R_ENTRY_MODE
, R_ENTRY_MODE_HORZ
);
167 lcd_write_reg(0x08, 0x01);
168 lcd_write_reg(0x0b, (1<<10));
169 lcd_write_reg(0x0c, 0);
171 lcd_write_reg(0x30, 0x40);
172 lcd_write_reg(0x31, 0x0687);
173 lcd_write_reg(0x32, 0x0306);
174 lcd_write_reg(0x33, 0x104);
175 lcd_write_reg(0x34, 0x0585);
176 lcd_write_reg(0x35, 255+66);
177 lcd_write_reg(0x36, 0x0687+128);
178 lcd_write_reg(0x37, 259);
179 lcd_write_reg(0x38, 0);
180 lcd_write_reg(0x39, 0);
182 lcd_write_reg(0x42, (LCD_WIDTH
- 1));
183 lcd_write_reg(0x43, 0);
184 lcd_write_reg(0x44, (LCD_WIDTH
- 1));
185 lcd_write_reg(0x45, 0);
186 lcd_write_reg(0x46, (((LCD_WIDTH
- 1) + xoffset
) << 8) | xoffset
);
187 lcd_write_reg(0x47, (LCD_HEIGHT
- 1));
188 lcd_write_reg(0x48, 0x0);
190 lcd_write_reg(0x07, 0x11);
191 lcd_write_reg(0x07, 0x17);
193 display_on
= true; /* must be done before calling lcd_update() */
197 #if defined(HAVE_LCD_ENABLE)
198 void lcd_enable(bool on
)
200 if (display_on
== on
)
201 return; /* nothing to do */
205 lcd_write_reg(0x10, 0);
206 lcd_write_reg(0x11, 0x3704);
207 lcd_write_reg(0x14, 0x1a1b);
208 lcd_write_reg(0x10, 0x3860);
209 lcd_write_reg(0x13, 0x40);
210 lcd_write_reg(0x13, 0x60);
211 lcd_write_reg(0x13, 112);
212 lcd_write_reg(0x07, 0x11);
213 lcd_write_reg(0x07, 0x17);
215 lcd_update(); /* Resync display */
216 lcd_activation_call_hook();
222 lcd_write_reg(0x07, 0x22);
223 lcd_write_reg(0x07, 0);
224 lcd_write_reg(0x10, 1);
230 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
231 bool lcd_active(void)
237 /*** update functions ***/
239 /* Set horizontal window addresses */
240 static void lcd_window_x(int xmin
, int xmax
)
244 lcd_write_reg(0x46, (xmax
<< 8) | xmin
);
245 lcd_write_reg(0x20, xmin
);
248 /* Set vertical window addresses */
249 static void lcd_window_y(int ymin
, int ymax
)
251 lcd_write_reg(0x47, ymax
);
252 lcd_write_reg(0x48, ymin
);
253 lcd_write_reg(0x21, ymin
);
256 void lcd_yuv_set_options(unsigned options
)
258 lcd_yuv_options
= options
;
261 /* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */
262 extern void lcd_write_yuv420_lines(unsigned char const * const src
[3],
265 extern void lcd_write_yuv420_lines_odither(unsigned char const * const src
[3],
268 int x_screen
, /* To align dither */
269 int y_screen
); /* pattern */
270 /* Performance function to blit a YUV bitmap directly to the LCD */
271 void lcd_blit_yuv(unsigned char * const src
[3],
272 int src_x
, int src_y
, int stride
,
273 int x
, int y
, int width
, int height
)
275 unsigned char const * yuv_src
[3];
280 lcd_write_reg(R_ENTRY_MODE
, R_ENTRY_MODE_VERT
);
282 /* Sorry, but width and height must be >= 2 or else */
287 yuv_src
[0] = src
[0] + z
+ src_x
;
288 yuv_src
[1] = src
[1] + (z
>> 2) + (src_x
>> 1);
289 yuv_src
[2] = src
[2] + (yuv_src
[1] - src
[1]);
291 lcd_window_x(x
, x
+ width
- 1);
293 if (lcd_yuv_options
& LCD_YUV_DITHER
)
297 lcd_window_y(y
, y
+ 1);
298 /* Start write to GRAM */
299 lcd_write_cmd(R_WRITE_DATA_2_GRAM
);
301 lcd_write_yuv420_lines_odither(yuv_src
, width
, stride
, x
, y
);
302 yuv_src
[0] += stride
<< 1; /* Skip down two luma lines */
303 yuv_src
[1] += stride
>> 1; /* Skip down one chroma line */
304 yuv_src
[2] += stride
>> 1;
307 while (--height
> 0);
313 lcd_window_y(y
, y
+ 1);
314 /* Start write to GRAM */
315 lcd_write_cmd(R_WRITE_DATA_2_GRAM
);
317 lcd_write_yuv420_lines(yuv_src
, width
, stride
);
318 yuv_src
[0] += stride
<< 1; /* Skip down two luma lines */
319 yuv_src
[1] += stride
>> 1; /* Skip down one chroma line */
320 yuv_src
[2] += stride
>> 1;
323 while (--height
> 0);
329 void lcd_init_device()
333 GPIOA_DIR
|= (1<<5|1<<4|1<<3);
335 GPIOA_PIN(3) = (1<<3);
337 GPIOA_PIN(5) = (1<<5);
342 /* Update the display.
343 This must be called after all other LCD functions that change the display. */
344 void lcd_update(void)
348 lcd_write_reg(R_ENTRY_MODE
, R_ENTRY_MODE_HORZ
);
351 lcd_window_x(0, LCD_WIDTH
- 1);
352 lcd_window_y(0, LCD_HEIGHT
- 1);
354 /* Start write to GRAM */
355 lcd_write_cmd(R_WRITE_DATA_2_GRAM
);
358 lcd_write_data((unsigned short *)lcd_framebuffer
, LCD_WIDTH
*LCD_HEIGHT
);
362 /* Update a fraction of the display. */
363 void lcd_update_rect(int x
, int y
, int width
, int height
)
373 if (xmax
>= LCD_WIDTH
)
374 xmax
= LCD_WIDTH
- 1; /* Clip right */
376 x
= 0; /* Clip left */
378 return; /* nothing left to do */
380 width
= xmax
- x
+ 1; /* Fix width */
383 if (ymax
>= LCD_HEIGHT
)
384 ymax
= LCD_HEIGHT
- 1; /* Clip bottom */
386 y
= 0; /* Clip top */
388 return; /* nothing left to do */
390 lcd_write_reg(R_ENTRY_MODE
, R_ENTRY_MODE_HORZ
);
393 lcd_window_x(x
, xmax
);
394 lcd_window_y(y
, ymax
);
396 /* Start write to GRAM */
397 lcd_write_cmd(R_WRITE_DATA_2_GRAM
);
399 ptr
= &lcd_framebuffer
[y
][x
];
403 lcd_write_data(ptr
, width
);
410 /* writes one read pixel outside the visible area, needed for correct dbop reads */
411 bool lcd_button_support(void)
413 fb_data data
= 0xf<<12;
416 lcd_write_reg(R_ENTRY_MODE
, R_ENTRY_MODE_HORZ
);
417 /* Set start position and window */
421 lcd_write_cmd(R_WRITE_DATA_2_GRAM
);
423 lcd_write_single_data16(data
);