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