Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.4-rc3 372 lines 9.2 kB view raw
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 43struct 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 52static 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 89static 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 136static 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 155static 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 187static 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 202static 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 235static 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 264static DEVICE_ATTR(mode, 0644, max8997_led_show_mode, max8997_led_store_mode); 265 266static 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 329err_file: 330 led_classdev_unregister(&led->cdev); 331err_led: 332 kfree(led); 333err_mem: 334 return ret; 335} 336 337static 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 348static 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 357static int __init max8997_led_init(void) 358{ 359 return platform_driver_register(&max8997_led_driver); 360} 361module_init(max8997_led_init); 362 363static void __exit max8997_led_exit(void) 364{ 365 platform_driver_unregister(&max8997_led_driver); 366} 367module_exit(max8997_led_exit); 368 369MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 370MODULE_DESCRIPTION("MAX8997 LED driver"); 371MODULE_LICENSE("GPL"); 372MODULE_ALIAS("platform:max8997-led");