1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Rockbox driver for iPod LCDs
12 * Based on code from the ipodlinux project - http://ipodlinux.org/
13 * Adapted for Rockbox in November 2005
15 * Original file: linux/arch/armnommu/mach-ipod/fb.c
17 * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org)
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation; either version 2
22 * of the License, or (at your option) any later version.
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
25 * KIND, either express or implied.
27 ****************************************************************************/
36 #define SWAP_INT(X,Y) {int tmp=X; X=Y; Y=tmp;}
38 /* LCD command codes for HD66789R */
39 #define LCD_CNTL_RAM_ADDR_SET 0x21
40 #define LCD_CNTL_WRITE_TO_GRAM 0x22
41 #define LCD_CNTL_HORIZ_RAM_ADDR_POS 0x44
42 #define LCD_CNTL_VERT_RAM_ADDR_POS 0x45
45 int lcd_type
= 1; /* 0 = "old" Color/Photo, 1 = "new" Color & Nano */
47 static inline void lcd_wait_write(void)
49 while (LCD2_PORT
& LCD2_BUSY_MASK
);
52 static void lcd_cmd_data(unsigned cmd
, unsigned data
)
54 if (lcd_type
== 0) { /* 16 bit transfers */
56 LCD2_PORT
= LCD2_CMD_MASK
| cmd
;
58 LCD2_PORT
= LCD2_CMD_MASK
| data
;
61 LCD2_PORT
= LCD2_CMD_MASK
;
62 LCD2_PORT
= LCD2_CMD_MASK
| cmd
;
64 LCD2_PORT
= LCD2_DATA_MASK
| (data
>> 8);
65 LCD2_PORT
= LCD2_DATA_MASK
| (data
& 0xff);
69 /*** hardware configuration ***/
71 void lcd_set_contrast(int val
)
73 /* TODO: Implement lcd_set_contrast() */
77 void lcd_set_invert_display(bool yesno
)
79 /* TODO: Implement lcd_set_invert_display() */
83 /* turn the display upside down (call lcd_update() afterwards) */
84 void lcd_set_flip(bool yesno
)
86 /* TODO: Implement lcd_set_flip() */
91 void lcd_init_device(void)
93 #if CONFIG_LCD == LCD_IPODCOLOR
94 if (IPOD_HW_REVISION
== 0x60000) {
97 int gpio_a01
, gpio_a04
;
100 gpio_a01
= (GPIOA_INPUT_VAL
& 0x2) >> 1;
102 gpio_a04
= (GPIOA_INPUT_VAL
& 0x10) >> 4;
104 if (((gpio_a01
<< 1) | gpio_a04
) == 0 || ((gpio_a01
<< 1) | gpio_a04
) == 2) {
111 lcd_cmd_data(0xef, 0x0);
112 lcd_cmd_data(0x1, 0x0);
113 lcd_cmd_data(0x80, 0x1);
114 lcd_cmd_data(0x10, 0xc);
115 lcd_cmd_data(0x18, 0x6);
116 lcd_cmd_data(0x7e, 0x4);
117 lcd_cmd_data(0x7e, 0x5);
118 lcd_cmd_data(0x7f, 0x1);
121 #elif CONFIG_LCD == LCD_IPODNANO
122 /* iPodLinux doesn't appear have any LCD init code for the Nano */
126 /* Helper function to set up drawing region and start drawing */
127 static void lcd_setup_drawing_region(int x
, int y
, int width
, int height
)
131 /* calculate the drawing region */
132 #if CONFIG_LCD == LCD_IPODNANO
133 y0
= x
; /* start horiz */
134 x0
= y
; /* start vert */
135 y1
= (x
+ width
) - 1; /* max horiz */
136 x1
= (y
+ height
) - 1; /* max vert */
137 #elif CONFIG_LCD == LCD_IPODCOLOR
138 y0
= y
; /* start vert */
139 x0
= (LCD_WIDTH
- 1) - x
; /* start horiz */
140 y1
= (y
+ height
) - 1; /* end vert */
141 x1
= (x0
- width
) + 1; /* end horiz */
144 /* setup the drawing region */
146 lcd_cmd_data(0x12, y0
); /* start vert */
147 lcd_cmd_data(0x13, x0
); /* start horiz */
148 lcd_cmd_data(0x15, y1
); /* end vert */
149 lcd_cmd_data(0x16, x1
); /* end horiz */
151 if (y1
< y0
) SWAP_INT(y0
,y1
) /* swap max horiz < start horiz */
152 if (x1
< x0
) SWAP_INT(x0
,x1
) /* swap max vert < start vert */
154 /* max horiz << 8 | start horiz */
155 lcd_cmd_data(LCD_CNTL_HORIZ_RAM_ADDR_POS
, (y1
<< 8) | y0
);
156 /* max vert << 8 | start vert */
157 lcd_cmd_data(LCD_CNTL_VERT_RAM_ADDR_POS
, (x1
<< 8) | x0
);
159 /* start vert = max vert */
160 #if CONFIG_LCD == LCD_IPODCOLOR
164 /* position cursor (set AD0-AD15) */
165 /* start vert << 8 | start horiz */
166 lcd_cmd_data(LCD_CNTL_RAM_ADDR_SET
, ((x0
<< 8) | y0
));
170 LCD2_PORT
= LCD2_CMD_MASK
;
171 LCD2_PORT
= (LCD2_CMD_MASK
|LCD_CNTL_WRITE_TO_GRAM
);
175 /* Line write helper function for lcd_yuv_blit. Writes two lines of yuv420. */
176 extern void lcd_write_yuv420_lines(unsigned char const * const src
[3],
177 const unsigned int lcd_baseadress
,
181 /* Performance function to blit a YUV bitmap directly to the LCD */
182 void lcd_blit_yuv(unsigned char * const src
[3],
183 int src_x
, int src_y
, int stride
,
184 int x
, int y
, int width
, int height
)
187 unsigned char const * yuv_src
[3];
189 width
= (width
+ 1) & ~1; /* ensure width is even */
190 height
= (height
+ 1) & ~1; /* ensure height is even */
192 lcd_setup_drawing_region(x
, y
, width
, height
);
195 yuv_src
[0] = src
[0] + z
+ src_x
;
196 yuv_src
[1] = src
[1] + (z
>> 2) + (src_x
>> 1);
197 yuv_src
[2] = src
[2] + (yuv_src
[1] - src
[1]);
200 int r
, h
, pixels_to_write
;
202 pixels_to_write
= (width
* height
) * 2;
205 /* calculate how much we can do in one go */
206 if (pixels_to_write
> 0x10000) {
207 h
= ((0x10000/2) / width
) & ~1; /* ensure h is even */
208 pixels_to_write
= (width
* h
) * 2;
211 LCD2_BLOCK_CTRL
= 0x10000080;
212 LCD2_BLOCK_CONFIG
= 0xc0010000 | (pixels_to_write
- 1);
213 LCD2_BLOCK_CTRL
= 0x34000000;
215 r
= h
>>1; /* lcd_write_yuv420_lines writes two lines at once */
217 lcd_write_yuv420_lines(yuv_src
, LCD2_BASE
, width
, stride
);
218 yuv_src
[0] += stride
<< 1;
219 yuv_src
[1] += stride
>> 1;
220 yuv_src
[2] += stride
>> 1;
223 /* transfer of pixels_to_write bytes finished */
224 while (!(LCD2_BLOCK_CTRL
& LCD2_BLOCK_READY
));
225 LCD2_BLOCK_CONFIG
= 0;
231 /* Helper function writes 'count' consecutive pixels from src to LCD IF */
232 static void lcd_write_line(int count
, unsigned long *src
)
235 while (!(LCD2_BLOCK_CTRL
& LCD2_BLOCK_TXOK
)); /* FIFO wait */
236 LCD2_BLOCK_DATA
= *src
++; /* output 2 pixels */
241 /* Update a fraction of the display. */
242 void lcd_update_rect(int x
, int y
, int width
, int height
)
246 /* Ensure both x and width are even to be able to read 32-bit aligned
247 * data from lcd_framebuffer */
248 x
= x
& ~1; /* use the smaller even number */
249 width
= (width
+ 1) & ~1; /* use the bigger even number */
251 lcd_setup_drawing_region(x
, y
, width
, height
);
253 addr
= (unsigned long*)&lcd_framebuffer
[y
][x
];
256 int r
, h
, pixels_to_write
;
258 pixels_to_write
= (width
* height
) * 2;
261 /* calculate how much we can do in one go */
262 if (pixels_to_write
> 0x10000) {
263 h
= ((0x10000/2) / width
) & ~1; /* ensure h is even */
264 pixels_to_write
= (width
* h
) * 2;
267 LCD2_BLOCK_CTRL
= 0x10000080;
268 LCD2_BLOCK_CONFIG
= 0xc0010000 | (pixels_to_write
- 1);
269 LCD2_BLOCK_CTRL
= 0x34000000;
271 if (LCD_WIDTH
== width
) {
272 /* for each row and column in a single call */
273 lcd_write_line(h
*width
, addr
);
274 addr
+= LCD_WIDTH
/2*h
;
277 for (r
= 0; r
< h
; r
++) {
278 lcd_write_line(width
, addr
);
283 /* transfer of pixels_to_write bytes finished */
284 while (!(LCD2_BLOCK_CTRL
& LCD2_BLOCK_READY
));
285 LCD2_BLOCK_CONFIG
= 0;
291 /* Update the display.
292 This must be called after all other LCD functions that change the display. */
293 void lcd_update(void)
295 lcd_update_rect(0, 0, LCD_WIDTH
, LCD_HEIGHT
);