1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * TI LP8788 MFD - keyled driver 4 * 5 * Copyright 2012 Texas Instruments 6 * 7 * Author: Milo(Woogyom) Kim <milo.kim@ti.com> 8 */ 9 10 #include <linux/module.h> 11 #include <linux/slab.h> 12 #include <linux/err.h> 13 #include <linux/platform_device.h> 14 #include <linux/leds.h> 15 #include <linux/mutex.h> 16 #include <linux/mfd/lp8788.h> 17 #include <linux/mfd/lp8788-isink.h> 18 19 #define MAX_BRIGHTNESS LP8788_ISINK_MAX_PWM 20 #define DEFAULT_LED_NAME "keyboard-backlight" 21 22 struct lp8788_led { 23 struct lp8788 *lp; 24 struct mutex lock; 25 struct led_classdev led_dev; 26 enum lp8788_isink_number isink_num; 27 int on; 28 }; 29 30 struct lp8788_led_config { 31 enum lp8788_isink_scale scale; 32 enum lp8788_isink_number num; 33 int iout; 34 }; 35 36 static struct lp8788_led_config default_led_config = { 37 .scale = LP8788_ISINK_SCALE_100mA, 38 .num = LP8788_ISINK_3, 39 .iout = 0, 40 }; 41 42 static int lp8788_led_init_device(struct lp8788_led *led, 43 struct lp8788_led_platform_data *pdata) 44 { 45 struct lp8788_led_config *cfg = &default_led_config; 46 u8 addr, mask, val; 47 int ret; 48 49 if (pdata) { 50 cfg->scale = pdata->scale; 51 cfg->num = pdata->num; 52 cfg->iout = pdata->iout_code; 53 } 54 55 led->isink_num = cfg->num; 56 57 /* scale configuration */ 58 addr = LP8788_ISINK_CTRL; 59 mask = 1 << (cfg->num + LP8788_ISINK_SCALE_OFFSET); 60 val = cfg->scale << (cfg->num + LP8788_ISINK_SCALE_OFFSET); 61 ret = lp8788_update_bits(led->lp, addr, mask, val); 62 if (ret) 63 return ret; 64 65 /* current configuration */ 66 addr = lp8788_iout_addr[cfg->num]; 67 mask = lp8788_iout_mask[cfg->num]; 68 val = cfg->iout; 69 70 return lp8788_update_bits(led->lp, addr, mask, val); 71 } 72 73 static int lp8788_led_enable(struct lp8788_led *led, 74 enum lp8788_isink_number num, int on) 75 { 76 int ret; 77 78 u8 mask = 1 << num; 79 u8 val = on << num; 80 81 ret = lp8788_update_bits(led->lp, LP8788_ISINK_CTRL, mask, val); 82 if (ret == 0) 83 led->on = on; 84 85 return ret; 86 } 87 88 static int lp8788_brightness_set(struct led_classdev *led_cdev, 89 enum led_brightness val) 90 { 91 struct lp8788_led *led = 92 container_of(led_cdev, struct lp8788_led, led_dev); 93 94 enum lp8788_isink_number num = led->isink_num; 95 int enable, ret; 96 97 mutex_lock(&led->lock); 98 99 switch (num) { 100 case LP8788_ISINK_1: 101 case LP8788_ISINK_2: 102 case LP8788_ISINK_3: 103 ret = lp8788_write_byte(led->lp, lp8788_pwm_addr[num], val); 104 if (ret < 0) 105 goto unlock; 106 break; 107 default: 108 mutex_unlock(&led->lock); 109 return -EINVAL; 110 } 111 112 enable = (val > 0) ? 1 : 0; 113 if (enable != led->on) 114 ret = lp8788_led_enable(led, num, enable); 115 unlock: 116 mutex_unlock(&led->lock); 117 return ret; 118 } 119 120 static int lp8788_led_probe(struct platform_device *pdev) 121 { 122 struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); 123 struct lp8788_led_platform_data *led_pdata; 124 struct lp8788_led *led; 125 struct device *dev = &pdev->dev; 126 int ret; 127 128 led = devm_kzalloc(dev, sizeof(struct lp8788_led), GFP_KERNEL); 129 if (!led) 130 return -ENOMEM; 131 132 led->lp = lp; 133 led->led_dev.max_brightness = MAX_BRIGHTNESS; 134 led->led_dev.brightness_set_blocking = lp8788_brightness_set; 135 136 led_pdata = lp->pdata ? lp->pdata->led_pdata : NULL; 137 138 if (!led_pdata || !led_pdata->name) 139 led->led_dev.name = DEFAULT_LED_NAME; 140 else 141 led->led_dev.name = led_pdata->name; 142 143 mutex_init(&led->lock); 144 145 ret = lp8788_led_init_device(led, led_pdata); 146 if (ret) { 147 dev_err(dev, "led init device err: %d\n", ret); 148 return ret; 149 } 150 151 ret = devm_led_classdev_register(dev, &led->led_dev); 152 if (ret) { 153 dev_err(dev, "led register err: %d\n", ret); 154 return ret; 155 } 156 157 return 0; 158 } 159 160 static struct platform_driver lp8788_led_driver = { 161 .probe = lp8788_led_probe, 162 .driver = { 163 .name = LP8788_DEV_KEYLED, 164 }, 165 }; 166 module_platform_driver(lp8788_led_driver); 167 168 MODULE_DESCRIPTION("Texas Instruments LP8788 Keyboard LED Driver"); 169 MODULE_AUTHOR("Milo Kim"); 170 MODULE_LICENSE("GPL"); 171 MODULE_ALIAS("platform:lp8788-keyled"); 172