blob: ba12e072da7dd00dfa208254b86457276fe8bf8c [file] [log] [blame]
Marcin Bukat28d54c62010-04-26 21:40:16 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
Marcin Bukat082c7d32010-10-22 12:28:43 +00008 * $Id$
Marcin Bukat28d54c62010-04-26 21:40:16 +00009 *
10 * Copyright (C) 2010 Marcin Bukat
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "config.h"
23
24#include "system.h"
25#include "kernel.h"
26#include "lcd.h"
27
28/*** definitions ***/
29/* TOMATO LSI 0350 - definitions and slightly tweaked functions
30 * taken from lcd-remote-iaudio.c
31 */
32
33#define LCD_SET_DUTY_RATIO 0x48
34#define LCD_SELECT_ADC 0xa0
35#define LCD_SELECT_SHL 0xc0
36#define LCD_SET_COM0 0x44
37#define LCD_OSC_ON 0xab
38#define LCD_SELECT_DCDC 0x64
39#define LCD_SELECT_RES 0x20
40#define LCD_SET_VOLUME 0x81
41#define LCD_SET_BIAS 0x50
42#define LCD_CONTROL_POWER 0x28
43#define LCD_DISPLAY_ON 0xae
44#define LCD_SET_INITLINE 0x40
45#define LCD_SET_COLUMN 0x10
46#define LCD_SET_PAGE 0xb0
47#define LCD_SET_GRAY 0x88
48#define LCD_SET_PWM_FRC 0x90
49#define LCD_SET_POWER_SAVE 0xa8
50#define LCD_REVERSE 0xa6
51#define LCD_RESET 0xe2
52
53/* cached settings */
54static bool cached_invert = false;
55static bool cached_flip = false;
56static int cached_contrast = DEFAULT_CONTRAST_SETTING;
Marcin Bukat28d54c62010-04-26 21:40:16 +000057
Marcin Bukata86b08e2010-08-08 18:41:19 +000058static struct mutex lcd_mtx; /* The update functions use DMA and yield */
59
60volatile unsigned char page IBSS_ATTR;
61unsigned char column IBSS_ATTR;
62unsigned int dma_len IBSS_ATTR;
63volatile unsigned long dma_count IBSS_ATTR;
64
Marcin Bukat28d54c62010-04-26 21:40:16 +000065/*** hardware configuration ***/
66int lcd_default_contrast(void)
67{
68 return DEFAULT_CONTRAST_SETTING;
69}
70
71void lcd_powersave(bool on)
72{
Marcin Bukat4fad74e2010-05-10 12:36:58 +000073 if (on)
Marcin Bukat12fa7592010-06-08 09:44:17 +000074 /* enter power saving mode
75 * this turns off lcd without controller reset
76 * probably ~1mA saving
77 */
Marcin Bukat4fad74e2010-05-10 12:36:58 +000078 lcd_write_command(LCD_SET_POWER_SAVE | 1);
79 else
Marcin Bukat12fa7592010-06-08 09:44:17 +000080 /* leave lcd power saving mode
81 * no need to reset and initialize controller
82 */
83 lcd_write_command(LCD_SET_POWER_SAVE | 0);
Marcin Bukat28d54c62010-04-26 21:40:16 +000084}
85
86void lcd_set_contrast(int val)
87{
88 if (val < MIN_CONTRAST_SETTING)
89 val = MIN_CONTRAST_SETTING;
90 else if (val > MAX_CONTRAST_SETTING)
91 val = MAX_CONTRAST_SETTING;
92
93 cached_contrast = val;
Marcin Bukat4fad74e2010-05-10 12:36:58 +000094 lcd_write_command_e(LCD_SET_VOLUME, val);
Marcin Bukat28d54c62010-04-26 21:40:16 +000095}
96
97void lcd_set_invert_display(bool yesno)
98{
99 cached_invert = yesno;
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000100 lcd_write_command(LCD_REVERSE | yesno);
Marcin Bukat28d54c62010-04-26 21:40:16 +0000101
102}
103
104/* turn the display upside down (call lcd_update() afterwards) */
105void lcd_set_flip(bool yesno)
106{
107 cached_flip = yesno;
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000108 if(yesno)
Marcin Bukat28d54c62010-04-26 21:40:16 +0000109 {
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000110 lcd_write_command(LCD_SELECT_ADC | 1);
111 lcd_write_command(LCD_SELECT_SHL | 0);
112 lcd_write_command_e(LCD_SET_COM0, 0);
Marcin Bukat28d54c62010-04-26 21:40:16 +0000113 }
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000114 else
115 {
116 lcd_write_command(LCD_SELECT_ADC | 0);
117 lcd_write_command(LCD_SELECT_SHL | 8);
118 lcd_write_command_e(LCD_SET_COM0, 0);
119 }
Marcin Bukat28d54c62010-04-26 21:40:16 +0000120}
121
122void lcd_shutdown(void)
123{
124 /* Set power save -> Power OFF (VDD - VSS) .. that's it */
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000125 lcd_write_command(LCD_SET_POWER_SAVE | 1);
Marcin Bukat28d54c62010-04-26 21:40:16 +0000126}
127
128void lcd_init_device(void)
129{
130 and_l(~0x00000800, &GPIO_FUNCTION); /* CS3 line */
131
132 /* LCD Reset GPO34 */
133 or_l(0x00000004, &GPIO1_ENABLE); /* set as output */
134 or_l(0x00000004, &GPIO1_FUNCTION); /* switch to secondary function - GPIO */
135
136 and_l(~0x00000004, &GPIO1_OUT); /* RESET low */
137 sleep(1); /* delay at least 1000 ns */
138 or_l(0x00000004, &GPIO1_OUT); /* RESET high */
139 sleep(1);
140
141 /* parameters setup taken from original firmware */
142 lcd_write_command(LCD_RESET);
143 lcd_write_command_e(LCD_SET_DUTY_RATIO,0x80); /* 1/128 */
144 lcd_write_command(LCD_OSC_ON);
145 lcd_write_command(LCD_SELECT_DCDC | 3); /* DC/DC 6xboost */
146 lcd_write_command(LCD_SELECT_RES | 7); /* Regulator resistor: 7.2 */
147 lcd_write_command(LCD_SET_BIAS | 6); /* 1/11 */
148 lcd_write_command(LCD_SET_PWM_FRC | 6); /* 3FRC + 12PWM */
149 lcd_write_command_e(LCD_SET_GRAY | 0, 0x00);
150 lcd_write_command_e(LCD_SET_GRAY | 1, 0x00);
151 lcd_write_command_e(LCD_SET_GRAY | 2, 0x0c);
152 lcd_write_command_e(LCD_SET_GRAY | 3, 0x00);
153 lcd_write_command_e(LCD_SET_GRAY | 4, 0xc4);
154 lcd_write_command_e(LCD_SET_GRAY | 5, 0x00);
155 lcd_write_command_e(LCD_SET_GRAY | 6, 0xcc);
156 lcd_write_command_e(LCD_SET_GRAY | 7, 0x00);
157
158 lcd_write_command(LCD_CONTROL_POWER | 7); /* All circuits ON */
159 lcd_write_command(LCD_DISPLAY_ON | 1); /* display on */
160
Marcin Bukat28d54c62010-04-26 21:40:16 +0000161 lcd_set_flip(cached_flip);
162 lcd_set_contrast(cached_contrast);
163 lcd_set_invert_display(cached_invert);
164
Marcin Bukata86b08e2010-08-08 18:41:19 +0000165 /* Configure DMA3 */
166 DAR3 = 0xf0000002;
167 DSR3 = 1;
168 DIVR3 = 57; /* DMA3 is mapped into vector 57 in system.c */
169 ICR9 = (6 << 2); /* Enable DMA3 interrupt at level 6, priority 0 */
170 and_l(~(1<<17), &IMR);
171
172 mutex_init(&lcd_mtx);
173
Marcin Bukat28d54c62010-04-26 21:40:16 +0000174 lcd_update();
175}
176
Marcin Bukata86b08e2010-08-08 18:41:19 +0000177/* LCD DMA ISR */
178void DMA3(void) __attribute__ ((interrupt_handler, section(".icode")));
179void DMA3(void)
180{
181 DSR3 = 1;
182
183 if (--dma_count > 0)
184 {
185 /* Setup write address in lcd controller ram*/
186 lcd_write_command(LCD_SET_PAGE | ++page);
187 lcd_write_command_e(LCD_SET_COLUMN | ((column >> 4) & 0xf),
188 column & 0x0f);
189
190 SAR3 = (unsigned long)&lcd_framebuffer[page][column];
191 BCR3 = dma_len;
192 DCR3 = DMA_INT | DMA_AA | DMA_BWC(1)
193 | DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE)
194 | DMA_DSIZE(DMA_SIZE_BYTE) | DMA_START;
195
196 }
197}
198
Marcin Bukat28d54c62010-04-26 21:40:16 +0000199/* Update the display.
200 This must be called after all other LCD functions that change the display. */
201void lcd_update(void) ICODE_ATTR;
202void lcd_update(void)
203{
Marcin Bukata86b08e2010-08-08 18:41:19 +0000204 mutex_lock(&lcd_mtx);
Marcin Bukat28d54c62010-04-26 21:40:16 +0000205
Marcin Bukata86b08e2010-08-08 18:41:19 +0000206 /* Setup initial address in lcd controller */
207 lcd_write_command(LCD_SET_PAGE | 0);
208 lcd_write_command_e(LCD_SET_COLUMN, 0);
209
210 /* Initial lcd ram address */
211 page = 0;
212 column = 0;
213
214 /* Number of pages to address */
215 dma_count = LCD_FBHEIGHT;
216
217 /* Transfer size in bytes to the given page */
218 dma_len = LCD_WIDTH*2;
219
220 /* Initialize DMA transfer */
221 SAR3 = (unsigned long)lcd_framebuffer;
222 BCR3 = LCD_WIDTH*2;
223 DCR3 = DMA_INT | DMA_AA | DMA_BWC(1)
224 | DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE)
225 | DMA_DSIZE(DMA_SIZE_BYTE) | DMA_START;
226
227 while (dma_count > 0)
228 yield();
229
230 mutex_unlock(&lcd_mtx);
Marcin Bukat28d54c62010-04-26 21:40:16 +0000231}
232
233/* Update a fraction of the display. */
234void lcd_update_rect(int, int, int, int) ICODE_ATTR;
235void lcd_update_rect(int x, int y, int width, int height)
236{
237 int ymax;
238
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000239 /* The Y coordinates have to work on even 8 pixel rows */
240 ymax = (y + height-1) >> 3;
241 y >>= 3;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000242
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000243 if (x + width > LCD_WIDTH)
244 width = LCD_WIDTH - x;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000245
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000246 if (width <= 0)
247 return; /* nothing left to do, 0 is harmful to lcd_write_data() */
Marcin Bukat28d54c62010-04-26 21:40:16 +0000248
Marcin Bukata86b08e2010-08-08 18:41:19 +0000249 mutex_lock(&lcd_mtx);
250
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000251 if (ymax >= LCD_FBHEIGHT)
252 ymax = LCD_FBHEIGHT-1;
253
Marcin Bukata86b08e2010-08-08 18:41:19 +0000254 /* Initial lcd ram address*/
255 lcd_write_command(LCD_SET_PAGE | y );
256 lcd_write_command_e(LCD_SET_COLUMN | ((x >> 4) & 0xf), x & 0x0f);
257
258 page = y;
259 column = x;
260 dma_len = width*2;
261 dma_count = ymax - y + 1;
262
263 /* Initialize DMA transfer */
264 SAR3 = (unsigned long)&lcd_framebuffer[page][column];
265 BCR3 = dma_len;
266 DCR3 = DMA_INT | DMA_AA | DMA_BWC(1)
267 | DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE)
268 | DMA_DSIZE(DMA_SIZE_BYTE) | DMA_START;
269
270 while (dma_count > 0)
271 yield();
272
273 mutex_unlock(&lcd_mtx);
274
Marcin Bukat28d54c62010-04-26 21:40:16 +0000275}
276
Marcin Bukat88baa4d2010-04-30 14:13:52 +0000277/* Helper function. */
278void lcd_mono_data(const unsigned char *data, int count);
Marcin Bukat28d54c62010-04-26 21:40:16 +0000279
Marcin Bukat88baa4d2010-04-30 14:13:52 +0000280/* Performance function that works with an external buffer
281 note that by and bheight are in 8-pixel units! */
Marcin Bukat28d54c62010-04-26 21:40:16 +0000282void lcd_blit_mono(const unsigned char *data, int x, int by, int width,
283 int bheight, int stride)
284{
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000285 while (bheight--)
Marcin Bukat88baa4d2010-04-30 14:13:52 +0000286 {
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000287 lcd_write_command(LCD_SET_PAGE | (by & 0xf));
288 lcd_write_command_e(LCD_SET_COLUMN | ((x >> 4) & 0xf), x & 0xf);
Marcin Bukat88baa4d2010-04-30 14:13:52 +0000289
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000290 lcd_mono_data(data, width);
291 data += stride;
292 by++;
Marcin Bukat88baa4d2010-04-30 14:13:52 +0000293 }
Marcin Bukat28d54c62010-04-26 21:40:16 +0000294}
Marcin Bukat88baa4d2010-04-30 14:13:52 +0000295
296/* Helper function for lcd_grey_phase_blit(). */
297void lcd_grey_data(unsigned char *values, unsigned char *phases, int count);
298
299/* Performance function that works with an external buffer
300 note that by and bheight are in 8-pixel units! */
301void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases,
302 int x, int by, int width, int bheight, int stride)
303{
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000304 stride <<= 3; /* 8 pixels per block */
Marcin Bukat88baa4d2010-04-30 14:13:52 +0000305
Marcin Bukat4fad74e2010-05-10 12:36:58 +0000306 while (bheight--)
307 {
308 lcd_write_command(LCD_SET_PAGE | (by & 0xf));
309 lcd_write_command_e(LCD_SET_COLUMN | ((x >> 4) & 0xf), x & 0xf);
310
311 lcd_grey_data(values, phases, width);
312 values += stride;
313 phases += stride;
314 by++;
Marcin Bukat88baa4d2010-04-30 14:13:52 +0000315 }
316}
317