295 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * leds-max8997.c - LED class driver for MAX8997 LEDs.4 *5 * Copyright (C) 2011 Samsung Electronics6 * Donggeun Kim <dg77.kim@samsung.com>7 */8 9#include <linux/module.h>10#include <linux/err.h>11#include <linux/slab.h>12#include <linux/leds.h>13#include <linux/mfd/max8997.h>14#include <linux/mfd/max8997-private.h>15#include <linux/platform_device.h>16 17#define MAX8997_LED_FLASH_SHIFT 318#define MAX8997_LED_FLASH_CUR_MASK 0xf819#define MAX8997_LED_MOVIE_SHIFT 420#define MAX8997_LED_MOVIE_CUR_MASK 0xf021 22#define MAX8997_LED_FLASH_MAX_BRIGHTNESS 0x1f23#define MAX8997_LED_MOVIE_MAX_BRIGHTNESS 0xf24#define MAX8997_LED_NONE_MAX_BRIGHTNESS 025 26#define MAX8997_LED0_FLASH_MASK 0x127#define MAX8997_LED0_FLASH_PIN_MASK 0x528#define MAX8997_LED0_MOVIE_MASK 0x829#define MAX8997_LED0_MOVIE_PIN_MASK 0x2830 31#define MAX8997_LED1_FLASH_MASK 0x232#define MAX8997_LED1_FLASH_PIN_MASK 0x633#define MAX8997_LED1_MOVIE_MASK 0x1034#define MAX8997_LED1_MOVIE_PIN_MASK 0x3035 36#define MAX8997_LED_BOOST_ENABLE_MASK (1 << 6)37 38struct max8997_led {39 struct max8997_dev *iodev;40 struct led_classdev cdev;41 bool enabled;42 int id;43 enum max8997_led_mode led_mode;44 struct mutex mutex;45};46 47static void max8997_led_set_mode(struct max8997_led *led,48 enum max8997_led_mode mode)49{50 int ret;51 struct i2c_client *client = led->iodev->i2c;52 u8 mask = 0, val;53 54 switch (mode) {55 case MAX8997_FLASH_MODE:56 mask = MAX8997_LED1_FLASH_MASK | MAX8997_LED0_FLASH_MASK;57 val = led->id ?58 MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK;59 led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS;60 break;61 case MAX8997_MOVIE_MODE:62 mask = MAX8997_LED1_MOVIE_MASK | MAX8997_LED0_MOVIE_MASK;63 val = led->id ?64 MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK;65 led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS;66 break;67 case MAX8997_FLASH_PIN_CONTROL_MODE:68 mask = MAX8997_LED1_FLASH_PIN_MASK |69 MAX8997_LED0_FLASH_PIN_MASK;70 val = led->id ?71 MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK;72 led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS;73 break;74 case MAX8997_MOVIE_PIN_CONTROL_MODE:75 mask = MAX8997_LED1_MOVIE_PIN_MASK |76 MAX8997_LED0_MOVIE_PIN_MASK;77 val = led->id ?78 MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK;79 led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS;80 break;81 default:82 led->cdev.max_brightness = MAX8997_LED_NONE_MAX_BRIGHTNESS;83 break;84 }85 86 if (mask) {87 ret = max8997_update_reg(client, MAX8997_REG_LEN_CNTL, val,88 mask);89 if (ret)90 dev_err(led->iodev->dev,91 "failed to update register(%d)\n", ret);92 }93 94 led->led_mode = mode;95}96 97static void max8997_led_enable(struct max8997_led *led, bool enable)98{99 int ret;100 struct i2c_client *client = led->iodev->i2c;101 u8 val = 0, mask = MAX8997_LED_BOOST_ENABLE_MASK;102 103 if (led->enabled == enable)104 return;105 106 val = enable ? MAX8997_LED_BOOST_ENABLE_MASK : 0;107 108 ret = max8997_update_reg(client, MAX8997_REG_BOOST_CNTL, val, mask);109 if (ret)110 dev_err(led->iodev->dev,111 "failed to update register(%d)\n", ret);112 113 led->enabled = enable;114}115 116static void max8997_led_set_current(struct max8997_led *led,117 enum led_brightness value)118{119 int ret;120 struct i2c_client *client = led->iodev->i2c;121 u8 val = 0, mask = 0, reg = 0;122 123 switch (led->led_mode) {124 case MAX8997_FLASH_MODE:125 case MAX8997_FLASH_PIN_CONTROL_MODE:126 val = value << MAX8997_LED_FLASH_SHIFT;127 mask = MAX8997_LED_FLASH_CUR_MASK;128 reg = led->id ? MAX8997_REG_FLASH2_CUR : MAX8997_REG_FLASH1_CUR;129 break;130 case MAX8997_MOVIE_MODE:131 case MAX8997_MOVIE_PIN_CONTROL_MODE:132 val = value << MAX8997_LED_MOVIE_SHIFT;133 mask = MAX8997_LED_MOVIE_CUR_MASK;134 reg = MAX8997_REG_MOVIE_CUR;135 break;136 default:137 break;138 }139 140 if (mask) {141 ret = max8997_update_reg(client, reg, val, mask);142 if (ret)143 dev_err(led->iodev->dev,144 "failed to update register(%d)\n", ret);145 }146}147 148static void max8997_led_brightness_set(struct led_classdev *led_cdev,149 enum led_brightness value)150{151 struct max8997_led *led =152 container_of(led_cdev, struct max8997_led, cdev);153 154 if (value) {155 max8997_led_set_current(led, value);156 max8997_led_enable(led, true);157 } else {158 max8997_led_set_current(led, value);159 max8997_led_enable(led, false);160 }161}162 163static ssize_t mode_show(struct device *dev,164 struct device_attribute *attr, char *buf)165{166 struct led_classdev *led_cdev = dev_get_drvdata(dev);167 struct max8997_led *led =168 container_of(led_cdev, struct max8997_led, cdev);169 ssize_t ret = 0;170 171 mutex_lock(&led->mutex);172 173 switch (led->led_mode) {174 case MAX8997_FLASH_MODE:175 ret += sprintf(buf, "FLASH\n");176 break;177 case MAX8997_MOVIE_MODE:178 ret += sprintf(buf, "MOVIE\n");179 break;180 case MAX8997_FLASH_PIN_CONTROL_MODE:181 ret += sprintf(buf, "FLASH_PIN_CONTROL\n");182 break;183 case MAX8997_MOVIE_PIN_CONTROL_MODE:184 ret += sprintf(buf, "MOVIE_PIN_CONTROL\n");185 break;186 default:187 ret += sprintf(buf, "NONE\n");188 break;189 }190 191 mutex_unlock(&led->mutex);192 193 return ret;194}195 196static ssize_t mode_store(struct device *dev,197 struct device_attribute *attr,198 const char *buf, size_t size)199{200 struct led_classdev *led_cdev = dev_get_drvdata(dev);201 struct max8997_led *led =202 container_of(led_cdev, struct max8997_led, cdev);203 enum max8997_led_mode mode;204 205 mutex_lock(&led->mutex);206 207 if (!strncmp(buf, "FLASH_PIN_CONTROL", 17))208 mode = MAX8997_FLASH_PIN_CONTROL_MODE;209 else if (!strncmp(buf, "MOVIE_PIN_CONTROL", 17))210 mode = MAX8997_MOVIE_PIN_CONTROL_MODE;211 else if (!strncmp(buf, "FLASH", 5))212 mode = MAX8997_FLASH_MODE;213 else if (!strncmp(buf, "MOVIE", 5))214 mode = MAX8997_MOVIE_MODE;215 else216 mode = MAX8997_NONE;217 218 max8997_led_set_mode(led, mode);219 220 mutex_unlock(&led->mutex);221 222 return size;223}224 225static DEVICE_ATTR_RW(mode);226 227static struct attribute *max8997_attrs[] = {228 &dev_attr_mode.attr,229 NULL230};231ATTRIBUTE_GROUPS(max8997);232 233static int max8997_led_probe(struct platform_device *pdev)234{235 struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);236 struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev);237 struct max8997_led *led;238 char name[20];239 int ret = 0;240 241 led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);242 if (led == NULL)243 return -ENOMEM;244 245 led->id = pdev->id;246 snprintf(name, sizeof(name), "max8997-led%d", pdev->id);247 248 led->cdev.name = name;249 led->cdev.brightness_set = max8997_led_brightness_set;250 led->cdev.flags |= LED_CORE_SUSPENDRESUME;251 led->cdev.brightness = 0;252 led->cdev.groups = max8997_groups;253 led->iodev = iodev;254 255 /* initialize mode and brightness according to platform_data */256 if (pdata && pdata->led_pdata) {257 u8 mode = 0, brightness = 0;258 259 mode = pdata->led_pdata->mode[led->id];260 brightness = pdata->led_pdata->brightness[led->id];261 262 max8997_led_set_mode(led, mode);263 264 if (brightness > led->cdev.max_brightness)265 brightness = led->cdev.max_brightness;266 max8997_led_set_current(led, brightness);267 led->cdev.brightness = brightness;268 } else {269 max8997_led_set_mode(led, MAX8997_NONE);270 max8997_led_set_current(led, 0);271 }272 273 mutex_init(&led->mutex);274 275 ret = devm_led_classdev_register(&pdev->dev, &led->cdev);276 if (ret < 0)277 return ret;278 279 return 0;280}281 282static struct platform_driver max8997_led_driver = {283 .driver = {284 .name = "max8997-led",285 },286 .probe = max8997_led_probe,287};288 289module_platform_driver(max8997_led_driver);290 291MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");292MODULE_DESCRIPTION("MAX8997 LED driver");293MODULE_LICENSE("GPL");294MODULE_ALIAS("platform:max8997-led");295