Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Miscellaneous procedures for dealing with the PowerMac hardware.
0004  * Contains support for the backlight.
0005  *
0006  *   Copyright (C) 2000 Benjamin Herrenschmidt
0007  *   Copyright (C) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
0008  *
0009  */
0010 
0011 #include <linux/kernel.h>
0012 #include <linux/fb.h>
0013 #include <linux/backlight.h>
0014 #include <linux/adb.h>
0015 #include <linux/pmu.h>
0016 #include <linux/atomic.h>
0017 #include <linux/export.h>
0018 #include <asm/backlight.h>
0019 
0020 #define OLD_BACKLIGHT_MAX 15
0021 
0022 static void pmac_backlight_key_worker(struct work_struct *work);
0023 static void pmac_backlight_set_legacy_worker(struct work_struct *work);
0024 
0025 static DECLARE_WORK(pmac_backlight_key_work, pmac_backlight_key_worker);
0026 static DECLARE_WORK(pmac_backlight_set_legacy_work, pmac_backlight_set_legacy_worker);
0027 
0028 /* Although these variables are used in interrupt context, it makes no sense to
0029  * protect them. No user is able to produce enough key events per second and
0030  * notice the errors that might happen.
0031  */
0032 static int pmac_backlight_key_queued;
0033 static int pmac_backlight_set_legacy_queued;
0034 
0035 /* The via-pmu code allows the backlight to be grabbed, in which case the
0036  * in-kernel control of the brightness needs to be disabled. This should
0037  * only be used by really old PowerBooks.
0038  */
0039 static atomic_t kernel_backlight_disabled = ATOMIC_INIT(0);
0040 
0041 /* Protect the pmac_backlight variable below.
0042    You should hold this lock when using the pmac_backlight pointer to
0043    prevent its potential removal. */
0044 DEFINE_MUTEX(pmac_backlight_mutex);
0045 
0046 /* Main backlight storage
0047  *
0048  * Backlight drivers in this variable are required to have the "ops"
0049  * attribute set and to have an update_status function.
0050  *
0051  * We can only store one backlight here, but since Apple laptops have only one
0052  * internal display, it doesn't matter. Other backlight drivers can be used
0053  * independently.
0054  *
0055  */
0056 struct backlight_device *pmac_backlight;
0057 
0058 int pmac_has_backlight_type(const char *type)
0059 {
0060     struct device_node* bk_node = of_find_node_by_name(NULL, "backlight");
0061 
0062     if (bk_node) {
0063         const char *prop = of_get_property(bk_node,
0064                 "backlight-control", NULL);
0065         if (prop && strncmp(prop, type, strlen(type)) == 0) {
0066             of_node_put(bk_node);
0067             return 1;
0068         }
0069         of_node_put(bk_node);
0070     }
0071 
0072     return 0;
0073 }
0074 
0075 int pmac_backlight_curve_lookup(struct fb_info *info, int value)
0076 {
0077     int level = (FB_BACKLIGHT_LEVELS - 1);
0078 
0079     if (info && info->bl_dev) {
0080         int i, max = 0;
0081 
0082         /* Look for biggest value */
0083         for (i = 0; i < FB_BACKLIGHT_LEVELS; i++)
0084             max = max((int)info->bl_curve[i], max);
0085 
0086         /* Look for nearest value */
0087         for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) {
0088             int diff = abs(info->bl_curve[i] - value);
0089             if (diff < max) {
0090                 max = diff;
0091                 level = i;
0092             }
0093         }
0094 
0095     }
0096 
0097     return level;
0098 }
0099 
0100 static void pmac_backlight_key_worker(struct work_struct *work)
0101 {
0102     if (atomic_read(&kernel_backlight_disabled))
0103         return;
0104 
0105     mutex_lock(&pmac_backlight_mutex);
0106     if (pmac_backlight) {
0107         struct backlight_properties *props;
0108         int brightness;
0109 
0110         props = &pmac_backlight->props;
0111 
0112         brightness = props->brightness +
0113             ((pmac_backlight_key_queued?-1:1) *
0114              (props->max_brightness / 15));
0115 
0116         if (brightness < 0)
0117             brightness = 0;
0118         else if (brightness > props->max_brightness)
0119             brightness = props->max_brightness;
0120 
0121         props->brightness = brightness;
0122         backlight_update_status(pmac_backlight);
0123     }
0124     mutex_unlock(&pmac_backlight_mutex);
0125 }
0126 
0127 /* This function is called in interrupt context */
0128 void pmac_backlight_key(int direction)
0129 {
0130     if (atomic_read(&kernel_backlight_disabled))
0131         return;
0132 
0133     /* we can receive multiple interrupts here, but the scheduled work
0134      * will run only once, with the last value
0135      */
0136     pmac_backlight_key_queued = direction;
0137     schedule_work(&pmac_backlight_key_work);
0138 }
0139 
0140 static int __pmac_backlight_set_legacy_brightness(int brightness)
0141 {
0142     int error = -ENXIO;
0143 
0144     mutex_lock(&pmac_backlight_mutex);
0145     if (pmac_backlight) {
0146         struct backlight_properties *props;
0147 
0148         props = &pmac_backlight->props;
0149         props->brightness = brightness *
0150             (props->max_brightness + 1) /
0151             (OLD_BACKLIGHT_MAX + 1);
0152 
0153         if (props->brightness > props->max_brightness)
0154             props->brightness = props->max_brightness;
0155         else if (props->brightness < 0)
0156             props->brightness = 0;
0157 
0158         backlight_update_status(pmac_backlight);
0159 
0160         error = 0;
0161     }
0162     mutex_unlock(&pmac_backlight_mutex);
0163 
0164     return error;
0165 }
0166 
0167 static void pmac_backlight_set_legacy_worker(struct work_struct *work)
0168 {
0169     if (atomic_read(&kernel_backlight_disabled))
0170         return;
0171 
0172     __pmac_backlight_set_legacy_brightness(pmac_backlight_set_legacy_queued);
0173 }
0174 
0175 /* This function is called in interrupt context */
0176 void pmac_backlight_set_legacy_brightness_pmu(int brightness) {
0177     if (atomic_read(&kernel_backlight_disabled))
0178         return;
0179 
0180     pmac_backlight_set_legacy_queued = brightness;
0181     schedule_work(&pmac_backlight_set_legacy_work);
0182 }
0183 
0184 int pmac_backlight_set_legacy_brightness(int brightness)
0185 {
0186     return __pmac_backlight_set_legacy_brightness(brightness);
0187 }
0188 
0189 int pmac_backlight_get_legacy_brightness(void)
0190 {
0191     int result = -ENXIO;
0192 
0193     mutex_lock(&pmac_backlight_mutex);
0194     if (pmac_backlight) {
0195         struct backlight_properties *props;
0196 
0197         props = &pmac_backlight->props;
0198 
0199         result = props->brightness *
0200             (OLD_BACKLIGHT_MAX + 1) /
0201             (props->max_brightness + 1);
0202     }
0203     mutex_unlock(&pmac_backlight_mutex);
0204 
0205     return result;
0206 }
0207 
0208 void pmac_backlight_disable(void)
0209 {
0210     atomic_inc(&kernel_backlight_disabled);
0211 }
0212 
0213 void pmac_backlight_enable(void)
0214 {
0215     atomic_dec(&kernel_backlight_disabled);
0216 }
0217 
0218 EXPORT_SYMBOL_GPL(pmac_backlight);
0219 EXPORT_SYMBOL_GPL(pmac_backlight_mutex);
0220 EXPORT_SYMBOL_GPL(pmac_has_backlight_type);