linux/drivers/macintosh/via-pmu-backlight.c
<<
>>
Prefs
   1/*
   2 * Backlight code for via-pmu
   3 *
   4 * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
   5 * Copyright (C) 2001-2002 Benjamin Herrenschmidt
   6 * Copyright (C) 2006      Michael Hanselmann <linux-kernel@hansmi.ch>
   7 *
   8 */
   9
  10#include <asm/ptrace.h>
  11#include <linux/adb.h>
  12#include <linux/pmu.h>
  13#include <asm/backlight.h>
  14#include <asm/prom.h>
  15
  16#define MAX_PMU_LEVEL 0xFF
  17
  18static const struct backlight_ops pmu_backlight_data;
  19static DEFINE_SPINLOCK(pmu_backlight_lock);
  20static int sleeping, uses_pmu_bl;
  21static u8 bl_curve[FB_BACKLIGHT_LEVELS];
  22
  23static void pmu_backlight_init_curve(u8 off, u8 min, u8 max)
  24{
  25        int i, flat, count, range = (max - min);
  26
  27        bl_curve[0] = off;
  28
  29        for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
  30                bl_curve[flat] = min;
  31
  32        count = FB_BACKLIGHT_LEVELS * 15 / 16;
  33        for (i = 0; i < count; ++i)
  34                bl_curve[flat + i] = min + (range * (i + 1) / count);
  35}
  36
  37static int pmu_backlight_curve_lookup(int value)
  38{
  39        int level = (FB_BACKLIGHT_LEVELS - 1);
  40        int i, max = 0;
  41
  42        /* Look for biggest value */
  43        for (i = 0; i < FB_BACKLIGHT_LEVELS; i++)
  44                max = max((int)bl_curve[i], max);
  45
  46        /* Look for nearest value */
  47        for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) {
  48                int diff = abs(bl_curve[i] - value);
  49                if (diff < max) {
  50                        max = diff;
  51                        level = i;
  52                }
  53        }
  54        return level;
  55}
  56
  57static int pmu_backlight_get_level_brightness(int level)
  58{
  59        int pmulevel;
  60
  61        /* Get and convert the value */
  62        pmulevel = bl_curve[level] * FB_BACKLIGHT_MAX / MAX_PMU_LEVEL;
  63        if (pmulevel < 0)
  64                pmulevel = 0;
  65        else if (pmulevel > MAX_PMU_LEVEL)
  66                pmulevel = MAX_PMU_LEVEL;
  67
  68        return pmulevel;
  69}
  70
  71static int __pmu_backlight_update_status(struct backlight_device *bd)
  72{
  73        struct adb_request req;
  74        int level = bd->props.brightness;
  75
  76
  77        if (bd->props.power != FB_BLANK_UNBLANK ||
  78            bd->props.fb_blank != FB_BLANK_UNBLANK)
  79                level = 0;
  80
  81        if (level > 0) {
  82                int pmulevel = pmu_backlight_get_level_brightness(level);
  83
  84                pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, pmulevel);
  85                pmu_wait_complete(&req);
  86
  87                pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
  88                        PMU_POW_BACKLIGHT | PMU_POW_ON);
  89                pmu_wait_complete(&req);
  90        } else {
  91                pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
  92                        PMU_POW_BACKLIGHT | PMU_POW_OFF);
  93                pmu_wait_complete(&req);
  94        }
  95
  96        return 0;
  97}
  98
  99static int pmu_backlight_update_status(struct backlight_device *bd)
 100{
 101        unsigned long flags;
 102        int rc = 0;
 103
 104        spin_lock_irqsave(&pmu_backlight_lock, flags);
 105        /* Don't update brightness when sleeping */
 106        if (!sleeping)
 107                rc = __pmu_backlight_update_status(bd);
 108        spin_unlock_irqrestore(&pmu_backlight_lock, flags);
 109        return rc;
 110}
 111
 112
 113static const struct backlight_ops pmu_backlight_data = {
 114        .update_status  = pmu_backlight_update_status,
 115
 116};
 117
 118#ifdef CONFIG_PM
 119void pmu_backlight_set_sleep(int sleep)
 120{
 121        unsigned long flags;
 122
 123        spin_lock_irqsave(&pmu_backlight_lock, flags);
 124        sleeping = sleep;
 125        if (pmac_backlight && uses_pmu_bl) {
 126                if (sleep) {
 127                        struct adb_request req;
 128
 129                        pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
 130                                    PMU_POW_BACKLIGHT | PMU_POW_OFF);
 131                        pmu_wait_complete(&req);
 132                } else
 133                        __pmu_backlight_update_status(pmac_backlight);
 134        }
 135        spin_unlock_irqrestore(&pmu_backlight_lock, flags);
 136}
 137#endif /* CONFIG_PM */
 138
 139void __init pmu_backlight_init()
 140{
 141        struct backlight_properties props;
 142        struct backlight_device *bd;
 143        char name[10];
 144        int level, autosave;
 145
 146        /* Special case for the old PowerBook since I can't test on it */
 147        autosave =
 148                of_machine_is_compatible("AAPL,3400/2400") ||
 149                of_machine_is_compatible("AAPL,3500");
 150
 151        if (!autosave &&
 152            !pmac_has_backlight_type("pmu") &&
 153            !of_machine_is_compatible("AAPL,PowerBook1998") &&
 154            !of_machine_is_compatible("PowerBook1,1"))
 155                return;
 156
 157        snprintf(name, sizeof(name), "pmubl");
 158
 159        memset(&props, 0, sizeof(struct backlight_properties));
 160        props.type = BACKLIGHT_PLATFORM;
 161        props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
 162        bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data,
 163                                       &props);
 164        if (IS_ERR(bd)) {
 165                printk(KERN_ERR "PMU Backlight registration failed\n");
 166                return;
 167        }
 168        uses_pmu_bl = 1;
 169        pmu_backlight_init_curve(0x7F, 0x46, 0x0E);
 170
 171        level = bd->props.max_brightness;
 172
 173        if (autosave) {
 174                /* read autosaved value if available */
 175                struct adb_request req;
 176                pmu_request(&req, NULL, 2, 0xd9, 0);
 177                pmu_wait_complete(&req);
 178
 179                level = pmu_backlight_curve_lookup(
 180                                (req.reply[0] >> 4) *
 181                                bd->props.max_brightness / 15);
 182        }
 183
 184        bd->props.brightness = level;
 185        bd->props.power = FB_BLANK_UNBLANK;
 186        backlight_update_status(bd);
 187
 188        printk(KERN_INFO "PMU Backlight initialized (%s)\n", name);
 189}
 190