Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * TI LP8788 MFD - keyled driver
0004  *
0005  * Copyright 2012 Texas Instruments
0006  *
0007  * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/slab.h>
0012 #include <linux/err.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/leds.h>
0015 #include <linux/mutex.h>
0016 #include <linux/mfd/lp8788.h>
0017 #include <linux/mfd/lp8788-isink.h>
0018 
0019 #define MAX_BRIGHTNESS          LP8788_ISINK_MAX_PWM
0020 #define DEFAULT_LED_NAME        "keyboard-backlight"
0021 
0022 struct lp8788_led {
0023     struct lp8788 *lp;
0024     struct mutex lock;
0025     struct led_classdev led_dev;
0026     enum lp8788_isink_number isink_num;
0027     int on;
0028 };
0029 
0030 struct lp8788_led_config {
0031     enum lp8788_isink_scale scale;
0032     enum lp8788_isink_number num;
0033     int iout;
0034 };
0035 
0036 static struct lp8788_led_config default_led_config = {
0037     .scale = LP8788_ISINK_SCALE_100mA,
0038     .num   = LP8788_ISINK_3,
0039     .iout  = 0,
0040 };
0041 
0042 static int lp8788_led_init_device(struct lp8788_led *led,
0043                 struct lp8788_led_platform_data *pdata)
0044 {
0045     struct lp8788_led_config *cfg = &default_led_config;
0046     u8 addr, mask, val;
0047     int ret;
0048 
0049     if (pdata) {
0050         cfg->scale = pdata->scale;
0051         cfg->num = pdata->num;
0052         cfg->iout = pdata->iout_code;
0053     }
0054 
0055     led->isink_num = cfg->num;
0056 
0057     /* scale configuration */
0058     addr = LP8788_ISINK_CTRL;
0059     mask = 1 << (cfg->num + LP8788_ISINK_SCALE_OFFSET);
0060     val = cfg->scale << (cfg->num + LP8788_ISINK_SCALE_OFFSET);
0061     ret = lp8788_update_bits(led->lp, addr, mask, val);
0062     if (ret)
0063         return ret;
0064 
0065     /* current configuration */
0066     addr = lp8788_iout_addr[cfg->num];
0067     mask = lp8788_iout_mask[cfg->num];
0068     val = cfg->iout;
0069 
0070     return lp8788_update_bits(led->lp, addr, mask, val);
0071 }
0072 
0073 static int lp8788_led_enable(struct lp8788_led *led,
0074             enum lp8788_isink_number num, int on)
0075 {
0076     int ret;
0077 
0078     u8 mask = 1 << num;
0079     u8 val = on << num;
0080 
0081     ret = lp8788_update_bits(led->lp, LP8788_ISINK_CTRL, mask, val);
0082     if (ret == 0)
0083         led->on = on;
0084 
0085     return ret;
0086 }
0087 
0088 static int lp8788_brightness_set(struct led_classdev *led_cdev,
0089                 enum led_brightness val)
0090 {
0091     struct lp8788_led *led =
0092             container_of(led_cdev, struct lp8788_led, led_dev);
0093 
0094     enum lp8788_isink_number num = led->isink_num;
0095     int enable, ret;
0096 
0097     mutex_lock(&led->lock);
0098 
0099     switch (num) {
0100     case LP8788_ISINK_1:
0101     case LP8788_ISINK_2:
0102     case LP8788_ISINK_3:
0103         ret = lp8788_write_byte(led->lp, lp8788_pwm_addr[num], val);
0104         if (ret < 0)
0105             goto unlock;
0106         break;
0107     default:
0108         mutex_unlock(&led->lock);
0109         return -EINVAL;
0110     }
0111 
0112     enable = (val > 0) ? 1 : 0;
0113     if (enable != led->on)
0114         ret = lp8788_led_enable(led, num, enable);
0115 unlock:
0116     mutex_unlock(&led->lock);
0117     return ret;
0118 }
0119 
0120 static int lp8788_led_probe(struct platform_device *pdev)
0121 {
0122     struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
0123     struct lp8788_led_platform_data *led_pdata;
0124     struct lp8788_led *led;
0125     struct device *dev = &pdev->dev;
0126     int ret;
0127 
0128     led = devm_kzalloc(dev, sizeof(struct lp8788_led), GFP_KERNEL);
0129     if (!led)
0130         return -ENOMEM;
0131 
0132     led->lp = lp;
0133     led->led_dev.max_brightness = MAX_BRIGHTNESS;
0134     led->led_dev.brightness_set_blocking = lp8788_brightness_set;
0135 
0136     led_pdata = lp->pdata ? lp->pdata->led_pdata : NULL;
0137 
0138     if (!led_pdata || !led_pdata->name)
0139         led->led_dev.name = DEFAULT_LED_NAME;
0140     else
0141         led->led_dev.name = led_pdata->name;
0142 
0143     mutex_init(&led->lock);
0144 
0145     ret = lp8788_led_init_device(led, led_pdata);
0146     if (ret) {
0147         dev_err(dev, "led init device err: %d\n", ret);
0148         return ret;
0149     }
0150 
0151     ret = devm_led_classdev_register(dev, &led->led_dev);
0152     if (ret) {
0153         dev_err(dev, "led register err: %d\n", ret);
0154         return ret;
0155     }
0156 
0157     return 0;
0158 }
0159 
0160 static struct platform_driver lp8788_led_driver = {
0161     .probe = lp8788_led_probe,
0162     .driver = {
0163         .name = LP8788_DEV_KEYLED,
0164     },
0165 };
0166 module_platform_driver(lp8788_led_driver);
0167 
0168 MODULE_DESCRIPTION("Texas Instruments LP8788 Keyboard LED Driver");
0169 MODULE_AUTHOR("Milo Kim");
0170 MODULE_LICENSE("GPL");
0171 MODULE_ALIAS("platform:lp8788-keyled");