162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Backlight code for via-pmu 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi. 662306a36Sopenharmony_ci * Copyright (C) 2001-2002 Benjamin Herrenschmidt 762306a36Sopenharmony_ci * Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/ptrace.h> 1262306a36Sopenharmony_ci#include <linux/adb.h> 1362306a36Sopenharmony_ci#include <linux/pmu.h> 1462306a36Sopenharmony_ci#include <asm/backlight.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define MAX_PMU_LEVEL 0xFF 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic const struct backlight_ops pmu_backlight_data; 1962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(pmu_backlight_lock); 2062306a36Sopenharmony_cistatic int sleeping, uses_pmu_bl; 2162306a36Sopenharmony_cistatic u8 bl_curve[FB_BACKLIGHT_LEVELS]; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void pmu_backlight_init_curve(u8 off, u8 min, u8 max) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci int i, flat, count, range = (max - min); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci bl_curve[0] = off; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat) 3062306a36Sopenharmony_ci bl_curve[flat] = min; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci count = FB_BACKLIGHT_LEVELS * 15 / 16; 3362306a36Sopenharmony_ci for (i = 0; i < count; ++i) 3462306a36Sopenharmony_ci bl_curve[flat + i] = min + (range * (i + 1) / count); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int pmu_backlight_curve_lookup(int value) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci int level = (FB_BACKLIGHT_LEVELS - 1); 4062306a36Sopenharmony_ci int i, max = 0; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* Look for biggest value */ 4362306a36Sopenharmony_ci for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) 4462306a36Sopenharmony_ci max = max((int)bl_curve[i], max); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Look for nearest value */ 4762306a36Sopenharmony_ci for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) { 4862306a36Sopenharmony_ci int diff = abs(bl_curve[i] - value); 4962306a36Sopenharmony_ci if (diff < max) { 5062306a36Sopenharmony_ci max = diff; 5162306a36Sopenharmony_ci level = i; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci return level; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int pmu_backlight_get_level_brightness(int level) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci int pmulevel; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Get and convert the value */ 6262306a36Sopenharmony_ci pmulevel = bl_curve[level] * FB_BACKLIGHT_MAX / MAX_PMU_LEVEL; 6362306a36Sopenharmony_ci if (pmulevel < 0) 6462306a36Sopenharmony_ci pmulevel = 0; 6562306a36Sopenharmony_ci else if (pmulevel > MAX_PMU_LEVEL) 6662306a36Sopenharmony_ci pmulevel = MAX_PMU_LEVEL; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return pmulevel; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int __pmu_backlight_update_status(struct backlight_device *bd) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct adb_request req; 7462306a36Sopenharmony_ci int level = backlight_get_brightness(bd); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (level > 0) { 7762306a36Sopenharmony_ci int pmulevel = pmu_backlight_get_level_brightness(level); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, pmulevel); 8062306a36Sopenharmony_ci pmu_wait_complete(&req); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL, 8362306a36Sopenharmony_ci PMU_POW_BACKLIGHT | PMU_POW_ON); 8462306a36Sopenharmony_ci pmu_wait_complete(&req); 8562306a36Sopenharmony_ci } else { 8662306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL, 8762306a36Sopenharmony_ci PMU_POW_BACKLIGHT | PMU_POW_OFF); 8862306a36Sopenharmony_ci pmu_wait_complete(&req); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int pmu_backlight_update_status(struct backlight_device *bd) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci unsigned long flags; 9762306a36Sopenharmony_ci int rc = 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci spin_lock_irqsave(&pmu_backlight_lock, flags); 10062306a36Sopenharmony_ci /* Don't update brightness when sleeping */ 10162306a36Sopenharmony_ci if (!sleeping) 10262306a36Sopenharmony_ci rc = __pmu_backlight_update_status(bd); 10362306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_backlight_lock, flags); 10462306a36Sopenharmony_ci return rc; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic const struct backlight_ops pmu_backlight_data = { 10962306a36Sopenharmony_ci .update_status = pmu_backlight_update_status, 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#ifdef CONFIG_PM 11462306a36Sopenharmony_civoid pmu_backlight_set_sleep(int sleep) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci unsigned long flags; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci spin_lock_irqsave(&pmu_backlight_lock, flags); 11962306a36Sopenharmony_ci sleeping = sleep; 12062306a36Sopenharmony_ci if (pmac_backlight && uses_pmu_bl) { 12162306a36Sopenharmony_ci if (sleep) { 12262306a36Sopenharmony_ci struct adb_request req; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci pmu_request(&req, NULL, 2, PMU_POWER_CTRL, 12562306a36Sopenharmony_ci PMU_POW_BACKLIGHT | PMU_POW_OFF); 12662306a36Sopenharmony_ci pmu_wait_complete(&req); 12762306a36Sopenharmony_ci } else 12862306a36Sopenharmony_ci __pmu_backlight_update_status(pmac_backlight); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_backlight_lock, flags); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci#endif /* CONFIG_PM */ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_civoid __init pmu_backlight_init(void) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct backlight_properties props; 13762306a36Sopenharmony_ci struct backlight_device *bd; 13862306a36Sopenharmony_ci char name[10]; 13962306a36Sopenharmony_ci int level, autosave; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Special case for the old PowerBook since I can't test on it */ 14262306a36Sopenharmony_ci autosave = 14362306a36Sopenharmony_ci of_machine_is_compatible("AAPL,3400/2400") || 14462306a36Sopenharmony_ci of_machine_is_compatible("AAPL,3500"); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!autosave && 14762306a36Sopenharmony_ci !pmac_has_backlight_type("pmu") && 14862306a36Sopenharmony_ci !of_machine_is_compatible("AAPL,PowerBook1998") && 14962306a36Sopenharmony_ci !of_machine_is_compatible("PowerBook1,1")) 15062306a36Sopenharmony_ci return; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci snprintf(name, sizeof(name), "pmubl"); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 15562306a36Sopenharmony_ci props.type = BACKLIGHT_PLATFORM; 15662306a36Sopenharmony_ci props.max_brightness = FB_BACKLIGHT_LEVELS - 1; 15762306a36Sopenharmony_ci bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data, 15862306a36Sopenharmony_ci &props); 15962306a36Sopenharmony_ci if (IS_ERR(bd)) { 16062306a36Sopenharmony_ci printk(KERN_ERR "PMU Backlight registration failed\n"); 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci uses_pmu_bl = 1; 16462306a36Sopenharmony_ci pmu_backlight_init_curve(0x7F, 0x46, 0x0E); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci level = bd->props.max_brightness; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (autosave) { 16962306a36Sopenharmony_ci /* read autosaved value if available */ 17062306a36Sopenharmony_ci struct adb_request req; 17162306a36Sopenharmony_ci pmu_request(&req, NULL, 2, 0xd9, 0); 17262306a36Sopenharmony_ci pmu_wait_complete(&req); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci level = pmu_backlight_curve_lookup( 17562306a36Sopenharmony_ci (req.reply[0] >> 4) * 17662306a36Sopenharmony_ci bd->props.max_brightness / 15); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci bd->props.brightness = level; 18062306a36Sopenharmony_ci bd->props.power = FB_BLANK_UNBLANK; 18162306a36Sopenharmony_ci backlight_update_status(bd); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci printk(KERN_INFO "PMU Backlight initialized (%s)\n", name); 18462306a36Sopenharmony_ci} 185