186 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Backlight code for via-pmu4 *5 * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.6 * Copyright (C) 2001-2002 Benjamin Herrenschmidt7 * 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/backlight.h>14#include <linux/pmu.h>15#include <asm/backlight.h>16 17#define MAX_PMU_LEVEL 0xFF18 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 = backlight_get_brightness(bd);76 77 if (level > 0) {78 int pmulevel = pmu_backlight_get_level_brightness(level);79 80 pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, pmulevel);81 pmu_wait_complete(&req);82 83 pmu_request(&req, NULL, 2, PMU_POWER_CTRL,84 PMU_POW_BACKLIGHT | PMU_POW_ON);85 pmu_wait_complete(&req);86 } else {87 pmu_request(&req, NULL, 2, PMU_POWER_CTRL,88 PMU_POW_BACKLIGHT | PMU_POW_OFF);89 pmu_wait_complete(&req);90 }91 92 return 0;93}94 95static int pmu_backlight_update_status(struct backlight_device *bd)96{97 unsigned long flags;98 int rc = 0;99 100 spin_lock_irqsave(&pmu_backlight_lock, flags);101 /* Don't update brightness when sleeping */102 if (!sleeping)103 rc = __pmu_backlight_update_status(bd);104 spin_unlock_irqrestore(&pmu_backlight_lock, flags);105 return rc;106}107 108 109static const struct backlight_ops pmu_backlight_data = {110 .update_status = pmu_backlight_update_status,111 112};113 114#ifdef CONFIG_PM115void pmu_backlight_set_sleep(int sleep)116{117 unsigned long flags;118 119 spin_lock_irqsave(&pmu_backlight_lock, flags);120 sleeping = sleep;121 if (pmac_backlight && uses_pmu_bl) {122 if (sleep) {123 struct adb_request req;124 125 pmu_request(&req, NULL, 2, PMU_POWER_CTRL,126 PMU_POW_BACKLIGHT | PMU_POW_OFF);127 pmu_wait_complete(&req);128 } else129 __pmu_backlight_update_status(pmac_backlight);130 }131 spin_unlock_irqrestore(&pmu_backlight_lock, flags);132}133#endif /* CONFIG_PM */134 135void __init pmu_backlight_init(void)136{137 struct backlight_properties props;138 struct backlight_device *bd;139 char name[10];140 int level, autosave;141 142 /* Special case for the old PowerBook since I can't test on it */143 autosave =144 of_machine_is_compatible("AAPL,3400/2400") ||145 of_machine_is_compatible("AAPL,3500");146 147 if (!autosave &&148 !pmac_has_backlight_type("pmu") &&149 !of_machine_is_compatible("AAPL,PowerBook1998") &&150 !of_machine_is_compatible("PowerBook1,1"))151 return;152 153 snprintf(name, sizeof(name), "pmubl");154 155 memset(&props, 0, sizeof(struct backlight_properties));156 props.type = BACKLIGHT_PLATFORM;157 props.max_brightness = FB_BACKLIGHT_LEVELS - 1;158 bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data,159 &props);160 if (IS_ERR(bd)) {161 printk(KERN_ERR "PMU Backlight registration failed\n");162 return;163 }164 uses_pmu_bl = 1;165 pmu_backlight_init_curve(0x7F, 0x46, 0x0E);166 167 level = bd->props.max_brightness;168 169 if (autosave) {170 /* read autosaved value if available */171 struct adb_request req;172 pmu_request(&req, NULL, 2, 0xd9, 0);173 pmu_wait_complete(&req);174 175 level = pmu_backlight_curve_lookup(176 (req.reply[0] >> 4) *177 bd->props.max_brightness / 15);178 }179 180 bd->props.brightness = level;181 bd->props.power = BACKLIGHT_POWER_ON;182 backlight_update_status(bd);183 184 printk(KERN_INFO "PMU Backlight initialized (%s)\n", name);185}186