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 acquire_console_sem(); 377 ignore_fb_events = 1; 378 if (fb_blank(fbinfo, FB_BLANK_UNBLANK)) { 379 ignore_fb_events = 0; 380 release_console_sem(); 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 release_console_sem(); 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 acquire_console_sem(); 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 release_console_sem(); 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"); 866