* Use proper definitions for the PP (mono) LCD bridge. * Clean up pp5002.h a bit.
[Rockbox.git] / firmware / target / arm / ipod / lcd-gray.c
blobbea1ed46ed1f9d5f6bab5b1029ec64f64ab501ba
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 * All files in this archive are subject to the GNU General Public License.
20 * See the file COPYING in the source tree root for full license agreement.
22 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
23 * KIND, either express or implied.
25 ****************************************************************************/
26 #include "config.h"
27 #include "cpu.h"
28 #include "lcd.h"
29 #include "kernel.h"
30 #include "system.h"
31 #include "hwcompat.h"
33 /* LCD command codes for HD66753 */
35 #define R_START_OSC 0x00
36 #define R_DRV_OUTPUT_CONTROL 0x01
37 #define R_DRV_WAVEFORM_CONTROL 0x02
38 #define R_POWER_CONTROL 0x03
39 #define R_CONTRAST_CONTROL 0x04
40 #define R_ENTRY_MODE 0x05
41 #define R_ROTATION 0x06
42 #define R_DISPLAY_CONTROL 0x07
43 #define R_CURSOR_CONTROL 0x08
44 #define R_HORIZONTAL_CURSOR_POS 0x0b
45 #define R_VERTICAL_CURSOR_POS 0x0c
46 #define R_1ST_SCR_DRV_POS 0x0d
47 #define R_2ND_SCR_DRV_POS 0x0e
48 #define R_RAM_WRITE_MASK 0x10
49 #define R_RAM_ADDR_SET 0x11
50 #define R_RAM_DATA 0x12
52 #ifdef HAVE_BACKLIGHT_INVERSION
53 /* The backlight makes the LCD appear negative on the 1st/2nd gen */
54 static bool lcd_inverted = false;
55 static bool lcd_backlit = false;
56 static void invert_display(void);
57 #endif
59 #if defined(IPOD_1G2G) || defined(IPOD_3G)
60 static unsigned short power_reg_h;
61 #define POWER_REG_H power_reg_h
62 #else
63 #define POWER_REG_H 0x1200
64 #endif
66 #ifdef IPOD_1G2G
67 static unsigned short contrast_reg_h;
68 #define CONTRAST_REG_H contrast_reg_h
69 #else
70 #define CONTRAST_REG_H 0x400
71 #endif
73 /* needed for flip */
74 static int addr_offset;
75 #if defined(IPOD_MINI) || defined(IPOD_MINI2G)
76 static int pix_offset;
77 #endif
79 static const unsigned char dibits[16] ICONST_ATTR = {
80 0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F,
81 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF
84 /* wait for LCD with timeout */
85 static inline void lcd_wait_write(void)
87 while (LCD1_BASE & LCD1_BUSY_MASK);
90 /* send LCD data */
91 #if CONFIG_CPU == PP5002
92 STATICIRAM void ICODE_ATTR lcd_send_data(unsigned data)
93 #else
94 static void lcd_send_data(unsigned data)
95 #endif
97 lcd_wait_write();
98 #ifdef IPOD_MINI2G
99 LCD1_CMD = data | 0x760000;
100 #else
101 LCD1_DATA = data >> 8;
102 lcd_wait_write();
103 LCD1_DATA = data & 0xff;
104 #endif
107 /* send LCD command */
108 static void lcd_prepare_cmd(unsigned cmd)
110 lcd_wait_write();
111 #ifdef IPOD_MINI2G
112 LCD1_CMD = cmd | 0x740000;
113 #else
114 LCD1_CMD = 0;
115 lcd_wait_write();
116 LCD1_CMD = cmd;
117 #endif
120 /* send LCD command and data */
121 static void lcd_cmd_and_data(unsigned cmd, unsigned data)
123 lcd_prepare_cmd(cmd);
124 lcd_send_data(data);
127 /* LCD init */
128 void lcd_init_device(void)
130 #ifdef IPOD_1G2G
131 if ((IPOD_HW_REVISION >> 16) == 1)
133 power_reg_h = 0x1500;
134 contrast_reg_h = 0x700;
136 else /* 2nd gen */
138 if (inl(0xcf00404c) & 0x01) /* check bit 0 */
140 power_reg_h = 0x1520; /* Set step-up frequency to f/8 instead of
141 * f/32, for better blacklevel stability */
142 contrast_reg_h = 0x400;
144 else
146 power_reg_h = 0x1100;
147 contrast_reg_h = 0x300;
150 #elif defined IPOD_3G
151 if (inl(0xcf00404c) & 0x01) /* check bit 0 */
152 power_reg_h = 0x1520; /* Set step-up frequency to f/8 instead of
153 * f/32, for better blacklevel stability */
154 else
155 power_reg_h = 0x1100;
156 #elif defined IPOD_MINI2G
157 lcd_wait_write();
158 LCD1_BASE = (LCD1_BASE & ~0x1f00000) | 0x1700000;
159 #endif
161 lcd_cmd_and_data(R_POWER_CONTROL, POWER_REG_H | 0xc);
162 #ifdef HAVE_BACKLIGHT_INVERSION
163 invert_display();
164 #else
165 lcd_cmd_and_data(R_DISPLAY_CONTROL, 0x0015);
166 #endif
167 lcd_set_flip(false);
168 lcd_cmd_and_data(R_ENTRY_MODE, 0x0000);
170 #ifdef IPOD_4G
171 GPIOB_ENABLE |= 0x4; /* B02 enable */
172 GPIOB_ENABLE |= 0x8; /* B03 enable */
173 outl(inl(0x70000084) | 0x2000000, 0x70000084); /* D01 enable */
174 outl(inl(0x70000080) | 0x2000000, 0x70000080); /* D01 =1 */
176 DEV_EN |= 0x20000; /* PWM enable */
177 #endif
180 /*** hardware configuration ***/
182 int lcd_default_contrast(void)
184 #ifdef IPOD_1G2G
185 return 28;
186 #elif defined(IPOD_MINI) || defined(IPOD_MINI2G) || defined(IPOD_3G)
187 return 42;
188 #elif defined(IPOD_4G)
189 return 35;
190 #endif
193 /* Rockbox stores the contrast as 0..63 - we add 64 to it */
194 void lcd_set_contrast(int val)
196 if (val < 0) val = 0;
197 else if (val > 63) val = 63;
199 lcd_cmd_and_data(R_CONTRAST_CONTROL, CONTRAST_REG_H | (val + 64));
202 #ifdef HAVE_BACKLIGHT_INVERSION
203 static void invert_display(void)
205 if (lcd_inverted ^ lcd_backlit)
206 lcd_cmd_and_data(R_DISPLAY_CONTROL, 0x0017);
207 else
208 lcd_cmd_and_data(R_DISPLAY_CONTROL, 0x0015);
211 void lcd_set_invert_display(bool yesno)
213 lcd_inverted = yesno;
214 invert_display();
217 void lcd_set_backlight_inversion(bool yesno)
219 lcd_backlit = yesno;
220 invert_display();
222 #else
223 void lcd_set_invert_display(bool yesno)
225 if (yesno)
226 lcd_cmd_and_data(R_DISPLAY_CONTROL, 0x0017);
227 else
228 lcd_cmd_and_data(R_DISPLAY_CONTROL, 0x0015);
230 #endif
232 /* turn the display upside down (call lcd_update() afterwards) */
233 void lcd_set_flip(bool yesno)
235 #if defined(IPOD_MINI) || defined(IPOD_MINI2G)
236 if (yesno) {
237 /* 168x112, inverse COM order */
238 lcd_cmd_and_data(R_DRV_OUTPUT_CONTROL, 0x020d);
239 lcd_cmd_and_data(R_1ST_SCR_DRV_POS, 0x8316); /* 22..131 */
240 addr_offset = (22 << 5) | (20 - 4);
241 pix_offset = -2;
242 } else {
243 /* 168x112, inverse SEG order */
244 lcd_cmd_and_data(R_DRV_OUTPUT_CONTROL, 0x010d);
245 lcd_cmd_and_data(R_1ST_SCR_DRV_POS, 0x6d00); /* 0..109 */
246 addr_offset = 20;
247 pix_offset = 0;
249 #else
250 if (yesno) {
251 /* 168x128, inverse SEG & COM order */
252 lcd_cmd_and_data(R_DRV_OUTPUT_CONTROL, 0x030f);
253 lcd_cmd_and_data(R_1ST_SCR_DRV_POS, 0x8304); /* 4..131 */
254 addr_offset = (4 << 5) | (20 - 1);
255 } else {
256 /* 168x128 */
257 lcd_cmd_and_data(R_DRV_OUTPUT_CONTROL, 0x000f);
258 lcd_cmd_and_data(R_1ST_SCR_DRV_POS, 0x7f00); /* 0..127 */
259 addr_offset = 20;
261 #endif
264 void lcd_enable(bool on)
266 if (on)
268 lcd_cmd_and_data(R_START_OSC, 1); /* start oscillation */
269 sleep(HZ/10); /* wait 10ms */
270 lcd_cmd_and_data(R_POWER_CONTROL, POWER_REG_H); /*clear standby mode */
271 lcd_cmd_and_data(R_POWER_CONTROL, POWER_REG_H | 0xc);
272 /* enable opamp & booster */
274 else
276 lcd_cmd_and_data(R_POWER_CONTROL, POWER_REG_H);
277 /* switch off opamp & booster */
278 lcd_cmd_and_data(R_POWER_CONTROL, POWER_REG_H | 0x1);
279 /* enter standby mode */
283 /*** update functions ***/
285 /* Performance function that works with an external buffer
286 note that x, bwidtht and stride are in 8-pixel units! */
287 void lcd_blit(const unsigned char* data, int bx, int y, int bwidth,
288 int height, int stride)
290 const unsigned char *src, *src_end;
292 while (height--) {
293 src = data;
294 src_end = data + bwidth;
295 lcd_cmd_and_data(R_RAM_ADDR_SET, (y++ << 5) + addr_offset - bx);
296 lcd_prepare_cmd(R_RAM_DATA);
297 do {
298 unsigned byte = *src++;
299 lcd_send_data((dibits[byte>>4] << 8) | dibits[byte&0x0f]);
300 } while (src < src_end);
301 data += stride;
305 void lcd_update_rect(int x, int y, int width, int height)
307 int xmax, ymax;
309 if (x + width > LCD_WIDTH)
310 width = LCD_WIDTH - x;
311 if (width <= 0)
312 return;
314 ymax = y + height - 1;
315 if (ymax >= LCD_HEIGHT)
316 ymax = LCD_HEIGHT - 1;
318 #if defined(IPOD_MINI) || defined(IPOD_MINI2G)
319 x += pix_offset;
320 #endif
321 /* writing is done in 16-bit units (8 pixels) */
322 xmax = (x + width - 1) >> 3;
323 x >>= 3;
324 width = xmax - x + 1;
326 for (; y <= ymax; y++) {
327 unsigned char *data, *data_end;
329 lcd_cmd_and_data(R_RAM_ADDR_SET, (y << 5) + addr_offset - x);
330 lcd_prepare_cmd(R_RAM_DATA);
332 data = &lcd_framebuffer[y][2*x];
333 data_end = data + 2 * width;
334 #if defined(IPOD_MINI) || defined(IPOD_MINI2G)
335 if (pix_offset == -2) {
336 unsigned cur_word = *data++;
337 do {
338 cur_word = (cur_word << 8) | *data++;
339 cur_word = (cur_word << 8) | *data++;
340 lcd_send_data((cur_word >> 4) & 0xffff);
341 } while (data <= data_end);
342 } else
343 #endif
345 do {
346 unsigned highbyte = *data++;
347 lcd_send_data((highbyte << 8) | *data++);
348 } while (data < data_end);
353 /* Update the display. */
354 void lcd_update(void)
356 lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
359 #ifdef HAVE_LCD_SHUTDOWN
360 /* LCD powerdown */
361 void lcd_shutdown(void)
363 lcd_cmd_and_data(R_POWER_CONTROL, 0x1500); /* Turn off op amp power */
364 lcd_cmd_and_data(R_POWER_CONTROL, 0x1502); /* Put LCD driver in standby */
366 #endif