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