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