]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/video/omap/lcd_lph8923.c
ARM: OMAP: lcd_lph8923: fix register read / write
[linux-2.6-omap-h63xx.git] / drivers / video / omap / lcd_lph8923.c
1 #include <linux/module.h>
2 #include <linux/delay.h>
3
4 #include <asm/arch/hardware.h>
5 #include <asm/arch/gpio.h>
6 #include <asm/arch/board.h>
7 #include <asm/arch/omapfb.h>
8 #include <asm/arch/lcd_lph8923.h>
9
10 #include <linux/spi/spi.h>
11 #include <asm/arch/mcspi.h>
12
13 #include "../../cbus/tahvo.h"
14
15 /* #define OMAPFB_DBG 1 */
16
17 #include "debug.h"
18
19 #define LPH8923_MODULE_NAME             "lcd_lph8923"
20
21 #define LPH8923_VER_BUGGY               1
22 #define LPH8923_VER_NON_BUGGY           3
23
24 struct {
25         int             enabled;
26         int             version;
27
28         u8              display_id[3];
29         unsigned int    saved_bklight_level;
30         unsigned long   hw_guard_end;           /* next value of jiffies
31                                                    when we can issue the
32                                                    next sleep in/out command */
33         unsigned long   hw_guard_wait;          /* max guard time in jiffies */
34         struct omapfb_device *fbdev;
35         struct spi_device *spi;
36 } lph8923;
37
38 #define LPH8923_CMD_READ_DISP_ID        0x04
39 #define LPH8923_CMD_READ_RED            0x06
40 #define LPH8923_CMD_READ_GREEN          0x07
41 #define LPH8923_CMD_READ_BLUE           0x08
42 #define LPH8923_CMD_READ_DISP_STATUS    0x09
43 #define LPH8923_CMD_SLEEP_IN            0x10
44 #define LPH8923_CMD_SLEEP_OUT           0x11
45 #define LPH8923_CMD_DISP_OFF            0x28
46 #define LPH8923_CMD_DISP_ON             0x29
47
48 static struct lcd_panel lph8923_panel;
49
50 #define LPH8923_SPEED_HZ        12000000
51
52 static int lph8923_spi_probe(struct spi_device *spi)
53 {
54         DBGENTER(1);
55
56         spi->dev.power.power_state = PMSG_ON;
57         spi->mode = SPI_MODE_0;
58         spi->bits_per_word = 9;
59         spi_setup(spi);
60
61         DBGPRINT(1, "spi %p\n", spi);
62         lph8923.spi = spi;
63
64         omapfb_register_panel(&lph8923_panel);
65
66         return 0;
67 }
68
69 static int lph8923_spi_remove(struct spi_device *spi)
70 {
71         DBGENTER(1);
72
73         lph8923.spi = NULL;
74
75         return 0;
76 }
77
78 static struct spi_driver lph8923_spi_driver = {
79         .driver = {
80                 .name   = LPH8923_MODULE_NAME,
81                 .bus    = &spi_bus_type,
82                 .owner  = THIS_MODULE,
83         },
84         .probe  = lph8923_spi_probe,
85         .remove = __devexit_p(lph8923_spi_remove),
86 };
87
88 static int lph8923_drv_init(void)
89 {
90         spi_register_driver(&lph8923_spi_driver);
91
92         return 0;
93 }
94 module_init(lph8923_drv_init);
95
96 static void lph8923_drv_cleanup(void)
97 {
98         spi_unregister_driver(&lph8923_spi_driver);
99 }
100 module_exit(lph8923_drv_cleanup);
101
102 static void set_spi_data_width(int width)
103 {
104         if (lph8923.spi->bits_per_word != width) {
105                 lph8923.spi->bits_per_word = width;
106                 spi_setup(lph8923.spi);
107         }
108 }
109
110 static void lph8923_read(int cmd, u8 *buf, int len)
111 {
112         struct spi_message      m;
113         struct spi_transfer     t;
114         u16                     w;
115
116         BUG_ON(lph8923.spi == NULL);
117
118         spi_message_init(&m);
119         m.spi = lph8923.spi;
120
121         if (len > 1) {
122                 cmd <<= 1;
123                 set_spi_data_width(10);
124         } else
125                 set_spi_data_width(9);
126
127         w               = cmd;
128         t.cs_change     = len ? 1 : 0;
129         t.tx_buf        = &w;
130         t.rx_buf        = NULL;
131         t.len           = 2;
132
133         spi_message_add_tail(&t, &m);
134
135         spi_sync(m.spi, &m);
136
137         if (!len)
138                 return;
139
140         spi_message_init(&m);
141         m.spi = lph8923.spi;
142
143         t.cs_change     = 0;
144         t.tx_buf        = NULL;
145         t.rx_buf        = buf;
146         t.len           = len;
147
148         set_spi_data_width(8);
149
150         spi_message_add_tail(&t, &m);
151
152         spi_sync(m.spi, &m);
153 }
154
155 static void lph8923_write(int cmd, const u8 *buf, int len)
156 {
157         struct spi_message      m;
158         struct spi_transfer     t;
159         u16                     w;
160         int                     i;
161
162         BUG_ON(lph8923.spi == NULL);
163
164         spi_message_init(&m);
165         m.spi = lph8923.spi;
166         set_spi_data_width(9);
167
168         t.cs_change     = 0;
169         w               = cmd;
170         t.tx_buf        = &w;
171         t.rx_buf        = NULL;
172         t.len           = 2;
173
174         spi_message_add_tail(&t, &m);
175         spi_sync(m.spi, &m);
176
177         if (!len)
178                 return;
179
180         t.tx_buf = &w;
181         for (i = 0; i < len; i++) {
182                 spi_message_init(&m);
183                 m.spi = lph8923.spi;
184                 spi_message_add_tail(&t, &m);
185                 w = buf[i] | (1 << 8);
186                 spi_sync(m.spi, &m);
187         }
188 }
189
190 static inline void lph8923_cmd(int cmd)
191 {
192         lph8923_write(cmd, NULL, 0);
193 }
194
195 struct cmd_data {
196         u8 cmd;
197         u8 len;
198         const u8 *data;
199 } __attribute__ ((packed));;
200
201 static const struct cmd_data init_cmds_buggy_lph8923[] = {
202         { 0xb0, 1, "\x08" },
203         { 0xb1, 2, "\x0b\x1c" },
204         { 0xb2, 4, "\x00\x00\x00\x00" },
205         { 0xb3, 4, "\x00\x00\x00\x00" },
206         { 0xb4, 1, "\x87" },
207         { 0xb5, 4, "\x37\x07\x37\x07" },
208         { 0xb6, 2, "\x64\x24" },
209         { 0xb7, 1, "\x90" },
210         { 0xb8, 3, "\x10\x11\x20" },
211         { 0xb9, 2, "\x31\x02" },
212         { 0xba, 3, "\x04\xa3\x9d" },
213         { 0xbb, 4, "\x15\xb2\x8c\x00" },
214         { 0xc2, 3, "\x02\x00\x00" },
215 };
216
217 static const struct cmd_data init_cmds_non_buggy_lph8923[] = {
218         { 0xc2, 3, "\x02\x00\x00" },
219 };
220
221 static inline void lph8923_set_16bit_mode(void)
222 {
223         lph8923_write(0x3a, "\x50", 1);
224 }
225
226 static void lph8923_send_init_string(void)
227 {
228         int c;
229         const struct cmd_data *cd;
230
231         switch (lph8923.version) {
232         case LPH8923_VER_BUGGY:
233                 c = sizeof(init_cmds_buggy_lph8923)/sizeof(init_cmds_buggy_lph8923[0]);
234                 cd = init_cmds_buggy_lph8923;
235                 break;
236         case LPH8923_VER_NON_BUGGY:
237         default:
238                 c = sizeof(init_cmds_non_buggy_lph8923)/sizeof(init_cmds_non_buggy_lph8923[0]);
239                 cd = init_cmds_non_buggy_lph8923;
240                 break;
241         }
242         while (c--) {
243                 lph8923_write(cd->cmd, cd->data, cd->len);
244                 cd++;
245         }
246         lph8923_set_16bit_mode();
247 }
248
249 static void hw_guard_start(int guard_msec)
250 {
251         lph8923.hw_guard_wait = msecs_to_jiffies(guard_msec);
252         lph8923.hw_guard_end = jiffies + lph8923.hw_guard_wait;
253 }
254
255 static void hw_guard_wait(void)
256 {
257         unsigned long wait = lph8923.hw_guard_end - jiffies;
258
259         if ((long)wait > 0 && wait <= lph8923.hw_guard_wait) {
260                 set_current_state(TASK_UNINTERRUPTIBLE);
261                 schedule_timeout(wait);
262         }
263 }
264
265 static void lph8923_set_sleep_mode(int on)
266 {
267         int cmd, sleep_time = 5;
268
269         if (on)
270                 cmd = LPH8923_CMD_SLEEP_IN;
271         else
272                 cmd = LPH8923_CMD_SLEEP_OUT;
273         hw_guard_wait();
274         lph8923_cmd(cmd);
275         hw_guard_start(120);
276         /* When we enable the panel, it seems we _have_ to sleep
277          * 120 ms before sending the init string */
278         if (!on)
279                 sleep_time = 120;
280         msleep(sleep_time);
281 }
282
283 static void lph8923_set_display_state(int enabled)
284 {
285         int cmd = enabled ? LPH8923_CMD_DISP_ON : LPH8923_CMD_DISP_OFF;
286
287         lph8923_cmd(cmd);
288 }
289
290 static void lph8923_detect(void)
291 {
292         lph8923_read(LPH8923_CMD_READ_DISP_ID, lph8923.display_id, 3);
293         printk(KERN_INFO "Moscow display id: %02x%02x%02x\n",
294                 lph8923.display_id[0], lph8923.display_id[1],
295                 lph8923.display_id[2]);
296
297         if (lph8923.display_id[0] == 0x45) {
298                 lph8923.version = LPH8923_VER_NON_BUGGY;
299                 printk(KERN_INFO "Non-buggy Moscow detected\n");
300                 return;
301         } else {
302                 lph8923.version = LPH8923_VER_BUGGY;
303                 printk(KERN_INFO "Buggy Moscow detected\n");
304         }
305 }
306
307 static int lph8923_enabled(void)
308 {
309         u32 disp_status;
310         int enabled;
311
312         lph8923_read(LPH8923_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4);
313         disp_status = __be32_to_cpu(disp_status);
314         enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10));
315         DBGPRINT(1, ": panel %senabled by bootloader (status 0x%04x)\n",
316                 enabled ? "" : "not ", disp_status);
317         return enabled;
318 }
319
320 static int lph8923_panel_init(struct omapfb_device *fbdev)
321 {
322         lph8923.fbdev = fbdev;
323
324         lph8923.enabled = 1;
325         lph8923_detect();
326         if (lph8923.version == LPH8923_VER_NON_BUGGY)
327                 lph8923.enabled = lph8923_enabled();
328         else
329                 /* We can't be sure, but assume the bootloader
330                 * enabled it already */
331                 lph8923.enabled = 1;
332
333         return 0;
334 }
335
336 static void lph8923_panel_cleanup(void)
337 {
338 }
339
340 static int lph8923_panel_set_bklight_level(unsigned int level)
341 {
342         if (level > tahvo_get_max_backlight_level())
343                 return -EINVAL;
344         if (!lph8923.enabled) {
345                 lph8923.saved_bklight_level = level;
346                 return 0;
347         }
348         tahvo_set_backlight_level(level);
349
350         return 0;
351 }
352
353 static unsigned int lph8923_panel_get_bklight_level(void)
354 {
355         return tahvo_get_backlight_level();
356 }
357
358 static unsigned int lph8923_panel_get_bklight_max(void)
359 {
360         return tahvo_get_max_backlight_level();
361 }
362
363 static int lph8923_panel_enable(void)
364 {
365         if (lph8923.enabled)
366                 return 0;
367
368         lph8923_set_sleep_mode(0);
369         lph8923.enabled = 1;
370         lph8923_send_init_string();
371         lph8923_set_display_state(1);
372         lph8923_panel_set_bklight_level(lph8923.saved_bklight_level);
373
374         return 0;
375 }
376
377 static void lph8923_panel_disable(void)
378 {
379         if (!lph8923.enabled)
380                 return;
381         lph8923.saved_bklight_level = lph8923_panel_get_bklight_level();
382         lph8923_panel_set_bklight_level(0);
383         lph8923_set_display_state(0);
384         lph8923_set_sleep_mode(1);
385         lph8923.enabled = 0;
386 }
387
388 static unsigned long lph8923_panel_get_caps(void)
389 {
390         return OMAPFB_CAPS_SET_BACKLIGHT;
391 }
392
393 static u16 read_first_pixel(void)
394 {
395         u8 b;
396         u16 pixel;
397
398         lph8923_read(LPH8923_CMD_READ_RED, &b, 1);
399         pixel = (b >> 1) << 11;
400         lph8923_read(LPH8923_CMD_READ_GREEN, &b, 1);
401         pixel |= b << 5;
402         lph8923_read(LPH8923_CMD_READ_BLUE, &b, 1);
403         pixel |= (b >> 1);
404
405         return pixel;
406 }
407
408 static int lph8923_panel_test(int test_num)
409 {
410         static const u16 test_values[4] = {
411                 0x0000, 0xffff, 0xaaaa, 0x5555,
412         };
413         int i;
414
415         if (test_num != LCD_LPH8923_TEST_RGB_LINES)
416                 return LCD_LPH8923_TEST_INVALID;
417
418         for (i = 0; i < ARRAY_SIZE(test_values); i++) {
419                 int delay;
420                 unsigned long tmo;
421
422                 omapfb_write_first_pixel(lph8923.fbdev, test_values[i]);
423                 tmo = jiffies + msecs_to_jiffies(100);
424                 delay = msecs_to_jiffies(25);
425                 while (1) {
426                         u16 pixel;
427
428                         set_current_state(TASK_UNINTERRUPTIBLE);
429                         schedule_timeout(delay);
430                         pixel = read_first_pixel();
431                         if (pixel == test_values[i])
432                                 break;
433                         if (time_after(jiffies, tmo)) {
434                                 printk(KERN_ERR "Moscow RGB I/F test failed: "
435                                        "expecting %04x, got %04x\n",
436                                        test_values[i], pixel);
437                                 return LCD_LPH8923_TEST_FAILED;
438                         }
439                         delay = msecs_to_jiffies(10);
440                 }
441         }
442
443         return 0;
444 }
445
446 static struct lcd_panel lph8923_panel = {
447         .name           = "lph8923",
448         .config         = OMAP_LCDC_PANEL_TFT,
449
450         .bpp            = 16,
451         .data_lines     = 16,
452         .x_res          = 800,
453         .y_res          = 480,
454         .pixel_clock    = 21940,
455         .hsw            = 50,
456         .hfp            = 20,
457         .hbp            = 15,
458         .vsw            = 2,
459         .vfp            = 1,
460         .vbp            = 3,
461
462         .init           = lph8923_panel_init,
463         .cleanup        = lph8923_panel_cleanup,
464         .enable         = lph8923_panel_enable,
465         .disable        = lph8923_panel_disable,
466         .get_caps       = lph8923_panel_get_caps,
467         .set_bklight_level= lph8923_panel_set_bklight_level,
468         .get_bklight_level= lph8923_panel_get_bklight_level,
469         .get_bklight_max= lph8923_panel_get_bklight_max,
470         .run_test       = lph8923_panel_test,
471 };