Linux Audio

Check our new training course

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