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