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