linux/drivers/staging/olpc_dcon/olpc_dcon.c
<<
>>
Prefs
   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
  39static ushort resumeline = 898;
  40module_param(resumeline, ushort, 0444);
  41
  42static struct dcon_platform_data *pdata;
  43
  44/* I2C structures */
  45
  46/* Platform devices */
  47static struct platform_device *dcon_device;
  48
  49static unsigned short normal_i2c[] = { 0x0d, I2C_CLIENT_END };
  50
  51static 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
  56static 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
  63static 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
 108err:
 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 */
 121static int dcon_bus_stabilize(struct dcon_priv *dcon, int is_powered_down)
 122{
 123        unsigned long timeout;
 124        int x;
 125
 126power_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
 158static 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 */
 174static 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
 197static 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 */
 238static 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
 252static 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) */
 277static 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
 377static 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
 388static void dcon_set_source_sync(struct dcon_priv *dcon, int arg)
 389{
 390        dcon_set_source(dcon, arg);
 391        flush_scheduled_work();
 392}
 393
 394static 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
 401static 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
 409static 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
 416static 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
 423static 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
 429static 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
 444static 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
 474static 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
 490static 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
 504static 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
 512static 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
 530static 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
 536static const struct backlight_ops dcon_bl_ops = {
 537        .update_status = dcon_bl_update,
 538        .get_brightness = dcon_bl_get,
 539};
 540
 541static struct backlight_properties dcon_bl_props = {
 542        .max_brightness = 15,
 543        .type = BACKLIGHT_RAW,
 544        .power = FB_BLANK_UNBLANK,
 545};
 546
 547static 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 NOTIFY_DONE;
 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 NOTIFY_DONE;
 559}
 560
 561static 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
 568static struct notifier_block dcon_panic_nb = {
 569        .notifier_call = unfreeze_on_panic,
 570};
 571
 572static 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
 579static 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
 666static 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
 688static 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
 701static 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
 722irqreturn_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
 763static const struct dev_pm_ops dcon_pm_ops = {
 764        .suspend = dcon_suspend,
 765        .resume = dcon_resume,
 766};
 767
 768static const struct i2c_device_id dcon_idtable[] = {
 769        { "olpc_dcon",  0 },
 770        { }
 771};
 772MODULE_DEVICE_TABLE(i2c, dcon_idtable);
 773
 774struct 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
 787static 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
 802static void __exit olpc_dcon_exit(void)
 803{
 804        i2c_del_driver(&dcon_driver);
 805}
 806
 807module_init(olpc_dcon_init);
 808module_exit(olpc_dcon_exit);
 809
 810MODULE_LICENSE("GPL");
 811