backlight.c (5569B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Miscellaneous procedures for dealing with the PowerMac hardware. 4 * Contains support for the backlight. 5 * 6 * Copyright (C) 2000 Benjamin Herrenschmidt 7 * Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch> 8 * 9 */ 10 11#include <linux/kernel.h> 12#include <linux/fb.h> 13#include <linux/backlight.h> 14#include <linux/adb.h> 15#include <linux/pmu.h> 16#include <linux/atomic.h> 17#include <linux/export.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(void) 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(void) 209{ 210 atomic_inc(&kernel_backlight_disabled); 211} 212 213void pmac_backlight_enable(void) 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);