Merge branch 'spi/merge' of git://git.secretlab.ca/git/linux-2.6
[pandora-kernel.git] / drivers / staging / olpc_dcon / olpc_dcon.c
1 /*
2  * Mainly by David Woodhouse, somewhat modified by Jordan Crouse
3  *
4  * Copyright © 2006-2007  Red Hat, Inc.
5  * Copyright © 2006-2007  Advanced Micro Devices, Inc.
6  * Copyright © 2009       VIA Technology, Inc.
7  * Copyright (c) 2010  Andres Salomon <dilinger@queued.net>
8  *
9  * This program is free software.  You can redistribute it and/or
10  * modify it under the terms of version 2 of the GNU General Public
11  * License as published by the Free Software Foundation.
12  */
13
14
15 #include <linux/kernel.h>
16 #include <linux/fb.h>
17 #include <linux/console.h>
18 #include <linux/i2c.h>
19 #include <linux/platform_device.h>
20 #include <linux/pci.h>
21 #include <linux/pci_ids.h>
22 #include <linux/interrupt.h>
23 #include <linux/delay.h>
24 #include <linux/backlight.h>
25 #include <linux/device.h>
26 #include <linux/notifier.h>
27 #include <asm/uaccess.h>
28 #include <linux/ctype.h>
29 #include <linux/reboot.h>
30 #include <asm/tsc.h>
31 #include <asm/olpc.h>
32
33 #include "olpc_dcon.h"
34
35 /* Module definitions */
36
37 static int resumeline = 898;
38 module_param(resumeline, int, 0444);
39
40 static int noinit;
41 module_param(noinit, int, 0444);
42
43 /* Default off since it doesn't work on DCON ASIC in B-test OLPC board */
44 static int useaa = 1;
45 module_param(useaa, int, 0444);
46
47 struct dcon_platform_data {
48         int (*init)(void);
49         void (*bus_stabilize_wiggle)(void);
50         void (*set_dconload)(int);
51         u8 (*read_status)(void);
52 };
53
54 static struct dcon_platform_data *pdata;
55
56 /* I2C structures */
57
58 static struct i2c_driver dcon_driver;
59 static struct i2c_client *dcon_client;
60
61 /* Platform devices */
62 static struct platform_device *dcon_device;
63
64 /* Backlight device */
65 static struct backlight_device *dcon_bl_dev;
66
67 static struct fb_info *fbinfo;
68
69 /* set this to 1 while controlling fb blank state from this driver */
70 static int ignore_fb_events = 0;
71
72 /* Current source, initialized at probe time */
73 static int dcon_source;
74
75 /* Desired source */
76 static int dcon_pending;
77
78 /* Current output type */
79 static int dcon_output = DCON_OUTPUT_COLOR;
80
81 /* Current sleep status (not yet implemented) */
82 static int dcon_sleep_val = DCON_ACTIVE;
83
84 /* Shadow register for the DCON_REG_MODE register */
85 static unsigned short dcon_disp_mode;
86
87 /* Variables used during switches */
88 static int dcon_switched;
89 static struct timespec dcon_irq_time;
90 static struct timespec dcon_load_time; 
91
92 static DECLARE_WAIT_QUEUE_HEAD(dcon_wait_queue);
93
94 static unsigned short normal_i2c[] = { 0x0d, I2C_CLIENT_END };
95
96 #define dcon_write(reg,val) i2c_smbus_write_word_data(dcon_client,reg,val)
97 #define dcon_read(reg) i2c_smbus_read_word_data(dcon_client,reg)
98
99 /* The current backlight value - this saves us some smbus traffic */
100 static int bl_val = -1;
101
102 /* ===== API functions - these are called by a variety of users ==== */
103
104 static int dcon_hw_init(struct i2c_client *client, int is_init)
105 {
106         uint16_t ver;
107         int rc = 0;
108
109         ver = i2c_smbus_read_word_data(client, DCON_REG_ID);
110         if ((ver >> 8) != 0xDC) {
111                 printk(KERN_ERR "olpc-dcon:  DCON ID not 0xDCxx: 0x%04x "
112                                 "instead.\n", ver);
113                 rc = -ENXIO;
114                 goto err;
115         }
116
117         if (is_init) {
118                 printk(KERN_INFO "olpc-dcon:  Discovered DCON version %x\n",
119                                 ver & 0xFF);
120                 if ((rc = pdata->init()) != 0) {
121                         printk(KERN_ERR "olpc-dcon:  Unable to init.\n");
122                         goto err;
123                 }
124         }
125
126         if (ver < 0xdc02 && !noinit) {
127                 /* Initialize the DCON registers */
128
129                 /* Start with work-arounds for DCON ASIC */
130                 i2c_smbus_write_word_data(client, 0x4b, 0x00cc);
131                 i2c_smbus_write_word_data(client, 0x4b, 0x00cc);
132                 i2c_smbus_write_word_data(client, 0x4b, 0x00cc);
133                 i2c_smbus_write_word_data(client, 0x0b, 0x007a);
134                 i2c_smbus_write_word_data(client, 0x36, 0x025c);
135                 i2c_smbus_write_word_data(client, 0x37, 0x025e);
136                 
137                 /* Initialise SDRAM */
138
139                 i2c_smbus_write_word_data(client, 0x3b, 0x002b);
140                 i2c_smbus_write_word_data(client, 0x41, 0x0101);
141                 i2c_smbus_write_word_data(client, 0x42, 0x0101);
142         }
143         else if (!noinit) {
144                 /* SDRAM setup/hold time */
145                 i2c_smbus_write_word_data(client, 0x3a, 0xc040);
146                 i2c_smbus_write_word_data(client, 0x41, 0x0000);
147                 i2c_smbus_write_word_data(client, 0x41, 0x0101);
148                 i2c_smbus_write_word_data(client, 0x42, 0x0101);
149         }
150
151         /* Colour swizzle, AA, no passthrough, backlight */
152         if (is_init) {
153                 dcon_disp_mode = MODE_PASSTHRU | MODE_BL_ENABLE | MODE_CSWIZZLE;
154                 if (useaa)
155                         dcon_disp_mode |= MODE_COL_AA;
156         }
157         i2c_smbus_write_word_data(client, DCON_REG_MODE, dcon_disp_mode);
158
159
160         /* Set the scanline to interrupt on during resume */
161         i2c_smbus_write_word_data(client, DCON_REG_SCAN_INT, resumeline);
162
163 err:
164         return rc;
165 }
166
167 /*
168  * The smbus doesn't always come back due to what is believed to be
169  * hardware (power rail) bugs.  For older models where this is known to
170  * occur, our solution is to attempt to wait for the bus to stabilize;
171  * if it doesn't happen, cut power to the dcon, repower it, and wait
172  * for the bus to stabilize.  Rinse, repeat until we have a working
173  * smbus.  For newer models, we simply BUG(); we want to know if this
174  * still happens despite the power fixes that have been made!
175  */
176 static int dcon_bus_stabilize(struct i2c_client *client, int is_powered_down)
177 {
178         unsigned long timeout;
179         int x;
180
181 power_up:
182         if (is_powered_down) {
183                 x = 1;
184                 if ((x = olpc_ec_cmd(0x26, (unsigned char *) &x, 1, NULL, 0))) {
185                         printk(KERN_WARNING "olpc-dcon:  unable to force dcon "
186                                         "to power up: %d!\n", x);
187                         return x;
188                 }
189                 msleep(10); /* we'll be conservative */
190         }
191         
192         pdata->bus_stabilize_wiggle();
193
194         for (x = -1, timeout = 50; timeout && x < 0; timeout--) {
195                 msleep(1);
196                 x = dcon_read(DCON_REG_ID);
197         }
198         if (x < 0) {
199                 printk(KERN_ERR "olpc-dcon:  unable to stabilize dcon's "
200                                 "smbus, reasserting power and praying.\n");
201                 BUG_ON(olpc_board_at_least(olpc_board(0xc2)));
202                 x = 0;
203                 olpc_ec_cmd(0x26, (unsigned char *) &x, 1, NULL, 0);
204                 msleep(100);
205                 is_powered_down = 1;
206                 goto power_up;  /* argh, stupid hardware.. */
207         }
208
209         if (is_powered_down)
210                 return dcon_hw_init(client, 0);
211         return 0;
212 }
213
214 static int dcon_get_backlight(void)
215 {
216         if (dcon_client == NULL)
217                 return 0;
218
219         if (bl_val == -1)
220                 bl_val = dcon_read(DCON_REG_BRIGHT) & 0x0F;
221
222         return bl_val;
223 }
224
225
226 static void dcon_set_backlight_hw(int level)
227 {
228         bl_val = level & 0x0F;
229         dcon_write(DCON_REG_BRIGHT, bl_val);
230
231         /* Purposely turn off the backlight when we go to level 0 */
232         if (bl_val == 0) {
233                 dcon_disp_mode &= ~MODE_BL_ENABLE;
234                 dcon_write(DCON_REG_MODE, dcon_disp_mode);
235         } else if (!(dcon_disp_mode & MODE_BL_ENABLE)) {
236                 dcon_disp_mode |= MODE_BL_ENABLE;
237                 dcon_write(DCON_REG_MODE, dcon_disp_mode);
238         }
239 }
240
241 static void dcon_set_backlight(int level)
242 {
243         if (dcon_client == NULL)
244                 return;
245
246         if (bl_val == (level & 0x0F))
247                 return;
248
249         dcon_set_backlight_hw(level);
250 }
251
252 /* Set the output type to either color or mono */
253
254 static int dcon_set_output(int arg)
255 {
256         if (dcon_output == arg)
257                 return 0;
258
259         dcon_output = arg;
260
261         if (arg == DCON_OUTPUT_MONO) {
262                 dcon_disp_mode &= ~(MODE_CSWIZZLE | MODE_COL_AA);
263                 dcon_disp_mode |= MODE_MONO_LUMA;
264         }
265         else {
266                 dcon_disp_mode &= ~(MODE_MONO_LUMA);
267                 dcon_disp_mode |= MODE_CSWIZZLE;
268                 if (useaa)
269                         dcon_disp_mode |= MODE_COL_AA;
270         }
271
272         dcon_write(DCON_REG_MODE, dcon_disp_mode);
273         return 0;
274 }
275
276 /* For now, this will be really stupid - we need to address how
277  * DCONLOAD works in a sleep and account for it accordingly
278  */
279
280 static void dcon_sleep(int state)
281 {
282         int x;
283
284         /* Turn off the backlight and put the DCON to sleep */
285
286         if (state == dcon_sleep_val)
287                 return;
288
289         if (!olpc_board_at_least(olpc_board(0xc2)))
290                 return;
291
292         if (state == DCON_SLEEP) {
293                 x = 0;
294                 if ((x = olpc_ec_cmd(0x26, (unsigned char *) &x, 1, NULL, 0)))
295                         printk(KERN_WARNING "olpc-dcon:  unable to force dcon "
296                                         "to power down: %d!\n", x);
297                 else
298                         dcon_sleep_val = state;
299         }
300         else {
301                 /* Only re-enable the backlight if the backlight value is set */
302                 if (bl_val != 0)
303                         dcon_disp_mode |= MODE_BL_ENABLE;
304
305                 if ((x=dcon_bus_stabilize(dcon_client, 1)))
306                         printk(KERN_WARNING "olpc-dcon:  unable to reinit dcon"
307                                         " hardware: %d!\n", x);
308                 else
309                         dcon_sleep_val = state;
310
311                 /* Restore backlight */
312                 dcon_set_backlight_hw(bl_val);
313         }
314
315         /* We should turn off some stuff in the framebuffer - but what? */
316 }
317
318 /* the DCON seems to get confused if we change DCONLOAD too
319  * frequently -- i.e., approximately faster than frame time. 
320  * normally we don't change it this fast, so in general we won't
321  * delay here.
322  */
323 void dcon_load_holdoff(void)
324 {
325         struct timespec delta_t, now;
326         while(1) {
327                 getnstimeofday(&now);
328                 delta_t = timespec_sub(now, dcon_load_time);
329                 if (delta_t.tv_sec != 0 ||
330                         delta_t.tv_nsec > NSEC_PER_MSEC * 20) {
331                         break;
332                 }
333                 mdelay(4);
334         }
335 }
336 /* Set the source of the display (CPU or DCON) */
337
338 static void dcon_source_switch(struct work_struct *work)
339 {
340         DECLARE_WAITQUEUE(wait, current);
341         int source = dcon_pending;
342
343         if (dcon_source == source)
344                 return;
345
346         dcon_load_holdoff();
347
348         dcon_switched = 0;
349
350         switch (source) {
351         case DCON_SOURCE_CPU:
352                 printk("dcon_source_switch to CPU\n");
353                 /* Enable the scanline interrupt bit */
354                 if (dcon_write(DCON_REG_MODE, dcon_disp_mode | MODE_SCAN_INT))
355                         printk(KERN_ERR "olpc-dcon:  couldn't enable scanline interrupt!\n");
356                 else {
357                         /* Wait up to one second for the scanline interrupt */
358                         wait_event_timeout(dcon_wait_queue, dcon_switched == 1, HZ);
359                 }
360
361                 if (!dcon_switched)
362                         printk(KERN_ERR "olpc-dcon:  Timeout entering CPU mode; expect a screen glitch.\n");
363
364                 /* Turn off the scanline interrupt */
365                 if (dcon_write(DCON_REG_MODE, dcon_disp_mode))
366                         printk(KERN_ERR "olpc-dcon:  couldn't disable scanline interrupt!\n");
367
368                 /*
369                  * Ideally we'd like to disable interrupts here so that the
370                  * fb unblanking and DCON turn on happen at a known time value;
371                  * however, we can't do that right now with fb_blank
372                  * messing with semaphores.
373                  *
374                  * For now, we just hope..
375                  */
376                 console_lock();
377                 ignore_fb_events = 1;
378                 if (fb_blank(fbinfo, FB_BLANK_UNBLANK)) {
379                         ignore_fb_events = 0;
380                         console_unlock();
381                         printk(KERN_ERR "olpc-dcon:  Failed to enter CPU mode\n");
382                         dcon_pending = DCON_SOURCE_DCON;
383                         return;
384                 }
385                 ignore_fb_events = 0;
386                 console_unlock();
387
388                 /* And turn off the DCON */
389                 pdata->set_dconload(1);
390                 getnstimeofday(&dcon_load_time);
391
392                 printk(KERN_INFO "olpc-dcon: The CPU has control\n");
393                 break;
394         case DCON_SOURCE_DCON:
395         {
396                 int t;
397                 struct timespec delta_t;
398
399                 printk("dcon_source_switch to DCON\n");
400
401                 add_wait_queue(&dcon_wait_queue, &wait);
402                 set_current_state(TASK_UNINTERRUPTIBLE);
403
404                 /* Clear DCONLOAD - this implies that the DCON is in control */
405                 pdata->set_dconload(0);
406                 getnstimeofday(&dcon_load_time);
407
408                 t = schedule_timeout(HZ/2);
409                 remove_wait_queue(&dcon_wait_queue, &wait);
410                 set_current_state(TASK_RUNNING);
411
412                 if (!dcon_switched) {
413                         printk(KERN_ERR "olpc-dcon: Timeout entering DCON mode; expect a screen glitch.\n");
414                 } else {
415                         /* sometimes the DCON doesn't follow its own rules,
416                          * and doesn't wait for two vsync pulses before
417                          * ack'ing the frame load with an IRQ.  the result
418                          * is that the display shows the *previously*
419                          * loaded frame.  we can detect this by looking at
420                          * the time between asserting DCONLOAD and the IRQ --
421                          * if it's less than 20msec, then the DCON couldn't
422                          * have seen two VSYNC pulses.  in that case we
423                          * deassert and reassert, and hope for the best. 
424                          * see http://dev.laptop.org/ticket/9664
425                          */
426                         delta_t = timespec_sub(dcon_irq_time, dcon_load_time);
427                         if (dcon_switched && delta_t.tv_sec == 0 &&
428                                         delta_t.tv_nsec < NSEC_PER_MSEC * 20) {
429                                 printk(KERN_ERR "olpc-dcon: missed loading, retrying\n");
430                                 pdata->set_dconload(1);
431                                 mdelay(41);
432                                 pdata->set_dconload(0);
433                                 getnstimeofday(&dcon_load_time);
434                                 mdelay(41);
435                         }
436                 }
437
438                 console_lock();
439                 ignore_fb_events = 1;
440                 if (fb_blank(fbinfo, FB_BLANK_POWERDOWN))
441                         printk(KERN_ERR "olpc-dcon:  couldn't blank fb!\n");
442                 ignore_fb_events = 0;
443                 console_unlock();
444
445                 printk(KERN_INFO "olpc-dcon: The DCON has control\n");
446                 break;
447         }
448         default:
449                 BUG();
450         }
451
452         dcon_source = source;
453 }
454
455 static DECLARE_WORK(dcon_work, dcon_source_switch);
456
457 static void dcon_set_source(int arg)
458 {
459         if (dcon_pending == arg)
460                 return;
461
462         dcon_pending = arg;
463
464         if ((dcon_source != arg) && !work_pending(&dcon_work))
465                 schedule_work(&dcon_work);
466 }
467
468 static void dcon_set_source_sync(int arg)
469 {
470         dcon_set_source(arg);
471         flush_scheduled_work();
472 }
473
474 static int dconbl_set(struct backlight_device *dev) {
475
476         int level = dev->props.brightness;
477
478         if (dev->props.power != FB_BLANK_UNBLANK)
479                 level = 0;
480
481         dcon_set_backlight(level);
482         return 0;
483 }
484
485 static int dconbl_get(struct backlight_device *dev) {
486         return dcon_get_backlight();
487 }
488
489 static ssize_t dcon_mode_show(struct device *dev,
490         struct device_attribute *attr, char *buf)
491 {
492         return sprintf(buf, "%4.4X\n", dcon_disp_mode);
493 }
494
495 static ssize_t dcon_sleep_show(struct device *dev,
496         struct device_attribute *attr, char *buf)
497 {
498
499         return sprintf(buf, "%d\n", dcon_sleep_val);
500 }
501
502 static ssize_t dcon_freeze_show(struct device *dev,
503         struct device_attribute *attr, char *buf)
504 {
505         return sprintf(buf, "%d\n", dcon_source == DCON_SOURCE_DCON ? 1 : 0);
506 }
507
508 static ssize_t dcon_output_show(struct device *dev,
509         struct device_attribute *attr, char *buf)
510 {
511         return sprintf(buf, "%d\n", dcon_output);
512 }
513
514 static ssize_t dcon_resumeline_show(struct device *dev,
515         struct device_attribute *attr, char *buf)
516 {
517         return sprintf(buf, "%d\n", resumeline);
518 }
519
520 static int _strtoul(const char *buf, int len, unsigned int *val)
521 {
522
523         char *endp;
524         unsigned int output = simple_strtoul(buf, &endp, 0);
525         int size = endp - buf;
526
527         if (*endp && isspace(*endp))
528                 size++;
529
530         if (size != len)
531                 return -EINVAL;
532
533         *val = output;
534         return 0;
535 }
536
537 static ssize_t dcon_output_store(struct device *dev,
538         struct device_attribute *attr, const char *buf, size_t count)
539 {
540         int output;
541         int rc = -EINVAL;
542
543         if (_strtoul(buf, count, &output))
544                 return -EINVAL;
545
546         if (output == DCON_OUTPUT_COLOR || output == DCON_OUTPUT_MONO) {
547                 dcon_set_output(output);
548                 rc = count;
549         }
550
551         return rc;
552 }
553
554 static ssize_t dcon_freeze_store(struct device *dev,
555         struct device_attribute *attr, const char *buf, size_t count)
556 {
557         int output;
558
559         if (_strtoul(buf, count, &output))
560                 return -EINVAL;
561
562         printk("dcon_freeze_store: %d\n", output);
563
564         switch (output) {
565         case 0:
566                 dcon_set_source(DCON_SOURCE_CPU);
567                 break;
568         case 1:
569                 dcon_set_source_sync(DCON_SOURCE_DCON);
570                 break;
571         case 2:  // normally unused
572                 dcon_set_source(DCON_SOURCE_DCON);
573                 break;
574         default:
575                 return -EINVAL;
576         }
577
578         return count;
579 }
580
581 static ssize_t dcon_resumeline_store(struct device *dev,
582         struct device_attribute *attr, const char *buf, size_t count)
583 {
584         int rl;
585         int rc = -EINVAL;
586
587         if (_strtoul(buf, count, &rl))
588                 return rc;
589
590         resumeline = rl;
591         dcon_write(DCON_REG_SCAN_INT, resumeline);
592         rc = count;
593
594         return rc;
595 }
596
597 static ssize_t dcon_sleep_store(struct device *dev,
598         struct device_attribute *attr, const char *buf, size_t count)
599 {
600         int output;
601
602         if (_strtoul(buf, count, &output))
603                 return -EINVAL;
604
605         dcon_sleep(output ? DCON_SLEEP : DCON_ACTIVE);
606         return count;
607 }
608
609 static struct device_attribute dcon_device_files[] = {
610         __ATTR(mode, 0444, dcon_mode_show, NULL),
611         __ATTR(sleep, 0644, dcon_sleep_show, dcon_sleep_store),
612         __ATTR(freeze, 0644, dcon_freeze_show, dcon_freeze_store),
613         __ATTR(output, 0644, dcon_output_show, dcon_output_store),
614         __ATTR(resumeline, 0644, dcon_resumeline_show, dcon_resumeline_store),
615 };
616
617 static const struct backlight_ops dcon_bl_ops = {
618         .get_brightness = dconbl_get,
619         .update_status = dconbl_set
620 };
621
622
623 static int dcon_reboot_notify(struct notifier_block *nb, unsigned long foo, void *bar)
624 {
625         if (dcon_client == NULL)
626                 return 0;
627
628         /* Turn off the DCON. Entirely. */
629         dcon_write(DCON_REG_MODE, 0x39);
630         dcon_write(DCON_REG_MODE, 0x32);
631         return 0;
632 }
633
634 static struct notifier_block dcon_nb = {
635         .notifier_call = dcon_reboot_notify,
636         .priority = -1,
637 };
638
639 static int unfreeze_on_panic(struct notifier_block *nb, unsigned long e, void *p)
640 {
641         pdata->set_dconload(1);
642         return NOTIFY_DONE;
643 }
644
645 static struct notifier_block dcon_panic_nb = {
646         .notifier_call = unfreeze_on_panic,
647 };
648
649 /*
650  * When the framebuffer sleeps due to external sources (e.g. user idle), power
651  * down the DCON as well.  Power it back up when the fb comes back to life.
652  */
653 static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
654 {
655         struct fb_event *evdata = data;
656         int *blank = (int *) evdata->data;
657         if (((event != FB_EVENT_BLANK) && (event != FB_EVENT_CONBLANK)) ||
658                         ignore_fb_events)
659                 return 0;
660         dcon_sleep((*blank) ? DCON_SLEEP : DCON_ACTIVE);
661         return 0;
662 }
663
664 static struct notifier_block fb_nb = {
665         .notifier_call = fb_notifier_callback,
666 };
667
668 static int dcon_detect(struct i2c_client *client, struct i2c_board_info *info)
669 {
670         strlcpy(info->type, "olpc_dcon", I2C_NAME_SIZE);
671
672         return 0;
673 }
674
675 static int dcon_probe(struct i2c_client *client, const struct i2c_device_id *id)
676 {
677         int rc, i;
678
679         if (num_registered_fb >= 1)
680                 fbinfo = registered_fb[0];
681
682         rc = dcon_hw_init(client, 1);
683         if (rc)
684                 goto einit;
685
686         /* Add the DCON device */
687
688         dcon_device = platform_device_alloc("dcon", -1);
689
690         if (dcon_device == NULL) {
691                 printk("dcon:  Unable to create the DCON device\n");
692                 rc = -ENOMEM;
693                 goto eirq;
694         }
695         /* Place holder...*/
696         i2c_set_clientdata(client, dcon_device);
697
698         if ((rc = platform_device_add(dcon_device))) {
699                 printk("dcon:  Unable to add the DCON device\n");
700                 goto edev;
701         }
702
703         for(i = 0; i < ARRAY_SIZE(dcon_device_files); i++)
704                 device_create_file(&dcon_device->dev, &dcon_device_files[i]);
705
706         /* Add the backlight device for the DCON */
707
708         dcon_client = client;
709
710         dcon_bl_dev = backlight_device_register("dcon-bl", &dcon_device->dev,
711                 NULL, &dcon_bl_ops, NULL);
712
713         if (IS_ERR(dcon_bl_dev)) {
714                 printk("Could not register the backlight device for the DCON (%ld)\n", PTR_ERR(dcon_bl_dev));
715                 dcon_bl_dev = NULL;
716         }
717         else {
718                 dcon_bl_dev->props.max_brightness = 15;
719                 dcon_bl_dev->props.power = FB_BLANK_UNBLANK;
720                 dcon_bl_dev->props.brightness = dcon_get_backlight();
721
722                 backlight_update_status(dcon_bl_dev);
723         }
724
725         register_reboot_notifier(&dcon_nb);
726         atomic_notifier_chain_register(&panic_notifier_list, &dcon_panic_nb);
727         fb_register_client(&fb_nb);
728
729         return 0;
730
731  edev:
732         platform_device_unregister(dcon_device);
733         dcon_device = NULL;
734  eirq:
735         free_irq(DCON_IRQ, &dcon_driver);
736  einit:
737         return rc;
738 }
739
740 static int dcon_remove(struct i2c_client *client)
741 {
742         dcon_client = NULL;
743
744         fb_unregister_client(&fb_nb);
745         unregister_reboot_notifier(&dcon_nb);
746         atomic_notifier_chain_unregister(&panic_notifier_list, &dcon_panic_nb);
747
748         free_irq(DCON_IRQ, &dcon_driver);
749
750         if (dcon_bl_dev != NULL)
751                 backlight_device_unregister(dcon_bl_dev);
752
753         if (dcon_device != NULL)
754                 platform_device_unregister(dcon_device);
755         cancel_work_sync(&dcon_work);
756
757         return 0;
758 }
759
760 #ifdef CONFIG_PM
761 static int dcon_suspend(struct i2c_client *client, pm_message_t state)
762 {
763         if (dcon_sleep_val == DCON_ACTIVE) {
764                 /* Set up the DCON to have the source */
765                 dcon_set_source_sync(DCON_SOURCE_DCON);
766         }
767
768         return 0;
769 }
770
771 static int dcon_resume(struct i2c_client *client)
772 {
773         if (dcon_sleep_val == DCON_ACTIVE) {
774                 dcon_bus_stabilize(client, 0);
775                 dcon_set_source(DCON_SOURCE_CPU);
776         }
777
778         return 0;
779 }
780
781 #endif
782
783
784 static irqreturn_t dcon_interrupt(int irq, void *id)
785 {
786         int status = pdata->read_status();
787
788         if (status == -1)
789                 return IRQ_NONE;
790
791         switch (status & 3) {
792         case 3:
793                 printk(KERN_DEBUG "olpc-dcon: DCONLOAD_MISSED interrupt\n");
794                 break;
795
796         case 2: /* switch to DCON mode */
797         case 1: /* switch to CPU mode */
798                 dcon_switched = 1;
799                 getnstimeofday(&dcon_irq_time);
800                 wake_up(&dcon_wait_queue);
801                 break;
802
803         case 0:
804                 /* workaround resume case:  the DCON (on 1.5) doesn't
805                  * ever assert status 0x01 when switching to CPU mode
806                  * during resume.  this is because DCONLOAD is de-asserted
807                  * _immediately_ upon exiting S3, so the actual release
808                  * of the DCON happened long before this point.
809                  * see http://dev.laptop.org/ticket/9869
810                  */
811                 if (dcon_source != dcon_pending && !dcon_switched) {
812                         dcon_switched = 1;
813                         getnstimeofday(&dcon_irq_time);
814                         wake_up(&dcon_wait_queue);
815                         printk(KERN_DEBUG "olpc-dcon: switching w/ status 0/0\n");
816                 } else {
817                         printk(KERN_DEBUG "olpc-dcon: scanline interrupt w/CPU\n");
818                 }
819         }
820
821         return IRQ_HANDLED;
822 }
823
824 static struct i2c_device_id dcon_idtable[] = {
825         { "olpc_dcon",  0 },
826         { }
827 };
828
829 MODULE_DEVICE_TABLE(i2c, dcon_idtable);
830
831 static struct i2c_driver dcon_driver = {
832         .driver = {
833                 .name   = "olpc_dcon",
834         },
835         .class = I2C_CLASS_DDC | I2C_CLASS_HWMON,
836         .id_table = dcon_idtable,
837         .probe = dcon_probe,
838         .remove = __devexit_p(dcon_remove),
839         .detect = dcon_detect,
840         .address_list = normal_i2c,
841 #ifdef CONFIG_PM
842         .suspend = dcon_suspend,
843         .resume = dcon_resume,
844 #endif
845 };
846
847 #include "olpc_dcon_xo_1.c"
848
849 static int __init olpc_dcon_init(void)
850 {
851         pdata = &dcon_pdata_xo_1;
852
853         i2c_add_driver(&dcon_driver);
854         return 0;
855 }
856
857 static void __exit olpc_dcon_exit(void)
858 {
859         i2c_del_driver(&dcon_driver);
860 }
861
862 module_init(olpc_dcon_init);
863 module_exit(olpc_dcon_exit);
864
865 MODULE_LICENSE("GPL");