Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Backlight code for via-pmu
0004  *
0005  * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
0006  * Copyright (C) 2001-2002 Benjamin Herrenschmidt
0007  * Copyright (C) 2006      Michael Hanselmann <linux-kernel@hansmi.ch>
0008  *
0009  */
0010 
0011 #include <asm/ptrace.h>
0012 #include <linux/adb.h>
0013 #include <linux/pmu.h>
0014 #include <asm/backlight.h>
0015 
0016 #define MAX_PMU_LEVEL 0xFF
0017 
0018 static const struct backlight_ops pmu_backlight_data;
0019 static DEFINE_SPINLOCK(pmu_backlight_lock);
0020 static int sleeping, uses_pmu_bl;
0021 static u8 bl_curve[FB_BACKLIGHT_LEVELS];
0022 
0023 static void pmu_backlight_init_curve(u8 off, u8 min, u8 max)
0024 {
0025     int i, flat, count, range = (max - min);
0026 
0027     bl_curve[0] = off;
0028 
0029     for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
0030         bl_curve[flat] = min;
0031 
0032     count = FB_BACKLIGHT_LEVELS * 15 / 16;
0033     for (i = 0; i < count; ++i)
0034         bl_curve[flat + i] = min + (range * (i + 1) / count);
0035 }
0036 
0037 static int pmu_backlight_curve_lookup(int value)
0038 {
0039     int level = (FB_BACKLIGHT_LEVELS - 1);
0040     int i, max = 0;
0041 
0042     /* Look for biggest value */
0043     for (i = 0; i < FB_BACKLIGHT_LEVELS; i++)
0044         max = max((int)bl_curve[i], max);
0045 
0046     /* Look for nearest value */
0047     for (i = 0; i < FB_BACKLIGHT_LEVELS; i++) {
0048         int diff = abs(bl_curve[i] - value);
0049         if (diff < max) {
0050             max = diff;
0051             level = i;
0052         }
0053     }
0054     return level;
0055 }
0056 
0057 static int pmu_backlight_get_level_brightness(int level)
0058 {
0059     int pmulevel;
0060 
0061     /* Get and convert the value */
0062     pmulevel = bl_curve[level] * FB_BACKLIGHT_MAX / MAX_PMU_LEVEL;
0063     if (pmulevel < 0)
0064         pmulevel = 0;
0065     else if (pmulevel > MAX_PMU_LEVEL)
0066         pmulevel = MAX_PMU_LEVEL;
0067 
0068     return pmulevel;
0069 }
0070 
0071 static int __pmu_backlight_update_status(struct backlight_device *bd)
0072 {
0073     struct adb_request req;
0074     int level = bd->props.brightness;
0075 
0076 
0077     if (bd->props.power != FB_BLANK_UNBLANK ||
0078         bd->props.fb_blank != FB_BLANK_UNBLANK)
0079         level = 0;
0080 
0081     if (level > 0) {
0082         int pmulevel = pmu_backlight_get_level_brightness(level);
0083 
0084         pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, pmulevel);
0085         pmu_wait_complete(&req);
0086 
0087         pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
0088             PMU_POW_BACKLIGHT | PMU_POW_ON);
0089         pmu_wait_complete(&req);
0090     } else {
0091         pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
0092             PMU_POW_BACKLIGHT | PMU_POW_OFF);
0093         pmu_wait_complete(&req);
0094     }
0095 
0096     return 0;
0097 }
0098 
0099 static int pmu_backlight_update_status(struct backlight_device *bd)
0100 {
0101     unsigned long flags;
0102     int rc = 0;
0103 
0104     spin_lock_irqsave(&pmu_backlight_lock, flags);
0105     /* Don't update brightness when sleeping */
0106     if (!sleeping)
0107         rc = __pmu_backlight_update_status(bd);
0108     spin_unlock_irqrestore(&pmu_backlight_lock, flags);
0109     return rc;
0110 }
0111 
0112 
0113 static const struct backlight_ops pmu_backlight_data = {
0114     .update_status  = pmu_backlight_update_status,
0115 
0116 };
0117 
0118 #ifdef CONFIG_PM
0119 void pmu_backlight_set_sleep(int sleep)
0120 {
0121     unsigned long flags;
0122 
0123     spin_lock_irqsave(&pmu_backlight_lock, flags);
0124     sleeping = sleep;
0125     if (pmac_backlight && uses_pmu_bl) {
0126         if (sleep) {
0127             struct adb_request req;
0128 
0129             pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
0130                     PMU_POW_BACKLIGHT | PMU_POW_OFF);
0131             pmu_wait_complete(&req);
0132         } else
0133             __pmu_backlight_update_status(pmac_backlight);
0134     }
0135     spin_unlock_irqrestore(&pmu_backlight_lock, flags);
0136 }
0137 #endif /* CONFIG_PM */
0138 
0139 void __init pmu_backlight_init(void)
0140 {
0141     struct backlight_properties props;
0142     struct backlight_device *bd;
0143     char name[10];
0144     int level, autosave;
0145 
0146     /* Special case for the old PowerBook since I can't test on it */
0147     autosave =
0148         of_machine_is_compatible("AAPL,3400/2400") ||
0149         of_machine_is_compatible("AAPL,3500");
0150 
0151     if (!autosave &&
0152         !pmac_has_backlight_type("pmu") &&
0153         !of_machine_is_compatible("AAPL,PowerBook1998") &&
0154         !of_machine_is_compatible("PowerBook1,1"))
0155         return;
0156 
0157     snprintf(name, sizeof(name), "pmubl");
0158 
0159     memset(&props, 0, sizeof(struct backlight_properties));
0160     props.type = BACKLIGHT_PLATFORM;
0161     props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
0162     bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data,
0163                        &props);
0164     if (IS_ERR(bd)) {
0165         printk(KERN_ERR "PMU Backlight registration failed\n");
0166         return;
0167     }
0168     uses_pmu_bl = 1;
0169     pmu_backlight_init_curve(0x7F, 0x46, 0x0E);
0170 
0171     level = bd->props.max_brightness;
0172 
0173     if (autosave) {
0174         /* read autosaved value if available */
0175         struct adb_request req;
0176         pmu_request(&req, NULL, 2, 0xd9, 0);
0177         pmu_wait_complete(&req);
0178 
0179         level = pmu_backlight_curve_lookup(
0180                 (req.reply[0] >> 4) *
0181                 bd->props.max_brightness / 15);
0182     }
0183 
0184     bd->props.brightness = level;
0185     bd->props.power = FB_BLANK_UNBLANK;
0186     backlight_update_status(bd);
0187 
0188     printk(KERN_INFO "PMU Backlight initialized (%s)\n", name);
0189 }