xref: /OK3568_Linux_fs/kernel/arch/powerpc/platforms/powermac/backlight.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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