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 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 int pmu_backlight_get_brightness(struct backlight_device *bd)
 114{
 115        return bd->props.brightness;
 116}
 117
 118static struct backlight_ops pmu_backlight_data = {
 119        .get_brightness = pmu_backlight_get_brightness,
 120        .update_status  = pmu_backlight_update_status,
 121
 122};
 123
 124#ifdef CONFIG_PM
 125void pmu_backlight_set_sleep(int sleep)
 126{
 127        unsigned long flags;
 128
 129        spin_lock_irqsave(&pmu_backlight_lock, flags);
 130        sleeping = sleep;
 131        if (pmac_backlight && uses_pmu_bl) {
 132                if (sleep) {
 133                        struct adb_request req;
 134
 135                        pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
 136                                    PMU_POW_BACKLIGHT | PMU_POW_OFF);
 137                        pmu_wait_complete(&req);
 138                } else
 139                        __pmu_backlight_update_status(pmac_backlight);
 140        }
 141        spin_unlock_irqrestore(&pmu_backlight_lock, flags);
 142}
 143#endif /* CONFIG_PM */
 144
 145void __init pmu_backlight_init()
 146{
 147        struct backlight_device *bd;
 148        char name[10];
 149        int level, autosave;
 150
 151        /* Special case for the old PowerBook since I can't test on it */
 152        autosave =
 153                machine_is_compatible("AAPL,3400/2400") ||
 154                machine_is_compatible("AAPL,3500");
 155
 156        if (!autosave &&
 157            !pmac_has_backlight_type("pmu") &&
 158            !machine_is_compatible("AAPL,PowerBook1998") &&
 159            !machine_is_compatible("PowerBook1,1"))
 160                return;
 161
 162        snprintf(name, sizeof(name), "pmubl");
 163
 164        bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data);
 165        if (IS_ERR(bd)) {
 166                printk(KERN_ERR "PMU Backlight registration failed\n");
 167                return;
 168        }
 169        uses_pmu_bl = 1;
 170        bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
 171        pmu_backlight_init_curve(0x7F, 0x46, 0x0E);
 172
 173        level = bd->props.max_brightness;
 174
 175        if (autosave) {
 176                /* read autosaved value if available */
 177                struct adb_request req;
 178                pmu_request(&req, NULL, 2, 0xd9, 0);
 179                pmu_wait_complete(&req);
 180
 181                level = pmu_backlight_curve_lookup(
 182                                (req.reply[0] >> 4) *
 183                                bd->props.max_brightness / 15);
 184        }
 185
 186        bd->props.brightness = level;
 187        bd->props.power = FB_BLANK_UNBLANK;
 188        backlight_update_status(bd);
 189
 190        printk(KERN_INFO "PMU Backlight initialized (%s)\n", name);
 191}
 192