Import into DevLib.
[bertos.git] / drv / lcd_gfx.c
1 /**
2  * \file
3  * <!--
4  * Copyright 2003, 2004, 2005, 2006 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 2001 Bernardo Innocenti <bernie@codewiz.org>
6  * This file is part of DevLib - See README.devlib for information.
7  * -->
8  *
9  * \version $Id$
10  *
11  * \author Bernardo Innocenti <bernie@develer.com>
12  * \author Stefano Fedrigo <aleph@develer.com>
13  *
14  * \brief Displaytech 32122A LCD driver
15  */
16
17 /*#*
18  *#* $Log$
19  *#* Revision 1.1  2006/01/16 03:50:57  bernie
20  *#* Import into DevLib.
21  *#*
22  *#*/
23
24 #include "lcd.h"
25 #include <gfx/gfx.h>
26 #include <drv/timer.h>
27
28 #include <cfg/cpu.h>
29 #include <hw.h>
30 #include <cfg/macros.h> /* BV() */
31 #include <cfg/debug.h>
32
33 #include <avr/io.h>
34 #include <stdbool.h>
35 #include <inttypes.h>
36
37 #ifdef CONFIG_LCD_SOFTINT_REFRESH
38
39         /*! Interval between softint driven lcd refresh */
40 #       define LCD_REFRESH_INTERVAL 20  /* 20ms -> 50fps */
41
42 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
43
44 /*! Number of LCD pages */
45 #define LCD_PAGES 4
46
47 /*! Width of an LCD page */
48 #define LCD_PAGESIZE (LCD_WIDTH / 2)
49
50 /*!
51  * \name LCD I/O pins/ports
52  * @{
53  */
54 #define LCD_PF_DB0   PF4
55 #define LCD_PF_DB1   PF5
56 #define LCD_PF_DB2   PF6
57 #define LCD_PF_DB3   PF7
58 #define LCD_PD_DB4   PD4
59 #define LCD_PD_DB5   PD5
60 #define LCD_PD_DB6   PD6
61 #define LCD_PD_DB7   PD7
62 #define LCD_PB_A0    PB0
63 #define LCD_PE_RW    PE7
64 #define LCD_PE_E1    PE2
65 #define LCD_PE_E2    PE6
66 /*@}*/
67
68 /*!
69  * \name DB high nibble (DB[4-7])
70  * @{
71  */
72 #define LCD_DATA_HI_PORT    PORTD
73 #define LCD_DATA_HI_PIN     PIND
74 #define LCD_DATA_HI_DDR     DDRD
75 #define LCD_DATA_HI_SHIFT   0
76 #define LCD_DATA_HI_MASK    0xF0
77 /*@}*/
78
79 /*!
80  * \name DB low nibble (DB[0-3])
81  * @{
82  */
83 #define LCD_DATA_LO_PORT    PORTF
84 #define LCD_DATA_LO_PIN     PINF
85 #define LCD_DATA_LO_DDR     DDRF
86 #define LCD_DATA_LO_SHIFT   4
87 #define LCD_DATA_LO_MASK    0xF0
88 /*@}*/
89
90 /*!
91  * \name LCD bus control macros
92  * @{
93  */
94 #define LCD_CLR_A0   (PORTB &= ~BV(LCD_PB_A0))
95 #define LCD_SET_A0   (PORTB |=  BV(LCD_PB_A0))
96 #define LCD_CLR_RD   (PORTE &= ~BV(LCD_PE_RW))
97 #define LCD_SET_RD   (PORTE |=  BV(LCD_PE_RW))
98 #define LCD_CLR_E1   (PORTE &= ~BV(LCD_PE_E1))
99 #define LCD_SET_E1   (PORTE |=  BV(LCD_PE_E1))
100 #define LCD_CLR_E2   (PORTE &= ~BV(LCD_PE_E2))
101 #define LCD_SET_E2   (PORTE |=  BV(LCD_PE_E2))
102 #define LCD_SET_E(x) (PORTE |= (x))
103 #define LCD_CLR_E(x) (PORTE &= ~(x))
104 /*@}*/
105
106 /*!
107  * \name Chip select bits for LCD_SET_E()
108  * @{
109  */
110 #define LCDF_E1 (BV(LCD_PE_E1))
111 #define LCDF_E2 (BV(LCD_PE_E2))
112 /*@}*/
113
114 /*! Read from the LCD data bus (DB[0-7]) */
115 #define LCD_READ ( \
116                 ((LCD_DATA_LO_PIN & LCD_DATA_LO_MASK) >> LCD_DATA_LO_SHIFT) | \
117                 ((LCD_DATA_HI_PIN & LCD_DATA_HI_MASK) >> LCD_DATA_HI_SHIFT) \
118         )
119
120 /*! Write to the LCD data bus (DB[0-7]) */
121 #define LCD_WRITE(d) \
122         do { \
123                 LCD_DATA_LO_PORT = (LCD_DATA_LO_PORT & ~LCD_DATA_LO_MASK) | (((d)<<LCD_DATA_LO_SHIFT) & LCD_DATA_LO_MASK); \
124                 LCD_DATA_HI_PORT = (LCD_DATA_HI_PORT & ~LCD_DATA_HI_MASK) | (((d)<<LCD_DATA_HI_SHIFT) & LCD_DATA_HI_MASK); \
125         } while (0)
126
127 /*! Set data bus direction to output (write to display) */
128 #define LCD_DB_OUT \
129         do { \
130                 LCD_DATA_LO_DDR |= LCD_DATA_LO_MASK; \
131                 LCD_DATA_HI_DDR |= LCD_DATA_HI_MASK; \
132         } while (0)
133
134 /*! Set data bus direction to input (read from display) */
135 #define LCD_DB_IN \
136         do { \
137                 LCD_DATA_LO_DDR &= ~LCD_DATA_LO_MASK; \
138                 LCD_DATA_HI_DDR &= ~LCD_DATA_HI_MASK; \
139         } while (0)
140
141 /*! Delay for tEW (160ns) */
142 #define LCD_DELAY_WRITE \
143         do { \
144                 NOP; \
145                 NOP; \
146         } while (0)
147
148 /*! Delay for tACC6 (180ns) */
149 #define LCD_DELAY_READ \
150         do { \
151                 NOP; \
152                 NOP; \
153                 NOP; \
154         } while (0)
155
156
157 /*!
158  * \name 32122A Commands
159  * @{
160  */
161 #define LCD_CMD_DISPLAY_ON  0xAF
162 #define LCD_CMD_DISPLAY_OFF 0xAE
163 #define LCD_CMD_STARTLINE   0xC0
164 #define LCD_CMD_PAGEADDR    0xB8
165 #define LCD_CMD_COLADDR     0x00
166 #define LCD_CMD_ADC_LEFT    0xA1
167 #define LCD_CMD_ADC_RIGHT   0xA0
168 #define LCD_CMD_STATIC_OFF  0xA4
169 #define LCD_CMD_STATIC_ON   0xA5
170 #define LCD_CMD_DUTY_32     0xA9
171 #define LCD_CMD_DUTY_16     0xA8
172 #define LCD_CMD_RMW_ON      0xE0
173 #define LCD_CMD_RMW_OFF     0xEE
174 #define LCD_CMD_RESET       0xE2
175 /*@}*/
176
177 /* Status flags */
178 #define LCDF_BUSY BV(7)
179
180 #ifdef CONFIG_LCD_WAIT
181 /*!
182  * \code
183  *      __              __
184  * RS   __\____________/__
185  *         ____________
186  * R/W  __/            \__
187  *            _______
188  * E1   _____/       \____
189  *        ______      ____
190  * DATA X/      \====/
191  *
192  * \endcode
193  */
194 #define WAIT_LCD \
195         do { \
196                 uint8_t status; \
197                 LCD_DB_IN; \
198                 do { \
199                         LCD_SET_RD; \
200                         LCD_CLR_A0; \
201                         LCD_SET_E1; \
202                         LCD_DELAY_READ; \
203                         status = LCD_READ; \
204                         LCD_CLR_E1; \
205                         LCD_SET_A0; \
206                         LCD_CLR_RD; \
207                 } while (status & LCDF_BUSY); \
208                 LCD_DB_OUT; \
209         } while (0)
210
211 #else /* CONFIG_LCD_WAIT */
212
213 #define WAIT_LCD do {} while(0)
214
215 #endif /* CONFIG_LCD_WAIT */
216
217
218 /*!
219  * Raster buffer to draw into.
220  *
221  * Bits in the bitmap bytes have vertical orientation,
222  * as required by the LCD driver.
223  */
224 DECLARE_WALL(wall_before_raster, WALL_SIZE)
225 static uint8_t lcd_raster[LCD_WIDTH * ((LCD_HEIGHT + 7) / 8)];
226 DECLARE_WALL(wall_after_raster, WALL_SIZE)
227
228 /*! Default LCD bitmap */
229 struct Bitmap lcd_bitmap;
230
231
232 #ifdef CONFIG_LCD_SOFTINT_REFRESH
233
234 /*! Timer for regular LCD refresh */
235 static Timer *lcd_refresh_timer;
236
237 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
238
239
240 /*
241 static bool lcd_check(void)
242 {
243         uint8_t status;
244         uint16_t retries = 32768;
245         PORTA = 0xFF;
246         DDRA = 0x00;
247         do {
248                 cbi(PORTC, PCB_LCD_RS);
249                 sbi(PORTC, PCB_LCD_RW);
250                 sbi(PORTC, PCB_LCD_E);
251                 --retries;
252                 NOP;
253                 status = PINA;
254                 cbi(PORTC, PCB_LCD_E);
255                 cbi(PORTC, PCB_LCD_RW);
256         } while ((status & LCDF_BUSY) && retries);
257
258         return (retries != 0);
259 }
260 */
261
262
263 static inline void lcd_cmd(uint8_t cmd, uint8_t chip)
264 {
265         WAIT_LCD;
266
267         /*      __              __
268          * A0   __\____________/__
269          *
270          * R/W  __________________
271          *            ______
272          * E1   _____/      \_____
273          *
274          * DATA --<============>--
275          */
276         LCD_WRITE(cmd);
277         //LCD_DB_OUT;
278         LCD_CLR_A0;
279         LCD_SET_E(chip);
280         LCD_DELAY_WRITE;
281         LCD_CLR_E(chip);
282         LCD_SET_A0;
283         //LCD_DB_IN;
284 }
285
286
287 static inline uint8_t lcd_read(uint8_t chip)
288 {
289         uint8_t data;
290
291         WAIT_LCD;
292
293         /*!
294          * \code
295          *      __________________
296          * A0   __/            \__
297          *         ____________
298          * R/W  __/            \__
299          *            _______
300          * E1   _____/       \____
301          *
302          * DATA -------<=====>----
303          *
304          * \endcode
305          */
306         LCD_DB_IN;
307         //LCD_SET_A0;
308         LCD_SET_RD;
309         LCD_SET_E(chip);
310         LCD_DELAY_READ;
311         data = LCD_READ;
312         LCD_CLR_E(chip);
313         LCD_CLR_RD;
314         //LCD_CLR_A0;
315         LCD_DB_OUT;
316
317         return data;
318 }
319
320
321 static inline void lcd_write(uint8_t c, uint8_t chip)
322 {
323         WAIT_LCD;
324
325         /*!
326          * \code
327          *      __________________
328          * A0   ___/          \___
329          *
330          * R/W  __________________
331          *            ______
332          * E1   _____/      \_____
333          *
334          * DATA -<==============>-
335          *
336          * \endcode
337          */
338         LCD_WRITE(c);
339         //LCD_DB_OUT;
340         //LCD_SET_A0;
341         LCD_SET_E(chip);
342         LCD_DELAY_WRITE;
343         LCD_CLR_E(chip);
344         //LCD_CLR_A0;
345         //LCD_DB_IN;
346 }
347
348
349 /*!
350  * Set LCD contrast PWM.
351  */
352 void lcd_setpwm(int duty)
353 {
354         ASSERT(duty >= LCD_MIN_PWM);
355         ASSERT(duty <= LCD_MAX_PWM);
356
357         OCR3C = duty;
358 }
359
360
361 static void lcd_clear(void)
362 {
363         uint8_t page, j;
364
365         for (page = 0; page < LCD_PAGES; ++page)
366         {
367                 lcd_cmd(LCD_CMD_COLADDR | 0, LCDF_E1 | LCDF_E2);
368                 lcd_cmd(LCD_CMD_PAGEADDR | page, LCDF_E1 | LCDF_E2);
369                 for (j = 0; j < LCD_PAGESIZE; j++)
370                         lcd_write(0, LCDF_E1 | LCDF_E2);
371         }
372 }
373
374
375 static void lcd_writeraster(const uint8_t *raster)
376 {
377         uint8_t page, rows;
378         const uint8_t *right_raster;
379
380         CHECK_WALL(wall_before_raster);
381         CHECK_WALL(wall_after_raster);
382
383         for (page = 0; page < LCD_PAGES; ++page)
384         {
385                 lcd_cmd(LCD_CMD_PAGEADDR | page, LCDF_E1 | LCDF_E2);
386                 lcd_cmd(LCD_CMD_COLADDR | 0, LCDF_E1 | LCDF_E2);
387
388                 /* Super optimized lamer loop */
389                 right_raster = raster + LCD_PAGESIZE;
390                 rows = LCD_PAGESIZE;
391                 do
392                 {
393                         lcd_write(*raster++, LCDF_E1);
394                         lcd_write(*right_raster++, LCDF_E2);
395                 }
396                 while (--rows);
397                 raster = right_raster;
398         }
399 }
400
401
402 void lcd_blit_bitmap(Bitmap *bm)
403 {
404         lcd_writeraster(bm->raster);
405 }
406
407
408 #ifdef CONFIG_LCD_SOFTINT_REFRESH
409
410 static void lcd_refresh_softint(void)
411 {
412         lcd_blit_bitmap(&lcd_bitmap);
413         timer_add(lcd_refresh_timer);
414 }
415
416 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
417
418
419 /*!
420  * Initialize LCD subsystem.
421  *
422  * \note The PWM used for LCD contrast is initialized in drv/pwm.c
423  *       because it is the same PWM used for output attenuation.
424  */
425 void lcd_init(void)
426 {
427         // FIXME: interrupts are already disabled when we get here?!?
428         cpuflags_t flags;
429         IRQ_SAVE_DISABLE(flags);
430
431         PORTB |= BV(LCD_PB_A0);
432         DDRB |= BV(LCD_PB_A0);
433
434         PORTE &= ~(BV(LCD_PE_RW) | BV(LCD_PE_E1) | BV(LCD_PE_E2));
435         DDRE |= BV(LCD_PE_RW) | BV(LCD_PE_E1) | BV(LCD_PE_E2);
436
437 /* LCD hw reset
438         LCD_RESET_PORT |= BV(LCD_RESET_BIT);
439         LCD_RESET_DDR |= BV(LCD_RESET_BIT);
440         LCD_DELAY_WRITE;
441         LCD_DELAY_WRITE;
442         LCD_RESET_PORT &= ~BV(LCD_RESET_BIT);
443         LCD_DELAY_WRITE;
444         LCD_DELAY_WRITE;
445         LCD_RESET_PORT |= BV(LCD_RESET_BIT);
446 */
447         /*
448          * Data bus is in output state most of the time:
449          * LCD r/w functions assume it is left in output state
450          */
451         LCD_DB_OUT;
452
453         // Wait for RST line to stabilize at Vcc.
454         IRQ_ENABLE;
455         timer_delay(20);
456         IRQ_SAVE_DISABLE(flags);
457
458         lcd_cmd(LCD_CMD_RESET, LCDF_E1 | LCDF_E2);
459         lcd_cmd(LCD_CMD_DISPLAY_ON, LCDF_E1 | LCDF_E2);
460         lcd_cmd(LCD_CMD_STARTLINE | 0, LCDF_E1 | LCDF_E2);
461
462         /* Initialize anti-corruption walls for raster */
463         INIT_WALL(wall_before_raster);
464         INIT_WALL(wall_after_raster);
465
466         IRQ_RESTORE(flags);
467
468         lcd_clear();
469         lcd_setpwm(LCD_DEF_PWM);
470
471         gfx_bitmapInit(&lcd_bitmap, lcd_raster, LCD_WIDTH, LCD_HEIGHT);
472         gfx_bitmapClear(&lcd_bitmap);
473
474 #ifdef CONFIG_LCD_SOFTINT_REFRESH
475
476         /* Init IRQ driven LCD refresh */
477         lcd_refresh_timer = timer_new();
478         ASSERT(lcd_refresh_timer != NULL);
479         INITEVENT_INT(&lcd_refresh_timer->expire, (Hook)lcd_refresh_softint, 0);
480         lcd_refresh_timer->delay = LCD_REFRESH_INTERVAL;
481         timer_add(lcd_refresh_timer);
482
483 #endif /* CONFIG_LCD_SOFTINT_REFRESH */
484 }