Submit FS#11843 v17. Integrate YUV-blitting of nano 2G to nano1G/color LCD driver...
[kugel-rb.git] / firmware / target / arm / ipod / lcd-color_nano.c
blob8cff7bda21a129023d6f627f6be850bc6563d21a
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
28 #include "config.h"
29 #include "cpu.h"
30 #include "lcd.h"
31 #include "kernel.h"
32 #include "system.h"
33 #include "hwcompat.h"
35 /*** macros ***/
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
44 /*** globals ***/
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 */
55 lcd_wait_write();
56 LCD2_PORT = LCD2_CMD_MASK | cmd;
57 lcd_wait_write();
58 LCD2_PORT = LCD2_CMD_MASK | data;
59 } else {
60 lcd_wait_write();
61 LCD2_PORT = LCD2_CMD_MASK;
62 LCD2_PORT = LCD2_CMD_MASK | cmd;
63 lcd_wait_write();
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() */
74 (void)val;
77 void lcd_set_invert_display(bool yesno)
79 /* TODO: Implement lcd_set_invert_display() */
80 (void)yesno;
83 /* turn the display upside down (call lcd_update() afterwards) */
84 void lcd_set_flip(bool yesno)
86 /* TODO: Implement lcd_set_flip() */
87 (void)yesno;
90 /* LCD init */
91 void lcd_init_device(void)
93 #if CONFIG_LCD == LCD_IPODCOLOR
94 if (IPOD_HW_REVISION == 0x60000) {
95 lcd_type = 0;
96 } else {
97 int gpio_a01, gpio_a04;
99 /* A01 */
100 gpio_a01 = (GPIOA_INPUT_VAL & 0x2) >> 1;
101 /* A04 */
102 gpio_a04 = (GPIOA_INPUT_VAL & 0x10) >> 4;
104 if (((gpio_a01 << 1) | gpio_a04) == 0 || ((gpio_a01 << 1) | gpio_a04) == 2) {
105 lcd_type = 0;
106 } else {
107 lcd_type = 1;
110 if (lcd_type == 0) {
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 */
123 #endif
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)
129 int y0, x0, y1, x1;
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 */
142 #endif
144 /* setup the drawing region */
145 if (lcd_type == 0) {
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 */
150 } else {
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
161 x0 = x1;
162 #endif
164 /* position cursor (set AD0-AD15) */
165 /* start vert << 8 | start horiz */
166 lcd_cmd_data(LCD_CNTL_RAM_ADDR_SET, ((x0 << 8) | y0));
168 /* start drawing */
169 lcd_wait_write();
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,
178 int width,
179 int stride);
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)
186 int z;
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);
194 z = stride * src_y;
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]);
199 while (height > 0) {
200 int r, h, pixels_to_write;
202 pixels_to_write = (width * height) * 2;
203 h = height;
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 */
216 do {
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;
221 } while (--r > 0);
223 /* transfer of pixels_to_write bytes finished */
224 while (!(LCD2_BLOCK_CTRL & LCD2_BLOCK_READY));
225 LCD2_BLOCK_CONFIG = 0;
227 height -= h;
231 /* Helper function writes 'count' consecutive pixels from src to LCD IF */
232 static void lcd_write_line(int count, unsigned long *src)
234 do {
235 while (!(LCD2_BLOCK_CTRL & LCD2_BLOCK_TXOK)); /* FIFO wait */
236 LCD2_BLOCK_DATA = *src++; /* output 2 pixels */
237 count -= 2;
238 } while (count > 0);
241 /* Update a fraction of the display. */
242 void lcd_update_rect(int x, int y, int width, int height)
244 unsigned long *addr;
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];
255 while (height > 0) {
256 int r, h, pixels_to_write;
258 pixels_to_write = (width * height) * 2;
259 h = height;
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;
275 } else {
276 /* for each row */
277 for (r = 0; r < h; r++) {
278 lcd_write_line(width, addr);
279 addr += LCD_WIDTH/2;
283 /* transfer of pixels_to_write bytes finished */
284 while (!(LCD2_BLOCK_CTRL & LCD2_BLOCK_READY));
285 LCD2_BLOCK_CONFIG = 0;
287 height -= h;
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);