18584cb82SDonggeun Kim /* 28584cb82SDonggeun Kim * leds-max8997.c - LED class driver for MAX8997 LEDs. 38584cb82SDonggeun Kim * 48584cb82SDonggeun Kim * Copyright (C) 2011 Samsung Electronics 58584cb82SDonggeun Kim * Donggeun Kim <dg77.kim@samsung.com> 68584cb82SDonggeun Kim * 78584cb82SDonggeun Kim * This program is free software; you can redistribute it and/or modify 88584cb82SDonggeun Kim * it under the terms of the GNU General Public License version 2 as 98584cb82SDonggeun Kim * published by the Free Software Foundation. 108584cb82SDonggeun Kim * 118584cb82SDonggeun Kim */ 128584cb82SDonggeun Kim 138584cb82SDonggeun Kim #include <linux/module.h> 148584cb82SDonggeun Kim #include <linux/err.h> 158584cb82SDonggeun Kim #include <linux/slab.h> 168584cb82SDonggeun Kim #include <linux/workqueue.h> 178584cb82SDonggeun Kim #include <linux/leds.h> 188584cb82SDonggeun Kim #include <linux/mfd/max8997.h> 198584cb82SDonggeun Kim #include <linux/mfd/max8997-private.h> 208584cb82SDonggeun Kim #include <linux/platform_device.h> 218584cb82SDonggeun Kim 228584cb82SDonggeun Kim #define MAX8997_LED_FLASH_SHIFT 3 238584cb82SDonggeun Kim #define MAX8997_LED_FLASH_CUR_MASK 0xf8 248584cb82SDonggeun Kim #define MAX8997_LED_MOVIE_SHIFT 4 258584cb82SDonggeun Kim #define MAX8997_LED_MOVIE_CUR_MASK 0xf0 268584cb82SDonggeun Kim 278584cb82SDonggeun Kim #define MAX8997_LED_FLASH_MAX_BRIGHTNESS 0x1f 288584cb82SDonggeun Kim #define MAX8997_LED_MOVIE_MAX_BRIGHTNESS 0xf 298584cb82SDonggeun Kim #define MAX8997_LED_NONE_MAX_BRIGHTNESS 0 308584cb82SDonggeun Kim 318584cb82SDonggeun Kim #define MAX8997_LED0_FLASH_MASK 0x1 328584cb82SDonggeun Kim #define MAX8997_LED0_FLASH_PIN_MASK 0x5 338584cb82SDonggeun Kim #define MAX8997_LED0_MOVIE_MASK 0x8 348584cb82SDonggeun Kim #define MAX8997_LED0_MOVIE_PIN_MASK 0x28 358584cb82SDonggeun Kim 368584cb82SDonggeun Kim #define MAX8997_LED1_FLASH_MASK 0x2 378584cb82SDonggeun Kim #define MAX8997_LED1_FLASH_PIN_MASK 0x6 388584cb82SDonggeun Kim #define MAX8997_LED1_MOVIE_MASK 0x10 398584cb82SDonggeun Kim #define MAX8997_LED1_MOVIE_PIN_MASK 0x30 408584cb82SDonggeun Kim 418584cb82SDonggeun Kim #define MAX8997_LED_BOOST_ENABLE_MASK (1 << 6) 428584cb82SDonggeun Kim 438584cb82SDonggeun Kim struct max8997_led { 448584cb82SDonggeun Kim struct max8997_dev *iodev; 458584cb82SDonggeun Kim struct led_classdev cdev; 468584cb82SDonggeun Kim bool enabled; 478584cb82SDonggeun Kim int id; 488584cb82SDonggeun Kim enum max8997_led_mode led_mode; 498584cb82SDonggeun Kim struct mutex mutex; 508584cb82SDonggeun Kim }; 518584cb82SDonggeun Kim 528584cb82SDonggeun Kim static void max8997_led_clear_mode(struct max8997_led *led, 538584cb82SDonggeun Kim enum max8997_led_mode mode) 548584cb82SDonggeun Kim { 558584cb82SDonggeun Kim struct i2c_client *client = led->iodev->i2c; 568584cb82SDonggeun Kim u8 val = 0, mask = 0; 578584cb82SDonggeun Kim int ret; 588584cb82SDonggeun Kim 598584cb82SDonggeun Kim switch (mode) { 608584cb82SDonggeun Kim case MAX8997_FLASH_MODE: 618584cb82SDonggeun Kim mask = led->id ? 628584cb82SDonggeun Kim MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK; 638584cb82SDonggeun Kim break; 648584cb82SDonggeun Kim case MAX8997_MOVIE_MODE: 658584cb82SDonggeun Kim mask = led->id ? 668584cb82SDonggeun Kim MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK; 678584cb82SDonggeun Kim break; 688584cb82SDonggeun Kim case MAX8997_FLASH_PIN_CONTROL_MODE: 698584cb82SDonggeun Kim mask = led->id ? 708584cb82SDonggeun Kim MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK; 718584cb82SDonggeun Kim break; 728584cb82SDonggeun Kim case MAX8997_MOVIE_PIN_CONTROL_MODE: 738584cb82SDonggeun Kim mask = led->id ? 748584cb82SDonggeun Kim MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK; 758584cb82SDonggeun Kim break; 768584cb82SDonggeun Kim default: 778584cb82SDonggeun Kim break; 788584cb82SDonggeun Kim } 798584cb82SDonggeun Kim 808584cb82SDonggeun Kim if (mask) { 818584cb82SDonggeun Kim ret = max8997_update_reg(client, 828584cb82SDonggeun Kim MAX8997_REG_LEN_CNTL, val, mask); 838584cb82SDonggeun Kim if (ret) 848584cb82SDonggeun Kim dev_err(led->iodev->dev, 858584cb82SDonggeun Kim "failed to update register(%d)\n", ret); 868584cb82SDonggeun Kim } 878584cb82SDonggeun Kim } 888584cb82SDonggeun Kim 898584cb82SDonggeun Kim static void max8997_led_set_mode(struct max8997_led *led, 908584cb82SDonggeun Kim enum max8997_led_mode mode) 918584cb82SDonggeun Kim { 928584cb82SDonggeun Kim int ret; 938584cb82SDonggeun Kim struct i2c_client *client = led->iodev->i2c; 948584cb82SDonggeun Kim u8 mask = 0; 958584cb82SDonggeun Kim 968584cb82SDonggeun Kim /* First, clear the previous mode */ 978584cb82SDonggeun Kim max8997_led_clear_mode(led, led->led_mode); 988584cb82SDonggeun Kim 998584cb82SDonggeun Kim switch (mode) { 1008584cb82SDonggeun Kim case MAX8997_FLASH_MODE: 1018584cb82SDonggeun Kim mask = led->id ? 1028584cb82SDonggeun Kim MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK; 1038584cb82SDonggeun Kim led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS; 1048584cb82SDonggeun Kim break; 1058584cb82SDonggeun Kim case MAX8997_MOVIE_MODE: 1068584cb82SDonggeun Kim mask = led->id ? 1078584cb82SDonggeun Kim MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK; 1088584cb82SDonggeun Kim led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS; 1098584cb82SDonggeun Kim break; 1108584cb82SDonggeun Kim case MAX8997_FLASH_PIN_CONTROL_MODE: 1118584cb82SDonggeun Kim mask = led->id ? 1128584cb82SDonggeun Kim MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK; 1138584cb82SDonggeun Kim led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS; 1148584cb82SDonggeun Kim break; 1158584cb82SDonggeun Kim case MAX8997_MOVIE_PIN_CONTROL_MODE: 1168584cb82SDonggeun Kim mask = led->id ? 1178584cb82SDonggeun Kim MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK; 1188584cb82SDonggeun Kim led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS; 1198584cb82SDonggeun Kim break; 1208584cb82SDonggeun Kim default: 1218584cb82SDonggeun Kim led->cdev.max_brightness = MAX8997_LED_NONE_MAX_BRIGHTNESS; 1228584cb82SDonggeun Kim break; 1238584cb82SDonggeun Kim } 1248584cb82SDonggeun Kim 1258584cb82SDonggeun Kim if (mask) { 1268584cb82SDonggeun Kim ret = max8997_update_reg(client, 1278584cb82SDonggeun Kim MAX8997_REG_LEN_CNTL, mask, mask); 1288584cb82SDonggeun Kim if (ret) 1298584cb82SDonggeun Kim dev_err(led->iodev->dev, 1308584cb82SDonggeun Kim "failed to update register(%d)\n", ret); 1318584cb82SDonggeun Kim } 1328584cb82SDonggeun Kim 1338584cb82SDonggeun Kim led->led_mode = mode; 1348584cb82SDonggeun Kim } 1358584cb82SDonggeun Kim 1368584cb82SDonggeun Kim static void max8997_led_enable(struct max8997_led *led, bool enable) 1378584cb82SDonggeun Kim { 1388584cb82SDonggeun Kim int ret; 1398584cb82SDonggeun Kim struct i2c_client *client = led->iodev->i2c; 1408584cb82SDonggeun Kim u8 val = 0, mask = MAX8997_LED_BOOST_ENABLE_MASK; 1418584cb82SDonggeun Kim 1428584cb82SDonggeun Kim if (led->enabled == enable) 1438584cb82SDonggeun Kim return; 1448584cb82SDonggeun Kim 1458584cb82SDonggeun Kim val = enable ? MAX8997_LED_BOOST_ENABLE_MASK : 0; 1468584cb82SDonggeun Kim 1478584cb82SDonggeun Kim ret = max8997_update_reg(client, MAX8997_REG_BOOST_CNTL, val, mask); 1488584cb82SDonggeun Kim if (ret) 1498584cb82SDonggeun Kim dev_err(led->iodev->dev, 1508584cb82SDonggeun Kim "failed to update register(%d)\n", ret); 1518584cb82SDonggeun Kim 1528584cb82SDonggeun Kim led->enabled = enable; 1538584cb82SDonggeun Kim } 1548584cb82SDonggeun Kim 1558584cb82SDonggeun Kim static void max8997_led_set_current(struct max8997_led *led, 1568584cb82SDonggeun Kim enum led_brightness value) 1578584cb82SDonggeun Kim { 1588584cb82SDonggeun Kim int ret; 1598584cb82SDonggeun Kim struct i2c_client *client = led->iodev->i2c; 1608584cb82SDonggeun Kim u8 val = 0, mask = 0, reg = 0; 1618584cb82SDonggeun Kim 1628584cb82SDonggeun Kim switch (led->led_mode) { 1638584cb82SDonggeun Kim case MAX8997_FLASH_MODE: 1648584cb82SDonggeun Kim case MAX8997_FLASH_PIN_CONTROL_MODE: 1658584cb82SDonggeun Kim val = value << MAX8997_LED_FLASH_SHIFT; 1668584cb82SDonggeun Kim mask = MAX8997_LED_FLASH_CUR_MASK; 1678584cb82SDonggeun Kim reg = led->id ? MAX8997_REG_FLASH2_CUR : MAX8997_REG_FLASH1_CUR; 1688584cb82SDonggeun Kim break; 1698584cb82SDonggeun Kim case MAX8997_MOVIE_MODE: 1708584cb82SDonggeun Kim case MAX8997_MOVIE_PIN_CONTROL_MODE: 1718584cb82SDonggeun Kim val = value << MAX8997_LED_MOVIE_SHIFT; 1728584cb82SDonggeun Kim mask = MAX8997_LED_MOVIE_CUR_MASK; 1738584cb82SDonggeun Kim reg = MAX8997_REG_MOVIE_CUR; 1748584cb82SDonggeun Kim break; 1758584cb82SDonggeun Kim default: 1768584cb82SDonggeun Kim break; 1778584cb82SDonggeun Kim } 1788584cb82SDonggeun Kim 1798584cb82SDonggeun Kim if (mask) { 1808584cb82SDonggeun Kim ret = max8997_update_reg(client, reg, val, mask); 1818584cb82SDonggeun Kim if (ret) 1828584cb82SDonggeun Kim dev_err(led->iodev->dev, 1838584cb82SDonggeun Kim "failed to update register(%d)\n", ret); 1848584cb82SDonggeun Kim } 1858584cb82SDonggeun Kim } 1868584cb82SDonggeun Kim 1878584cb82SDonggeun Kim static void max8997_led_brightness_set(struct led_classdev *led_cdev, 1888584cb82SDonggeun Kim enum led_brightness value) 1898584cb82SDonggeun Kim { 1908584cb82SDonggeun Kim struct max8997_led *led = 1918584cb82SDonggeun Kim container_of(led_cdev, struct max8997_led, cdev); 1928584cb82SDonggeun Kim 1938584cb82SDonggeun Kim if (value) { 1948584cb82SDonggeun Kim max8997_led_set_current(led, value); 1958584cb82SDonggeun Kim max8997_led_enable(led, true); 1968584cb82SDonggeun Kim } else { 1978584cb82SDonggeun Kim max8997_led_set_current(led, value); 1988584cb82SDonggeun Kim max8997_led_enable(led, false); 1998584cb82SDonggeun Kim } 2008584cb82SDonggeun Kim } 2018584cb82SDonggeun Kim 2028584cb82SDonggeun Kim static ssize_t max8997_led_show_mode(struct device *dev, 2038584cb82SDonggeun Kim struct device_attribute *attr, char *buf) 2048584cb82SDonggeun Kim { 2058584cb82SDonggeun Kim struct led_classdev *led_cdev = dev_get_drvdata(dev); 2068584cb82SDonggeun Kim struct max8997_led *led = 2078584cb82SDonggeun Kim container_of(led_cdev, struct max8997_led, cdev); 2088584cb82SDonggeun Kim ssize_t ret = 0; 2098584cb82SDonggeun Kim 2108584cb82SDonggeun Kim mutex_lock(&led->mutex); 2118584cb82SDonggeun Kim 2128584cb82SDonggeun Kim switch (led->led_mode) { 2138584cb82SDonggeun Kim case MAX8997_FLASH_MODE: 2148584cb82SDonggeun Kim ret += sprintf(buf, "FLASH\n"); 2158584cb82SDonggeun Kim break; 2168584cb82SDonggeun Kim case MAX8997_MOVIE_MODE: 2178584cb82SDonggeun Kim ret += sprintf(buf, "MOVIE\n"); 2188584cb82SDonggeun Kim break; 2198584cb82SDonggeun Kim case MAX8997_FLASH_PIN_CONTROL_MODE: 2208584cb82SDonggeun Kim ret += sprintf(buf, "FLASH_PIN_CONTROL\n"); 2218584cb82SDonggeun Kim break; 2228584cb82SDonggeun Kim case MAX8997_MOVIE_PIN_CONTROL_MODE: 2238584cb82SDonggeun Kim ret += sprintf(buf, "MOVIE_PIN_CONTROL\n"); 2248584cb82SDonggeun Kim break; 2258584cb82SDonggeun Kim default: 2268584cb82SDonggeun Kim ret += sprintf(buf, "NONE\n"); 2278584cb82SDonggeun Kim break; 2288584cb82SDonggeun Kim } 2298584cb82SDonggeun Kim 2308584cb82SDonggeun Kim mutex_unlock(&led->mutex); 2318584cb82SDonggeun Kim 2328584cb82SDonggeun Kim return ret; 2338584cb82SDonggeun Kim } 2348584cb82SDonggeun Kim 2358584cb82SDonggeun Kim static ssize_t max8997_led_store_mode(struct device *dev, 2368584cb82SDonggeun Kim struct device_attribute *attr, 2378584cb82SDonggeun Kim const char *buf, size_t size) 2388584cb82SDonggeun Kim { 2398584cb82SDonggeun Kim struct led_classdev *led_cdev = dev_get_drvdata(dev); 2408584cb82SDonggeun Kim struct max8997_led *led = 2418584cb82SDonggeun Kim container_of(led_cdev, struct max8997_led, cdev); 2428584cb82SDonggeun Kim enum max8997_led_mode mode; 2438584cb82SDonggeun Kim 2448584cb82SDonggeun Kim mutex_lock(&led->mutex); 2458584cb82SDonggeun Kim 2468584cb82SDonggeun Kim if (!strncmp(buf, "FLASH_PIN_CONTROL", 17)) 2478584cb82SDonggeun Kim mode = MAX8997_FLASH_PIN_CONTROL_MODE; 2488584cb82SDonggeun Kim else if (!strncmp(buf, "MOVIE_PIN_CONTROL", 17)) 2498584cb82SDonggeun Kim mode = MAX8997_MOVIE_PIN_CONTROL_MODE; 2508584cb82SDonggeun Kim else if (!strncmp(buf, "FLASH", 5)) 2518584cb82SDonggeun Kim mode = MAX8997_FLASH_MODE; 2528584cb82SDonggeun Kim else if (!strncmp(buf, "MOVIE", 5)) 2538584cb82SDonggeun Kim mode = MAX8997_MOVIE_MODE; 2548584cb82SDonggeun Kim else 2558584cb82SDonggeun Kim mode = MAX8997_NONE; 2568584cb82SDonggeun Kim 2578584cb82SDonggeun Kim max8997_led_set_mode(led, mode); 2588584cb82SDonggeun Kim 2598584cb82SDonggeun Kim mutex_unlock(&led->mutex); 2608584cb82SDonggeun Kim 2618584cb82SDonggeun Kim return size; 2628584cb82SDonggeun Kim } 2638584cb82SDonggeun Kim 2648584cb82SDonggeun Kim static DEVICE_ATTR(mode, 0644, max8997_led_show_mode, max8997_led_store_mode); 2658584cb82SDonggeun Kim 2668584cb82SDonggeun Kim static int __devinit max8997_led_probe(struct platform_device *pdev) 2678584cb82SDonggeun Kim { 2688584cb82SDonggeun Kim struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); 2698584cb82SDonggeun Kim struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); 2708584cb82SDonggeun Kim struct max8997_led *led; 2718584cb82SDonggeun Kim char name[20]; 2728584cb82SDonggeun Kim int ret = 0; 2738584cb82SDonggeun Kim 2748584cb82SDonggeun Kim if (pdata == NULL) { 2758584cb82SDonggeun Kim dev_err(&pdev->dev, "no platform data\n"); 2768584cb82SDonggeun Kim return -ENODEV; 2778584cb82SDonggeun Kim } 2788584cb82SDonggeun Kim 279*7f13bbf7SSachin Kamat led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); 280*7f13bbf7SSachin Kamat if (led == NULL) 281*7f13bbf7SSachin Kamat return -ENOMEM; 2828584cb82SDonggeun Kim 2838584cb82SDonggeun Kim led->id = pdev->id; 2848584cb82SDonggeun Kim snprintf(name, sizeof(name), "max8997-led%d", pdev->id); 2858584cb82SDonggeun Kim 2868584cb82SDonggeun Kim led->cdev.name = name; 2878584cb82SDonggeun Kim led->cdev.brightness_set = max8997_led_brightness_set; 2888584cb82SDonggeun Kim led->cdev.flags |= LED_CORE_SUSPENDRESUME; 2898584cb82SDonggeun Kim led->cdev.brightness = 0; 2908584cb82SDonggeun Kim led->iodev = iodev; 2918584cb82SDonggeun Kim 2928584cb82SDonggeun Kim /* initialize mode and brightness according to platform_data */ 2938584cb82SDonggeun Kim if (pdata->led_pdata) { 2948584cb82SDonggeun Kim u8 mode = 0, brightness = 0; 2958584cb82SDonggeun Kim 2968584cb82SDonggeun Kim mode = pdata->led_pdata->mode[led->id]; 2978584cb82SDonggeun Kim brightness = pdata->led_pdata->brightness[led->id]; 2988584cb82SDonggeun Kim 2998584cb82SDonggeun Kim max8997_led_set_mode(led, pdata->led_pdata->mode[led->id]); 3008584cb82SDonggeun Kim 3018584cb82SDonggeun Kim if (brightness > led->cdev.max_brightness) 3028584cb82SDonggeun Kim brightness = led->cdev.max_brightness; 3038584cb82SDonggeun Kim max8997_led_set_current(led, brightness); 3048584cb82SDonggeun Kim led->cdev.brightness = brightness; 3058584cb82SDonggeun Kim } else { 3068584cb82SDonggeun Kim max8997_led_set_mode(led, MAX8997_NONE); 3078584cb82SDonggeun Kim max8997_led_set_current(led, 0); 3088584cb82SDonggeun Kim } 3098584cb82SDonggeun Kim 3108584cb82SDonggeun Kim mutex_init(&led->mutex); 3118584cb82SDonggeun Kim 3128584cb82SDonggeun Kim platform_set_drvdata(pdev, led); 3138584cb82SDonggeun Kim 3148584cb82SDonggeun Kim ret = led_classdev_register(&pdev->dev, &led->cdev); 3158584cb82SDonggeun Kim if (ret < 0) 316*7f13bbf7SSachin Kamat return ret; 3178584cb82SDonggeun Kim 3188584cb82SDonggeun Kim ret = device_create_file(led->cdev.dev, &dev_attr_mode); 3198584cb82SDonggeun Kim if (ret != 0) { 3208584cb82SDonggeun Kim dev_err(&pdev->dev, 3218584cb82SDonggeun Kim "failed to create file: %d\n", ret); 322*7f13bbf7SSachin Kamat led_classdev_unregister(&led->cdev); 323*7f13bbf7SSachin Kamat return ret; 3248584cb82SDonggeun Kim } 3258584cb82SDonggeun Kim 3268584cb82SDonggeun Kim return 0; 3278584cb82SDonggeun Kim } 3288584cb82SDonggeun Kim 3298584cb82SDonggeun Kim static int __devexit max8997_led_remove(struct platform_device *pdev) 3308584cb82SDonggeun Kim { 3318584cb82SDonggeun Kim struct max8997_led *led = platform_get_drvdata(pdev); 3328584cb82SDonggeun Kim 3338584cb82SDonggeun Kim device_remove_file(led->cdev.dev, &dev_attr_mode); 3348584cb82SDonggeun Kim led_classdev_unregister(&led->cdev); 3358584cb82SDonggeun Kim 3368584cb82SDonggeun Kim return 0; 3378584cb82SDonggeun Kim } 3388584cb82SDonggeun Kim 3398584cb82SDonggeun Kim static struct platform_driver max8997_led_driver = { 3408584cb82SDonggeun Kim .driver = { 3418584cb82SDonggeun Kim .name = "max8997-led", 3428584cb82SDonggeun Kim .owner = THIS_MODULE, 3438584cb82SDonggeun Kim }, 3448584cb82SDonggeun Kim .probe = max8997_led_probe, 3458584cb82SDonggeun Kim .remove = __devexit_p(max8997_led_remove), 3468584cb82SDonggeun Kim }; 3478584cb82SDonggeun Kim 3487fafefb8SSachin Kamat module_platform_driver(max8997_led_driver); 3498584cb82SDonggeun Kim 3508584cb82SDonggeun Kim MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 3518584cb82SDonggeun Kim MODULE_DESCRIPTION("MAX8997 LED driver"); 3528584cb82SDonggeun Kim MODULE_LICENSE("GPL"); 3538584cb82SDonggeun Kim MODULE_ALIAS("platform:max8997-led"); 354