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