-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
yaml --- r: 284575 b: refs/heads/master c: 8584cb8 h: refs/heads/master i: 284573: d84c122 284571: 4c2d374 284567: ac0abfd 284559: 1ea4a41 284543: 8191fd3 v: v3
- Loading branch information
Donggeun Kim
authored and
Samuel Ortiz
committed
Jan 8, 2012
1 parent
33c02d9
commit 8018543
Showing
4 changed files
with
381 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: f6dd2db940a1a0c6b9f7112109115c8243ba752b | ||
refs/heads/master: 8584cb82f1516042e7390082d27b7c29329e21f4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,372 @@ | ||
/* | ||
* leds-max8997.c - LED class driver for MAX8997 LEDs. | ||
* | ||
* Copyright (C) 2011 Samsung Electronics | ||
* Donggeun Kim <dg77.kim@samsung.com> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
*/ | ||
|
||
#include <linux/module.h> | ||
#include <linux/err.h> | ||
#include <linux/slab.h> | ||
#include <linux/workqueue.h> | ||
#include <linux/leds.h> | ||
#include <linux/mfd/max8997.h> | ||
#include <linux/mfd/max8997-private.h> | ||
#include <linux/platform_device.h> | ||
|
||
#define MAX8997_LED_FLASH_SHIFT 3 | ||
#define MAX8997_LED_FLASH_CUR_MASK 0xf8 | ||
#define MAX8997_LED_MOVIE_SHIFT 4 | ||
#define MAX8997_LED_MOVIE_CUR_MASK 0xf0 | ||
|
||
#define MAX8997_LED_FLASH_MAX_BRIGHTNESS 0x1f | ||
#define MAX8997_LED_MOVIE_MAX_BRIGHTNESS 0xf | ||
#define MAX8997_LED_NONE_MAX_BRIGHTNESS 0 | ||
|
||
#define MAX8997_LED0_FLASH_MASK 0x1 | ||
#define MAX8997_LED0_FLASH_PIN_MASK 0x5 | ||
#define MAX8997_LED0_MOVIE_MASK 0x8 | ||
#define MAX8997_LED0_MOVIE_PIN_MASK 0x28 | ||
|
||
#define MAX8997_LED1_FLASH_MASK 0x2 | ||
#define MAX8997_LED1_FLASH_PIN_MASK 0x6 | ||
#define MAX8997_LED1_MOVIE_MASK 0x10 | ||
#define MAX8997_LED1_MOVIE_PIN_MASK 0x30 | ||
|
||
#define MAX8997_LED_BOOST_ENABLE_MASK (1 << 6) | ||
|
||
struct max8997_led { | ||
struct max8997_dev *iodev; | ||
struct led_classdev cdev; | ||
bool enabled; | ||
int id; | ||
enum max8997_led_mode led_mode; | ||
struct mutex mutex; | ||
}; | ||
|
||
static void max8997_led_clear_mode(struct max8997_led *led, | ||
enum max8997_led_mode mode) | ||
{ | ||
struct i2c_client *client = led->iodev->i2c; | ||
u8 val = 0, mask = 0; | ||
int ret; | ||
|
||
switch (mode) { | ||
case MAX8997_FLASH_MODE: | ||
mask = led->id ? | ||
MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK; | ||
break; | ||
case MAX8997_MOVIE_MODE: | ||
mask = led->id ? | ||
MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK; | ||
break; | ||
case MAX8997_FLASH_PIN_CONTROL_MODE: | ||
mask = led->id ? | ||
MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK; | ||
break; | ||
case MAX8997_MOVIE_PIN_CONTROL_MODE: | ||
mask = led->id ? | ||
MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK; | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
if (mask) { | ||
ret = max8997_update_reg(client, | ||
MAX8997_REG_LEN_CNTL, val, mask); | ||
if (ret) | ||
dev_err(led->iodev->dev, | ||
"failed to update register(%d)\n", ret); | ||
} | ||
} | ||
|
||
static void max8997_led_set_mode(struct max8997_led *led, | ||
enum max8997_led_mode mode) | ||
{ | ||
int ret; | ||
struct i2c_client *client = led->iodev->i2c; | ||
u8 mask = 0; | ||
|
||
/* First, clear the previous mode */ | ||
max8997_led_clear_mode(led, led->led_mode); | ||
|
||
switch (mode) { | ||
case MAX8997_FLASH_MODE: | ||
mask = led->id ? | ||
MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK; | ||
led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS; | ||
break; | ||
case MAX8997_MOVIE_MODE: | ||
mask = led->id ? | ||
MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK; | ||
led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS; | ||
break; | ||
case MAX8997_FLASH_PIN_CONTROL_MODE: | ||
mask = led->id ? | ||
MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK; | ||
led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS; | ||
break; | ||
case MAX8997_MOVIE_PIN_CONTROL_MODE: | ||
mask = led->id ? | ||
MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK; | ||
led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS; | ||
break; | ||
default: | ||
led->cdev.max_brightness = MAX8997_LED_NONE_MAX_BRIGHTNESS; | ||
break; | ||
} | ||
|
||
if (mask) { | ||
ret = max8997_update_reg(client, | ||
MAX8997_REG_LEN_CNTL, mask, mask); | ||
if (ret) | ||
dev_err(led->iodev->dev, | ||
"failed to update register(%d)\n", ret); | ||
} | ||
|
||
led->led_mode = mode; | ||
} | ||
|
||
static void max8997_led_enable(struct max8997_led *led, bool enable) | ||
{ | ||
int ret; | ||
struct i2c_client *client = led->iodev->i2c; | ||
u8 val = 0, mask = MAX8997_LED_BOOST_ENABLE_MASK; | ||
|
||
if (led->enabled == enable) | ||
return; | ||
|
||
val = enable ? MAX8997_LED_BOOST_ENABLE_MASK : 0; | ||
|
||
ret = max8997_update_reg(client, MAX8997_REG_BOOST_CNTL, val, mask); | ||
if (ret) | ||
dev_err(led->iodev->dev, | ||
"failed to update register(%d)\n", ret); | ||
|
||
led->enabled = enable; | ||
} | ||
|
||
static void max8997_led_set_current(struct max8997_led *led, | ||
enum led_brightness value) | ||
{ | ||
int ret; | ||
struct i2c_client *client = led->iodev->i2c; | ||
u8 val = 0, mask = 0, reg = 0; | ||
|
||
switch (led->led_mode) { | ||
case MAX8997_FLASH_MODE: | ||
case MAX8997_FLASH_PIN_CONTROL_MODE: | ||
val = value << MAX8997_LED_FLASH_SHIFT; | ||
mask = MAX8997_LED_FLASH_CUR_MASK; | ||
reg = led->id ? MAX8997_REG_FLASH2_CUR : MAX8997_REG_FLASH1_CUR; | ||
break; | ||
case MAX8997_MOVIE_MODE: | ||
case MAX8997_MOVIE_PIN_CONTROL_MODE: | ||
val = value << MAX8997_LED_MOVIE_SHIFT; | ||
mask = MAX8997_LED_MOVIE_CUR_MASK; | ||
reg = MAX8997_REG_MOVIE_CUR; | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
if (mask) { | ||
ret = max8997_update_reg(client, reg, val, mask); | ||
if (ret) | ||
dev_err(led->iodev->dev, | ||
"failed to update register(%d)\n", ret); | ||
} | ||
} | ||
|
||
static void max8997_led_brightness_set(struct led_classdev *led_cdev, | ||
enum led_brightness value) | ||
{ | ||
struct max8997_led *led = | ||
container_of(led_cdev, struct max8997_led, cdev); | ||
|
||
if (value) { | ||
max8997_led_set_current(led, value); | ||
max8997_led_enable(led, true); | ||
} else { | ||
max8997_led_set_current(led, value); | ||
max8997_led_enable(led, false); | ||
} | ||
} | ||
|
||
static ssize_t max8997_led_show_mode(struct device *dev, | ||
struct device_attribute *attr, char *buf) | ||
{ | ||
struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
struct max8997_led *led = | ||
container_of(led_cdev, struct max8997_led, cdev); | ||
ssize_t ret = 0; | ||
|
||
mutex_lock(&led->mutex); | ||
|
||
switch (led->led_mode) { | ||
case MAX8997_FLASH_MODE: | ||
ret += sprintf(buf, "FLASH\n"); | ||
break; | ||
case MAX8997_MOVIE_MODE: | ||
ret += sprintf(buf, "MOVIE\n"); | ||
break; | ||
case MAX8997_FLASH_PIN_CONTROL_MODE: | ||
ret += sprintf(buf, "FLASH_PIN_CONTROL\n"); | ||
break; | ||
case MAX8997_MOVIE_PIN_CONTROL_MODE: | ||
ret += sprintf(buf, "MOVIE_PIN_CONTROL\n"); | ||
break; | ||
default: | ||
ret += sprintf(buf, "NONE\n"); | ||
break; | ||
} | ||
|
||
mutex_unlock(&led->mutex); | ||
|
||
return ret; | ||
} | ||
|
||
static ssize_t max8997_led_store_mode(struct device *dev, | ||
struct device_attribute *attr, | ||
const char *buf, size_t size) | ||
{ | ||
struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
struct max8997_led *led = | ||
container_of(led_cdev, struct max8997_led, cdev); | ||
enum max8997_led_mode mode; | ||
|
||
mutex_lock(&led->mutex); | ||
|
||
if (!strncmp(buf, "FLASH_PIN_CONTROL", 17)) | ||
mode = MAX8997_FLASH_PIN_CONTROL_MODE; | ||
else if (!strncmp(buf, "MOVIE_PIN_CONTROL", 17)) | ||
mode = MAX8997_MOVIE_PIN_CONTROL_MODE; | ||
else if (!strncmp(buf, "FLASH", 5)) | ||
mode = MAX8997_FLASH_MODE; | ||
else if (!strncmp(buf, "MOVIE", 5)) | ||
mode = MAX8997_MOVIE_MODE; | ||
else | ||
mode = MAX8997_NONE; | ||
|
||
max8997_led_set_mode(led, mode); | ||
|
||
mutex_unlock(&led->mutex); | ||
|
||
return size; | ||
} | ||
|
||
static DEVICE_ATTR(mode, 0644, max8997_led_show_mode, max8997_led_store_mode); | ||
|
||
static int __devinit max8997_led_probe(struct platform_device *pdev) | ||
{ | ||
struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); | ||
struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); | ||
struct max8997_led *led; | ||
char name[20]; | ||
int ret = 0; | ||
|
||
if (pdata == NULL) { | ||
dev_err(&pdev->dev, "no platform data\n"); | ||
return -ENODEV; | ||
} | ||
|
||
led = kzalloc(sizeof(*led), GFP_KERNEL); | ||
if (led == NULL) { | ||
ret = -ENOMEM; | ||
goto err_mem; | ||
} | ||
|
||
led->id = pdev->id; | ||
snprintf(name, sizeof(name), "max8997-led%d", pdev->id); | ||
|
||
led->cdev.name = name; | ||
led->cdev.brightness_set = max8997_led_brightness_set; | ||
led->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
led->cdev.brightness = 0; | ||
led->iodev = iodev; | ||
|
||
/* initialize mode and brightness according to platform_data */ | ||
if (pdata->led_pdata) { | ||
u8 mode = 0, brightness = 0; | ||
|
||
mode = pdata->led_pdata->mode[led->id]; | ||
brightness = pdata->led_pdata->brightness[led->id]; | ||
|
||
max8997_led_set_mode(led, pdata->led_pdata->mode[led->id]); | ||
|
||
if (brightness > led->cdev.max_brightness) | ||
brightness = led->cdev.max_brightness; | ||
max8997_led_set_current(led, brightness); | ||
led->cdev.brightness = brightness; | ||
} else { | ||
max8997_led_set_mode(led, MAX8997_NONE); | ||
max8997_led_set_current(led, 0); | ||
} | ||
|
||
mutex_init(&led->mutex); | ||
|
||
platform_set_drvdata(pdev, led); | ||
|
||
ret = led_classdev_register(&pdev->dev, &led->cdev); | ||
if (ret < 0) | ||
goto err_led; | ||
|
||
ret = device_create_file(led->cdev.dev, &dev_attr_mode); | ||
if (ret != 0) { | ||
dev_err(&pdev->dev, | ||
"failed to create file: %d\n", ret); | ||
goto err_file; | ||
} | ||
|
||
return 0; | ||
|
||
err_file: | ||
led_classdev_unregister(&led->cdev); | ||
err_led: | ||
kfree(led); | ||
err_mem: | ||
return ret; | ||
} | ||
|
||
static int __devexit max8997_led_remove(struct platform_device *pdev) | ||
{ | ||
struct max8997_led *led = platform_get_drvdata(pdev); | ||
|
||
device_remove_file(led->cdev.dev, &dev_attr_mode); | ||
led_classdev_unregister(&led->cdev); | ||
kfree(led); | ||
|
||
return 0; | ||
} | ||
|
||
static struct platform_driver max8997_led_driver = { | ||
.driver = { | ||
.name = "max8997-led", | ||
.owner = THIS_MODULE, | ||
}, | ||
.probe = max8997_led_probe, | ||
.remove = __devexit_p(max8997_led_remove), | ||
}; | ||
|
||
static int __init max8997_led_init(void) | ||
{ | ||
return platform_driver_register(&max8997_led_driver); | ||
} | ||
module_init(max8997_led_init); | ||
|
||
static void __exit max8997_led_exit(void) | ||
{ | ||
platform_driver_unregister(&max8997_led_driver); | ||
} | ||
module_exit(max8997_led_exit); | ||
|
||
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); | ||
MODULE_DESCRIPTION("MAX8997 LED driver"); | ||
MODULE_LICENSE("GPL"); | ||
MODULE_ALIAS("platform:max8997-led"); |