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/workqueue.h> 17 #include <linux/leds.h> 18 #include <linux/mfd/max8997.h> 19 #include <linux/mfd/max8997-private.h> 20 #include <linux/platform_device.h> 21 22 #define MAX8997_LED_FLASH_SHIFT 3 23 #define MAX8997_LED_FLASH_CUR_MASK 0xf8 24 #define MAX8997_LED_MOVIE_SHIFT 4 25 #define MAX8997_LED_MOVIE_CUR_MASK 0xf0 26 27 #define MAX8997_LED_FLASH_MAX_BRIGHTNESS 0x1f 28 #define MAX8997_LED_MOVIE_MAX_BRIGHTNESS 0xf 29 #define MAX8997_LED_NONE_MAX_BRIGHTNESS 0 30 31 #define MAX8997_LED0_FLASH_MASK 0x1 32 #define MAX8997_LED0_FLASH_PIN_MASK 0x5 33 #define MAX8997_LED0_MOVIE_MASK 0x8 34 #define MAX8997_LED0_MOVIE_PIN_MASK 0x28 35 36 #define MAX8997_LED1_FLASH_MASK 0x2 37 #define MAX8997_LED1_FLASH_PIN_MASK 0x6 38 #define MAX8997_LED1_MOVIE_MASK 0x10 39 #define MAX8997_LED1_MOVIE_PIN_MASK 0x30 40 41 #define MAX8997_LED_BOOST_ENABLE_MASK (1 << 6) 42 43 struct max8997_led { 44 struct max8997_dev *iodev; 45 struct led_classdev cdev; 46 bool enabled; 47 int id; 48 enum max8997_led_mode led_mode; 49 struct mutex mutex; 50 }; 51 52 static void max8997_led_clear_mode(struct max8997_led *led, 53 enum max8997_led_mode mode) 54 { 55 struct i2c_client *client = led->iodev->i2c; 56 u8 val = 0, mask = 0; 57 int ret; 58 59 switch (mode) { 60 case MAX8997_FLASH_MODE: 61 mask = led->id ? 62 MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK; 63 break; 64 case MAX8997_MOVIE_MODE: 65 mask = led->id ? 66 MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK; 67 break; 68 case MAX8997_FLASH_PIN_CONTROL_MODE: 69 mask = led->id ? 70 MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK; 71 break; 72 case MAX8997_MOVIE_PIN_CONTROL_MODE: 73 mask = led->id ? 74 MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK; 75 break; 76 default: 77 break; 78 } 79 80 if (mask) { 81 ret = max8997_update_reg(client, 82 MAX8997_REG_LEN_CNTL, val, mask); 83 if (ret) 84 dev_err(led->iodev->dev, 85 "failed to update register(%d)\n", ret); 86 } 87 } 88 89 static void max8997_led_set_mode(struct max8997_led *led, 90 enum max8997_led_mode mode) 91 { 92 int ret; 93 struct i2c_client *client = led->iodev->i2c; 94 u8 mask = 0; 95 96 /* First, clear the previous mode */ 97 max8997_led_clear_mode(led, led->led_mode); 98 99 switch (mode) { 100 case MAX8997_FLASH_MODE: 101 mask = led->id ? 102 MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK; 103 led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS; 104 break; 105 case MAX8997_MOVIE_MODE: 106 mask = led->id ? 107 MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK; 108 led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS; 109 break; 110 case MAX8997_FLASH_PIN_CONTROL_MODE: 111 mask = led->id ? 112 MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK; 113 led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS; 114 break; 115 case MAX8997_MOVIE_PIN_CONTROL_MODE: 116 mask = led->id ? 117 MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK; 118 led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS; 119 break; 120 default: 121 led->cdev.max_brightness = MAX8997_LED_NONE_MAX_BRIGHTNESS; 122 break; 123 } 124 125 if (mask) { 126 ret = max8997_update_reg(client, 127 MAX8997_REG_LEN_CNTL, mask, mask); 128 if (ret) 129 dev_err(led->iodev->dev, 130 "failed to update register(%d)\n", ret); 131 } 132 133 led->led_mode = mode; 134 } 135 136 static void max8997_led_enable(struct max8997_led *led, bool enable) 137 { 138 int ret; 139 struct i2c_client *client = led->iodev->i2c; 140 u8 val = 0, mask = MAX8997_LED_BOOST_ENABLE_MASK; 141 142 if (led->enabled == enable) 143 return; 144 145 val = enable ? MAX8997_LED_BOOST_ENABLE_MASK : 0; 146 147 ret = max8997_update_reg(client, MAX8997_REG_BOOST_CNTL, val, mask); 148 if (ret) 149 dev_err(led->iodev->dev, 150 "failed to update register(%d)\n", ret); 151 152 led->enabled = enable; 153 } 154 155 static void max8997_led_set_current(struct max8997_led *led, 156 enum led_brightness value) 157 { 158 int ret; 159 struct i2c_client *client = led->iodev->i2c; 160 u8 val = 0, mask = 0, reg = 0; 161 162 switch (led->led_mode) { 163 case MAX8997_FLASH_MODE: 164 case MAX8997_FLASH_PIN_CONTROL_MODE: 165 val = value << MAX8997_LED_FLASH_SHIFT; 166 mask = MAX8997_LED_FLASH_CUR_MASK; 167 reg = led->id ? MAX8997_REG_FLASH2_CUR : MAX8997_REG_FLASH1_CUR; 168 break; 169 case MAX8997_MOVIE_MODE: 170 case MAX8997_MOVIE_PIN_CONTROL_MODE: 171 val = value << MAX8997_LED_MOVIE_SHIFT; 172 mask = MAX8997_LED_MOVIE_CUR_MASK; 173 reg = MAX8997_REG_MOVIE_CUR; 174 break; 175 default: 176 break; 177 } 178 179 if (mask) { 180 ret = max8997_update_reg(client, reg, val, mask); 181 if (ret) 182 dev_err(led->iodev->dev, 183 "failed to update register(%d)\n", ret); 184 } 185 } 186 187 static void max8997_led_brightness_set(struct led_classdev *led_cdev, 188 enum led_brightness value) 189 { 190 struct max8997_led *led = 191 container_of(led_cdev, struct max8997_led, cdev); 192 193 if (value) { 194 max8997_led_set_current(led, value); 195 max8997_led_enable(led, true); 196 } else { 197 max8997_led_set_current(led, value); 198 max8997_led_enable(led, false); 199 } 200 } 201 202 static ssize_t max8997_led_show_mode(struct device *dev, 203 struct device_attribute *attr, char *buf) 204 { 205 struct led_classdev *led_cdev = dev_get_drvdata(dev); 206 struct max8997_led *led = 207 container_of(led_cdev, struct max8997_led, cdev); 208 ssize_t ret = 0; 209 210 mutex_lock(&led->mutex); 211 212 switch (led->led_mode) { 213 case MAX8997_FLASH_MODE: 214 ret += sprintf(buf, "FLASH\n"); 215 break; 216 case MAX8997_MOVIE_MODE: 217 ret += sprintf(buf, "MOVIE\n"); 218 break; 219 case MAX8997_FLASH_PIN_CONTROL_MODE: 220 ret += sprintf(buf, "FLASH_PIN_CONTROL\n"); 221 break; 222 case MAX8997_MOVIE_PIN_CONTROL_MODE: 223 ret += sprintf(buf, "MOVIE_PIN_CONTROL\n"); 224 break; 225 default: 226 ret += sprintf(buf, "NONE\n"); 227 break; 228 } 229 230 mutex_unlock(&led->mutex); 231 232 return ret; 233 } 234 235 static ssize_t max8997_led_store_mode(struct device *dev, 236 struct device_attribute *attr, 237 const char *buf, size_t size) 238 { 239 struct led_classdev *led_cdev = dev_get_drvdata(dev); 240 struct max8997_led *led = 241 container_of(led_cdev, struct max8997_led, cdev); 242 enum max8997_led_mode mode; 243 244 mutex_lock(&led->mutex); 245 246 if (!strncmp(buf, "FLASH_PIN_CONTROL", 17)) 247 mode = MAX8997_FLASH_PIN_CONTROL_MODE; 248 else if (!strncmp(buf, "MOVIE_PIN_CONTROL", 17)) 249 mode = MAX8997_MOVIE_PIN_CONTROL_MODE; 250 else if (!strncmp(buf, "FLASH", 5)) 251 mode = MAX8997_FLASH_MODE; 252 else if (!strncmp(buf, "MOVIE", 5)) 253 mode = MAX8997_MOVIE_MODE; 254 else 255 mode = MAX8997_NONE; 256 257 max8997_led_set_mode(led, mode); 258 259 mutex_unlock(&led->mutex); 260 261 return size; 262 } 263 264 static DEVICE_ATTR(mode, 0644, max8997_led_show_mode, max8997_led_store_mode); 265 266 static int __devinit max8997_led_probe(struct platform_device *pdev) 267 { 268 struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); 269 struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); 270 struct max8997_led *led; 271 char name[20]; 272 int ret = 0; 273 274 if (pdata == NULL) { 275 dev_err(&pdev->dev, "no platform data\n"); 276 return -ENODEV; 277 } 278 279 led = kzalloc(sizeof(*led), GFP_KERNEL); 280 if (led == NULL) { 281 ret = -ENOMEM; 282 goto err_mem; 283 } 284 285 led->id = pdev->id; 286 snprintf(name, sizeof(name), "max8997-led%d", pdev->id); 287 288 led->cdev.name = name; 289 led->cdev.brightness_set = max8997_led_brightness_set; 290 led->cdev.flags |= LED_CORE_SUSPENDRESUME; 291 led->cdev.brightness = 0; 292 led->iodev = iodev; 293 294 /* initialize mode and brightness according to platform_data */ 295 if (pdata->led_pdata) { 296 u8 mode = 0, brightness = 0; 297 298 mode = pdata->led_pdata->mode[led->id]; 299 brightness = pdata->led_pdata->brightness[led->id]; 300 301 max8997_led_set_mode(led, pdata->led_pdata->mode[led->id]); 302 303 if (brightness > led->cdev.max_brightness) 304 brightness = led->cdev.max_brightness; 305 max8997_led_set_current(led, brightness); 306 led->cdev.brightness = brightness; 307 } else { 308 max8997_led_set_mode(led, MAX8997_NONE); 309 max8997_led_set_current(led, 0); 310 } 311 312 mutex_init(&led->mutex); 313 314 platform_set_drvdata(pdev, led); 315 316 ret = led_classdev_register(&pdev->dev, &led->cdev); 317 if (ret < 0) 318 goto err_led; 319 320 ret = device_create_file(led->cdev.dev, &dev_attr_mode); 321 if (ret != 0) { 322 dev_err(&pdev->dev, 323 "failed to create file: %d\n", ret); 324 goto err_file; 325 } 326 327 return 0; 328 329 err_file: 330 led_classdev_unregister(&led->cdev); 331 err_led: 332 kfree(led); 333 err_mem: 334 return ret; 335 } 336 337 static int __devexit max8997_led_remove(struct platform_device *pdev) 338 { 339 struct max8997_led *led = platform_get_drvdata(pdev); 340 341 device_remove_file(led->cdev.dev, &dev_attr_mode); 342 led_classdev_unregister(&led->cdev); 343 kfree(led); 344 345 return 0; 346 } 347 348 static struct platform_driver max8997_led_driver = { 349 .driver = { 350 .name = "max8997-led", 351 .owner = THIS_MODULE, 352 }, 353 .probe = max8997_led_probe, 354 .remove = __devexit_p(max8997_led_remove), 355 }; 356 357 module_platform_driver(max8997_led_driver); 358 359 MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 360 MODULE_DESCRIPTION("MAX8997 LED driver"); 361 MODULE_LICENSE("GPL"); 362 MODULE_ALIAS("platform:max8997-led"); 363