Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
Marcin Bukat | 082c7d3 | 2010-10-22 12:28:43 +0000 | [diff] [blame^] | 8 | * $Id$ |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 9 | * |
| 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 */ |
| 54 | static bool cached_invert = false; |
| 55 | static bool cached_flip = false; |
| 56 | static int cached_contrast = DEFAULT_CONTRAST_SETTING; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 57 | |
Marcin Bukat | a86b08e | 2010-08-08 18:41:19 +0000 | [diff] [blame] | 58 | static struct mutex lcd_mtx; /* The update functions use DMA and yield */ |
| 59 | |
| 60 | volatile unsigned char page IBSS_ATTR; |
| 61 | unsigned char column IBSS_ATTR; |
| 62 | unsigned int dma_len IBSS_ATTR; |
| 63 | volatile unsigned long dma_count IBSS_ATTR; |
| 64 | |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 65 | /*** hardware configuration ***/ |
| 66 | int lcd_default_contrast(void) |
| 67 | { |
| 68 | return DEFAULT_CONTRAST_SETTING; |
| 69 | } |
| 70 | |
| 71 | void lcd_powersave(bool on) |
| 72 | { |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 73 | if (on) |
Marcin Bukat | 12fa759 | 2010-06-08 09:44:17 +0000 | [diff] [blame] | 74 | /* enter power saving mode |
| 75 | * this turns off lcd without controller reset |
| 76 | * probably ~1mA saving |
| 77 | */ |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 78 | lcd_write_command(LCD_SET_POWER_SAVE | 1); |
| 79 | else |
Marcin Bukat | 12fa759 | 2010-06-08 09:44:17 +0000 | [diff] [blame] | 80 | /* leave lcd power saving mode |
| 81 | * no need to reset and initialize controller |
| 82 | */ |
| 83 | lcd_write_command(LCD_SET_POWER_SAVE | 0); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | void 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 Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 94 | lcd_write_command_e(LCD_SET_VOLUME, val); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 95 | } |
| 96 | |
| 97 | void lcd_set_invert_display(bool yesno) |
| 98 | { |
| 99 | cached_invert = yesno; |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 100 | lcd_write_command(LCD_REVERSE | yesno); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 101 | |
| 102 | } |
| 103 | |
| 104 | /* turn the display upside down (call lcd_update() afterwards) */ |
| 105 | void lcd_set_flip(bool yesno) |
| 106 | { |
| 107 | cached_flip = yesno; |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 108 | if(yesno) |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 109 | { |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 110 | 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 Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 113 | } |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 114 | 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 Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | void lcd_shutdown(void) |
| 123 | { |
| 124 | /* Set power save -> Power OFF (VDD - VSS) .. that's it */ |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 125 | lcd_write_command(LCD_SET_POWER_SAVE | 1); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | void 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 Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 161 | lcd_set_flip(cached_flip); |
| 162 | lcd_set_contrast(cached_contrast); |
| 163 | lcd_set_invert_display(cached_invert); |
| 164 | |
Marcin Bukat | a86b08e | 2010-08-08 18:41:19 +0000 | [diff] [blame] | 165 | /* 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 Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 174 | lcd_update(); |
| 175 | } |
| 176 | |
Marcin Bukat | a86b08e | 2010-08-08 18:41:19 +0000 | [diff] [blame] | 177 | /* LCD DMA ISR */ |
| 178 | void DMA3(void) __attribute__ ((interrupt_handler, section(".icode"))); |
| 179 | void 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 Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 199 | /* Update the display. |
| 200 | This must be called after all other LCD functions that change the display. */ |
| 201 | void lcd_update(void) ICODE_ATTR; |
| 202 | void lcd_update(void) |
| 203 | { |
Marcin Bukat | a86b08e | 2010-08-08 18:41:19 +0000 | [diff] [blame] | 204 | mutex_lock(&lcd_mtx); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 205 | |
Marcin Bukat | a86b08e | 2010-08-08 18:41:19 +0000 | [diff] [blame] | 206 | /* 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 Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 231 | } |
| 232 | |
| 233 | /* Update a fraction of the display. */ |
| 234 | void lcd_update_rect(int, int, int, int) ICODE_ATTR; |
| 235 | void lcd_update_rect(int x, int y, int width, int height) |
| 236 | { |
| 237 | int ymax; |
| 238 | |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 239 | /* The Y coordinates have to work on even 8 pixel rows */ |
| 240 | ymax = (y + height-1) >> 3; |
| 241 | y >>= 3; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 242 | |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 243 | if (x + width > LCD_WIDTH) |
| 244 | width = LCD_WIDTH - x; |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 245 | |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 246 | if (width <= 0) |
| 247 | return; /* nothing left to do, 0 is harmful to lcd_write_data() */ |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 248 | |
Marcin Bukat | a86b08e | 2010-08-08 18:41:19 +0000 | [diff] [blame] | 249 | mutex_lock(&lcd_mtx); |
| 250 | |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 251 | if (ymax >= LCD_FBHEIGHT) |
| 252 | ymax = LCD_FBHEIGHT-1; |
| 253 | |
Marcin Bukat | a86b08e | 2010-08-08 18:41:19 +0000 | [diff] [blame] | 254 | /* 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 Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 275 | } |
| 276 | |
Marcin Bukat | 88baa4d | 2010-04-30 14:13:52 +0000 | [diff] [blame] | 277 | /* Helper function. */ |
| 278 | void lcd_mono_data(const unsigned char *data, int count); |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 279 | |
Marcin Bukat | 88baa4d | 2010-04-30 14:13:52 +0000 | [diff] [blame] | 280 | /* Performance function that works with an external buffer |
| 281 | note that by and bheight are in 8-pixel units! */ |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 282 | void lcd_blit_mono(const unsigned char *data, int x, int by, int width, |
| 283 | int bheight, int stride) |
| 284 | { |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 285 | while (bheight--) |
Marcin Bukat | 88baa4d | 2010-04-30 14:13:52 +0000 | [diff] [blame] | 286 | { |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 287 | lcd_write_command(LCD_SET_PAGE | (by & 0xf)); |
| 288 | lcd_write_command_e(LCD_SET_COLUMN | ((x >> 4) & 0xf), x & 0xf); |
Marcin Bukat | 88baa4d | 2010-04-30 14:13:52 +0000 | [diff] [blame] | 289 | |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 290 | lcd_mono_data(data, width); |
| 291 | data += stride; |
| 292 | by++; |
Marcin Bukat | 88baa4d | 2010-04-30 14:13:52 +0000 | [diff] [blame] | 293 | } |
Marcin Bukat | 28d54c6 | 2010-04-26 21:40:16 +0000 | [diff] [blame] | 294 | } |
Marcin Bukat | 88baa4d | 2010-04-30 14:13:52 +0000 | [diff] [blame] | 295 | |
| 296 | /* Helper function for lcd_grey_phase_blit(). */ |
| 297 | void 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! */ |
| 301 | void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases, |
| 302 | int x, int by, int width, int bheight, int stride) |
| 303 | { |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 304 | stride <<= 3; /* 8 pixels per block */ |
Marcin Bukat | 88baa4d | 2010-04-30 14:13:52 +0000 | [diff] [blame] | 305 | |
Marcin Bukat | 4fad74e | 2010-05-10 12:36:58 +0000 | [diff] [blame] | 306 | 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 Bukat | 88baa4d | 2010-04-30 14:13:52 +0000 | [diff] [blame] | 315 | } |
| 316 | } |
| 317 | |