123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- /*
- * Simple driver for Texas Instruments LM3639 Backlight + Flash LED driver chip
- * Copyright (C) 2012 Texas Instruments
- *
- * 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/slab.h>
- #include <linux/i2c.h>
- #include <linux/leds.h>
- #include <linux/backlight.h>
- #include <linux/err.h>
- #include <linux/delay.h>
- #include <linux/uaccess.h>
- #include <linux/interrupt.h>
- #include <linux/regmap.h>
- #include <linux/platform_data/lm3639_bl.h>
- #define REG_DEV_ID 0x00
- #define REG_CHECKSUM 0x01
- #define REG_BL_CONF_1 0x02
- #define REG_BL_CONF_2 0x03
- #define REG_BL_CONF_3 0x04
- #define REG_BL_CONF_4 0x05
- #define REG_FL_CONF_1 0x06
- #define REG_FL_CONF_2 0x07
- #define REG_FL_CONF_3 0x08
- #define REG_IO_CTRL 0x09
- #define REG_ENABLE 0x0A
- #define REG_FLAG 0x0B
- #define REG_MAX REG_FLAG
- struct lm3639_chip_data {
- struct device *dev;
- struct lm3639_platform_data *pdata;
- struct backlight_device *bled;
- struct led_classdev cdev_flash;
- struct led_classdev cdev_torch;
- struct regmap *regmap;
- unsigned int bled_mode;
- unsigned int bled_map;
- unsigned int last_flag;
- };
- /* initialize chip */
- static int lm3639_chip_init(struct lm3639_chip_data *pchip)
- {
- int ret;
- unsigned int reg_val;
- struct lm3639_platform_data *pdata = pchip->pdata;
- /* input pins config. */
- ret =
- regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x08,
- pdata->pin_pwm);
- if (ret < 0)
- goto out;
- reg_val = (pdata->pin_pwm & 0x40) | pdata->pin_strobe | pdata->pin_tx;
- ret = regmap_update_bits(pchip->regmap, REG_IO_CTRL, 0x7C, reg_val);
- if (ret < 0)
- goto out;
- /* init brightness */
- ret = regmap_write(pchip->regmap, REG_BL_CONF_4, pdata->init_brt_led);
- if (ret < 0)
- goto out;
- ret = regmap_write(pchip->regmap, REG_BL_CONF_3, pdata->init_brt_led);
- if (ret < 0)
- goto out;
- /* output pins config. */
- if (!pdata->init_brt_led) {
- reg_val = pdata->fled_pins;
- reg_val |= pdata->bled_pins;
- } else {
- reg_val = pdata->fled_pins;
- reg_val |= pdata->bled_pins | 0x01;
- }
- ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x79, reg_val);
- if (ret < 0)
- goto out;
- return ret;
- out:
- dev_err(pchip->dev, "i2c failed to access register\n");
- return ret;
- }
- /* update and get brightness */
- static int lm3639_bled_update_status(struct backlight_device *bl)
- {
- int ret;
- unsigned int reg_val;
- struct lm3639_chip_data *pchip = bl_get_data(bl);
- struct lm3639_platform_data *pdata = pchip->pdata;
- ret = regmap_read(pchip->regmap, REG_FLAG, ®_val);
- if (ret < 0)
- goto out;
- if (reg_val != 0)
- dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
- /* pwm control */
- if (pdata->pin_pwm) {
- if (pdata->pwm_set_intensity)
- pdata->pwm_set_intensity(bl->props.brightness,
- pdata->max_brt_led);
- else
- dev_err(pchip->dev,
- "No pwm control func. in plat-data\n");
- return bl->props.brightness;
- }
- /* i2c control and set brigtness */
- ret = regmap_write(pchip->regmap, REG_BL_CONF_4, bl->props.brightness);
- if (ret < 0)
- goto out;
- ret = regmap_write(pchip->regmap, REG_BL_CONF_3, bl->props.brightness);
- if (ret < 0)
- goto out;
- if (!bl->props.brightness)
- ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x00);
- else
- ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x01);
- if (ret < 0)
- goto out;
- return bl->props.brightness;
- out:
- dev_err(pchip->dev, "i2c failed to access registers\n");
- return bl->props.brightness;
- }
- static int lm3639_bled_get_brightness(struct backlight_device *bl)
- {
- int ret;
- unsigned int reg_val;
- struct lm3639_chip_data *pchip = bl_get_data(bl);
- struct lm3639_platform_data *pdata = pchip->pdata;
- if (pdata->pin_pwm) {
- if (pdata->pwm_get_intensity)
- bl->props.brightness = pdata->pwm_get_intensity();
- else
- dev_err(pchip->dev,
- "No pwm control func. in plat-data\n");
- return bl->props.brightness;
- }
- ret = regmap_read(pchip->regmap, REG_BL_CONF_1, ®_val);
- if (ret < 0)
- goto out;
- if (reg_val & 0x10)
- ret = regmap_read(pchip->regmap, REG_BL_CONF_4, ®_val);
- else
- ret = regmap_read(pchip->regmap, REG_BL_CONF_3, ®_val);
- if (ret < 0)
- goto out;
- bl->props.brightness = reg_val;
- return bl->props.brightness;
- out:
- dev_err(pchip->dev, "i2c failed to access register\n");
- return bl->props.brightness;
- }
- static const struct backlight_ops lm3639_bled_ops = {
- .options = BL_CORE_SUSPENDRESUME,
- .update_status = lm3639_bled_update_status,
- .get_brightness = lm3639_bled_get_brightness,
- };
- /* backlight mapping mode */
- static ssize_t lm3639_bled_mode_store(struct device *dev,
- struct device_attribute *devAttr,
- const char *buf, size_t size)
- {
- ssize_t ret;
- struct lm3639_chip_data *pchip = dev_get_drvdata(dev);
- unsigned int state;
- ret = kstrtouint(buf, 10, &state);
- if (ret)
- goto out_input;
- if (!state)
- ret =
- regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,
- 0x00);
- else
- ret =
- regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,
- 0x10);
- if (ret < 0)
- goto out;
- return size;
- out:
- dev_err(pchip->dev, "%s:i2c access fail to register\n", __func__);
- return ret;
- out_input:
- dev_err(pchip->dev, "%s:input conversion fail\n", __func__);
- return ret;
- }
- static DEVICE_ATTR(bled_mode, S_IWUSR, NULL, lm3639_bled_mode_store);
- /* torch */
- static void lm3639_torch_brightness_set(struct led_classdev *cdev,
- enum led_brightness brightness)
- {
- int ret;
- unsigned int reg_val;
- struct lm3639_chip_data *pchip;
- pchip = container_of(cdev, struct lm3639_chip_data, cdev_torch);
- ret = regmap_read(pchip->regmap, REG_FLAG, ®_val);
- if (ret < 0)
- goto out;
- if (reg_val != 0)
- dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
- /* brightness 0 means off state */
- if (!brightness) {
- ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
- if (ret < 0)
- goto out;
- return;
- }
- ret = regmap_update_bits(pchip->regmap,
- REG_FL_CONF_1, 0x70, (brightness - 1) << 4);
- if (ret < 0)
- goto out;
- ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x02);
- if (ret < 0)
- goto out;
- return;
- out:
- dev_err(pchip->dev, "i2c failed to access register\n");
- }
- /* flash */
- static void lm3639_flash_brightness_set(struct led_classdev *cdev,
- enum led_brightness brightness)
- {
- int ret;
- unsigned int reg_val;
- struct lm3639_chip_data *pchip;
- pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash);
- ret = regmap_read(pchip->regmap, REG_FLAG, ®_val);
- if (ret < 0)
- goto out;
- if (reg_val != 0)
- dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
- /* torch off before flash control */
- ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
- if (ret < 0)
- goto out;
- /* brightness 0 means off state */
- if (!brightness)
- return;
- ret = regmap_update_bits(pchip->regmap,
- REG_FL_CONF_1, 0x0F, brightness - 1);
- if (ret < 0)
- goto out;
- ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06);
- if (ret < 0)
- goto out;
- return;
- out:
- dev_err(pchip->dev, "i2c failed to access register\n");
- }
- static const struct regmap_config lm3639_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = REG_MAX,
- };
- static int lm3639_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- int ret;
- struct lm3639_chip_data *pchip;
- struct lm3639_platform_data *pdata = dev_get_platdata(&client->dev);
- struct backlight_properties props;
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- dev_err(&client->dev, "i2c functionality check fail.\n");
- return -EOPNOTSUPP;
- }
- if (pdata == NULL) {
- dev_err(&client->dev, "Needs Platform Data.\n");
- return -ENODATA;
- }
- pchip = devm_kzalloc(&client->dev,
- sizeof(struct lm3639_chip_data), GFP_KERNEL);
- if (!pchip)
- return -ENOMEM;
- pchip->pdata = pdata;
- pchip->dev = &client->dev;
- pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap);
- if (IS_ERR(pchip->regmap)) {
- ret = PTR_ERR(pchip->regmap);
- dev_err(&client->dev, "fail : allocate register map: %d\n",
- ret);
- return ret;
- }
- i2c_set_clientdata(client, pchip);
- /* chip initialize */
- ret = lm3639_chip_init(pchip);
- if (ret < 0) {
- dev_err(&client->dev, "fail : chip init\n");
- goto err_out;
- }
- /* backlight */
- props.type = BACKLIGHT_RAW;
- props.brightness = pdata->init_brt_led;
- props.max_brightness = pdata->max_brt_led;
- pchip->bled =
- devm_backlight_device_register(pchip->dev, "lm3639_bled",
- pchip->dev, pchip, &lm3639_bled_ops,
- &props);
- if (IS_ERR(pchip->bled)) {
- dev_err(&client->dev, "fail : backlight register\n");
- ret = PTR_ERR(pchip->bled);
- goto err_out;
- }
- ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode);
- if (ret < 0) {
- dev_err(&client->dev, "failed : add sysfs entries\n");
- goto err_out;
- }
- /* flash */
- pchip->cdev_flash.name = "lm3639_flash";
- pchip->cdev_flash.max_brightness = 16;
- pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set;
- ret = led_classdev_register((struct device *)
- &client->dev, &pchip->cdev_flash);
- if (ret < 0) {
- dev_err(&client->dev, "fail : flash register\n");
- goto err_flash;
- }
- /* torch */
- pchip->cdev_torch.name = "lm3639_torch";
- pchip->cdev_torch.max_brightness = 8;
- pchip->cdev_torch.brightness_set = lm3639_torch_brightness_set;
- ret = led_classdev_register((struct device *)
- &client->dev, &pchip->cdev_torch);
- if (ret < 0) {
- dev_err(&client->dev, "fail : torch register\n");
- goto err_torch;
- }
- return 0;
- err_torch:
- led_classdev_unregister(&pchip->cdev_flash);
- err_flash:
- device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
- err_out:
- return ret;
- }
- static int lm3639_remove(struct i2c_client *client)
- {
- struct lm3639_chip_data *pchip = i2c_get_clientdata(client);
- regmap_write(pchip->regmap, REG_ENABLE, 0x00);
- if (&pchip->cdev_torch)
- led_classdev_unregister(&pchip->cdev_torch);
- if (&pchip->cdev_flash)
- led_classdev_unregister(&pchip->cdev_flash);
- if (pchip->bled)
- device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
- return 0;
- }
- static const struct i2c_device_id lm3639_id[] = {
- {LM3639_NAME, 0},
- {}
- };
- MODULE_DEVICE_TABLE(i2c, lm3639_id);
- static struct i2c_driver lm3639_i2c_driver = {
- .driver = {
- .name = LM3639_NAME,
- },
- .probe = lm3639_probe,
- .remove = lm3639_remove,
- .id_table = lm3639_id,
- };
- module_i2c_driver(lm3639_i2c_driver);
- MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639");
- MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
- MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
- MODULE_LICENSE("GPL v2");
|