| /* | |
| * Backlight code for via-pmu | |
| * | |
| * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi. | |
| * Copyright (C) 2001-2002 Benjamin Herrenschmidt | |
| * Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> | |
| * | |
| */ | |
| #include <asm/ptrace.h> | |
| #include <linux/adb.h> | |
| #include <linux/pmu.h> | |
| #include <asm/backlight.h> | |
| #include <asm/prom.h> | |
| #define MAX_PMU_LEVEL 0xFF | |
| static const struct backlight_ops pmu_backlight_data; | |
| static DEFINE_SPINLOCK(pmu_backlight_lock); | |
| static int sleeping, uses_pmu_bl; | |
| static u8 bl_curve[FB_BACKLIGHT_LEVELS]; | |
| static void pmu_backlight_init_curve(u8 off, u8 min, u8 max) | |
| { | |
| int i, flat, count, range = (max - min); | |
| bl_curve[0] = off; | |
| for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat) | |
| bl_curve[flat] = min; | |
| count = FB_BACKLIGHT_LEVELS * 15 / 16; | |
| for (i = 0; i < count; ++i) | |
| bl_curve[flat + i] = min + (range * (i + 1) / count); | |
| } | |
| static int pmu_backlight_curve_lookup(int value) | |
| { | |
| int level = (FB_BACKLIGHT_LEVELS - 1); | |
| int i, max = 0; | |
| /* Look for biggest value */ | |
| for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) | |
| max = max((int)bl_curve[i], max); | |
| /* Look for nearest value */ | |
| for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) { | |
| int diff = abs(bl_curve[i] - value); | |
| if (diff < max) { | |
| max = diff; | |
| level = i; | |
| } | |
| } | |
| return level; | |
| } | |
| static int pmu_backlight_get_level_brightness(int level) | |
| { | |
| int pmulevel; | |
| /* Get and convert the value */ | |
| pmulevel = bl_curve[level] * FB_BACKLIGHT_MAX / MAX_PMU_LEVEL; | |
| if (pmulevel < 0) | |
| pmulevel = 0; | |
| else if (pmulevel > MAX_PMU_LEVEL) | |
| pmulevel = MAX_PMU_LEVEL; | |
| return pmulevel; | |
| } | |
| static int __pmu_backlight_update_status(struct backlight_device *bd) | |
| { | |
| struct adb_request req; | |
| int level = bd->props.brightness; | |
| if (bd->props.power != FB_BLANK_UNBLANK || | |
| bd->props.fb_blank != FB_BLANK_UNBLANK) | |
| level = 0; | |
| if (level > 0) { | |
| int pmulevel = pmu_backlight_get_level_brightness(level); | |
| pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, pmulevel); | |
| pmu_wait_complete(&req); | |
| pmu_request(&req, NULL, 2, PMU_POWER_CTRL, | |
| PMU_POW_BACKLIGHT | PMU_POW_ON); | |
| pmu_wait_complete(&req); | |
| } else { | |
| pmu_request(&req, NULL, 2, PMU_POWER_CTRL, | |
| PMU_POW_BACKLIGHT | PMU_POW_OFF); | |
| pmu_wait_complete(&req); | |
| } | |
| return 0; | |
| } | |
| static int pmu_backlight_update_status(struct backlight_device *bd) | |
| { | |
| unsigned long flags; | |
| int rc = 0; | |
| spin_lock_irqsave(&pmu_backlight_lock, flags); | |
| /* Don't update brightness when sleeping */ | |
| if (!sleeping) | |
| rc = __pmu_backlight_update_status(bd); | |
| spin_unlock_irqrestore(&pmu_backlight_lock, flags); | |
| return rc; | |
| } | |
| static const struct backlight_ops pmu_backlight_data = { | |
| .update_status = pmu_backlight_update_status, | |
| }; | |
| #ifdef CONFIG_PM | |
| void pmu_backlight_set_sleep(int sleep) | |
| { | |
| unsigned long flags; | |
| spin_lock_irqsave(&pmu_backlight_lock, flags); | |
| sleeping = sleep; | |
| if (pmac_backlight && uses_pmu_bl) { | |
| if (sleep) { | |
| struct adb_request req; | |
| pmu_request(&req, NULL, 2, PMU_POWER_CTRL, | |
| PMU_POW_BACKLIGHT | PMU_POW_OFF); | |
| pmu_wait_complete(&req); | |
| } else | |
| __pmu_backlight_update_status(pmac_backlight); | |
| } | |
| spin_unlock_irqrestore(&pmu_backlight_lock, flags); | |
| } | |
| #endif /* CONFIG_PM */ | |
| void __init pmu_backlight_init() | |
| { | |
| struct backlight_properties props; | |
| struct backlight_device *bd; | |
| char name[10]; | |
| int level, autosave; | |
| /* Special case for the old PowerBook since I can't test on it */ | |
| autosave = | |
| of_machine_is_compatible("AAPL,3400/2400") || | |
| of_machine_is_compatible("AAPL,3500"); | |
| if (!autosave && | |
| !pmac_has_backlight_type("pmu") && | |
| !of_machine_is_compatible("AAPL,PowerBook1998") && | |
| !of_machine_is_compatible("PowerBook1,1")) | |
| return; | |
| snprintf(name, sizeof(name), "pmubl"); | |
| memset(&props, 0, sizeof(struct backlight_properties)); | |
| props.type = BACKLIGHT_PLATFORM; | |
| props.max_brightness = FB_BACKLIGHT_LEVELS - 1; | |
| bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data, | |
| &props); | |
| if (IS_ERR(bd)) { | |
| printk(KERN_ERR "PMU Backlight registration failed\n"); | |
| return; | |
| } | |
| uses_pmu_bl = 1; | |
| pmu_backlight_init_curve(0x7F, 0x46, 0x0E); | |
| level = bd->props.max_brightness; | |
| if (autosave) { | |
| /* read autosaved value if available */ | |
| struct adb_request req; | |
| pmu_request(&req, NULL, 2, 0xd9, 0); | |
| pmu_wait_complete(&req); | |
| level = pmu_backlight_curve_lookup( | |
| (req.reply[0] >> 4) * | |
| bd->props.max_brightness / 15); | |
| } | |
| bd->props.brightness = level; | |
| bd->props.power = FB_BLANK_UNBLANK; | |
| backlight_update_status(bd); | |
| printk(KERN_INFO "PMU Backlight initialized (%s)\n", name); | |
| } |