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