linux/arch/powerpc/platforms/powermac/backlight.c
<<
>>
Prefs
   1/*
   2 * Miscellaneous procedures for dealing with the PowerMac hardware.
   3 * Contains support for the backlight.
   4 *
   5 *   Copyright (C) 2000 Benjamin Herrenschmidt
   6 *   Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
   7 *
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/fb.h>
  12#include <linux/backlight.h>
  13#include <linux/adb.h>
  14#include <linux/pmu.h>
  15#include <linux/atomic.h>
  16#include <linux/export.h>
  17#include <asm/prom.h>
  18#include <asm/backlight.h>
  19
  20#define OLD_BACKLIGHT_MAX 15
  21
  22static void pmac_backlight_key_worker(struct work_struct *work);
  23static void pmac_backlight_set_legacy_worker(struct work_struct *work);
  24
  25static DECLARE_WORK(pmac_backlight_key_work, pmac_backlight_key_worker);
  26static DECLARE_WORK(pmac_backlight_set_legacy_work, pmac_backlight_set_legacy_worker);
  27
  28/* Although these variables are used in interrupt context, it makes no sense to
  29 * protect them. No user is able to produce enough key events per second and
  30 * notice the errors that might happen.
  31 */
  32static int pmac_backlight_key_queued;
  33static int pmac_backlight_set_legacy_queued;
  34
  35/* The via-pmu code allows the backlight to be grabbed, in which case the
  36 * in-kernel control of the brightness needs to be disabled. This should
  37 * only be used by really old PowerBooks.
  38 */
  39static atomic_t kernel_backlight_disabled = ATOMIC_INIT(0);
  40
  41/* Protect the pmac_backlight variable below.
  42   You should hold this lock when using the pmac_backlight pointer to
  43   prevent its potential removal. */
  44DEFINE_MUTEX(pmac_backlight_mutex);
  45
  46/* Main backlight storage
  47 *
  48 * Backlight drivers in this variable are required to have the "ops"
  49 * attribute set and to have an update_status function.
  50 *
  51 * We can only store one backlight here, but since Apple laptops have only one
  52 * internal display, it doesn't matter. Other backlight drivers can be used
  53 * independently.
  54 *
  55 */
  56struct backlight_device *pmac_backlight;
  57
  58int pmac_has_backlight_type(const char *type)
  59{
  60        struct device_node* bk_node = of_find_node_by_name(NULL, "backlight");
  61
  62        if (bk_node) {
  63                const char *prop = of_get_property(bk_node,
  64                                "backlight-control", NULL);
  65                if (prop && strncmp(prop, type, strlen(type)) == 0) {
  66                        of_node_put(bk_node);
  67                        return 1;
  68                }
  69                of_node_put(bk_node);
  70        }
  71
  72        return 0;
  73}
  74
  75int pmac_backlight_curve_lookup(struct fb_info *info, int value)
  76{
  77        int level = (FB_BACKLIGHT_LEVELS - 1);
  78
  79        if (info && info->bl_dev) {
  80                int i, max = 0;
  81
  82                /* Look for biggest value */
  83                for (i = 0; i < FB_BACKLIGHT_LEVELS; i++)
  84                        max = max((int)info->bl_curve[i], max);
  85
  86                /* Look for nearest value */
  87                for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) {
  88                        int diff = abs(info->bl_curve[i] - value);
  89                        if (diff < max) {
  90                                max = diff;
  91                                level = i;
  92                        }
  93                }
  94
  95        }
  96
  97        return level;
  98}
  99
 100static void pmac_backlight_key_worker(struct work_struct *work)
 101{
 102        if (atomic_read(&kernel_backlight_disabled))
 103                return;
 104
 105        mutex_lock(&pmac_backlight_mutex);
 106        if (pmac_backlight) {
 107                struct backlight_properties *props;
 108                int brightness;
 109
 110                props = &pmac_backlight->props;
 111
 112                brightness = props->brightness +
 113                        ((pmac_backlight_key_queued?-1:1) *
 114                         (props->max_brightness / 15));
 115
 116                if (brightness < 0)
 117                        brightness = 0;
 118                else if (brightness > props->max_brightness)
 119                        brightness = props->max_brightness;
 120
 121                props->brightness = brightness;
 122                backlight_update_status(pmac_backlight);
 123        }
 124        mutex_unlock(&pmac_backlight_mutex);
 125}
 126
 127/* This function is called in interrupt context */
 128void pmac_backlight_key(int direction)
 129{
 130        if (atomic_read(&kernel_backlight_disabled))
 131                return;
 132
 133        /* we can receive multiple interrupts here, but the scheduled work
 134         * will run only once, with the last value
 135         */
 136        pmac_backlight_key_queued = direction;
 137        schedule_work(&pmac_backlight_key_work);
 138}
 139
 140static int __pmac_backlight_set_legacy_brightness(int brightness)
 141{
 142        int error = -ENXIO;
 143
 144        mutex_lock(&pmac_backlight_mutex);
 145        if (pmac_backlight) {
 146                struct backlight_properties *props;
 147
 148                props = &pmac_backlight->props;
 149                props->brightness = brightness *
 150                        (props->max_brightness + 1) /
 151                        (OLD_BACKLIGHT_MAX + 1);
 152
 153                if (props->brightness > props->max_brightness)
 154                        props->brightness = props->max_brightness;
 155                else if (props->brightness < 0)
 156                        props->brightness = 0;
 157
 158                backlight_update_status(pmac_backlight);
 159
 160                error = 0;
 161        }
 162        mutex_unlock(&pmac_backlight_mutex);
 163
 164        return error;
 165}
 166
 167static void pmac_backlight_set_legacy_worker(struct work_struct *work)
 168{
 169        if (atomic_read(&kernel_backlight_disabled))
 170                return;
 171
 172        __pmac_backlight_set_legacy_brightness(pmac_backlight_set_legacy_queued);
 173}
 174
 175/* This function is called in interrupt context */
 176void pmac_backlight_set_legacy_brightness_pmu(int brightness) {
 177        if (atomic_read(&kernel_backlight_disabled))
 178                return;
 179
 180        pmac_backlight_set_legacy_queued = brightness;
 181        schedule_work(&pmac_backlight_set_legacy_work);
 182}
 183
 184int pmac_backlight_set_legacy_brightness(int brightness)
 185{
 186        return __pmac_backlight_set_legacy_brightness(brightness);
 187}
 188
 189int pmac_backlight_get_legacy_brightness()
 190{
 191        int result = -ENXIO;
 192
 193        mutex_lock(&pmac_backlight_mutex);
 194        if (pmac_backlight) {
 195                struct backlight_properties *props;
 196
 197                props = &pmac_backlight->props;
 198
 199                result = props->brightness *
 200                        (OLD_BACKLIGHT_MAX + 1) /
 201                        (props->max_brightness + 1);
 202        }
 203        mutex_unlock(&pmac_backlight_mutex);
 204
 205        return result;
 206}
 207
 208void pmac_backlight_disable()
 209{
 210        atomic_inc(&kernel_backlight_disabled);
 211}
 212
 213void pmac_backlight_enable()
 214{
 215        atomic_dec(&kernel_backlight_disabled);
 216}
 217
 218EXPORT_SYMBOL_GPL(pmac_backlight);
 219EXPORT_SYMBOL_GPL(pmac_backlight_mutex);
 220EXPORT_SYMBOL_GPL(pmac_has_backlight_type);
 221