blob: 494d10a8eb1f283ca045ce5e2d3f1d62f5534c23 [file] [log] [blame]
/*
* drivers/leds/leds-lm3644.c
* General device driver for TI LM3644, FLASH LED Driver
*
* Copyright (C) 2014 Texas Instruments
*
* Contact: Daniel Jeong <gshark.jeong@gmail.com>
* Ldd-Mlp <ldd-mlp@list.ti.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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/regmap.h>
#include <linux/workqueue.h>
#include <linux/platform_data/leds-lm3644.h>
#include <linux/of_gpio.h>
#include "msm_cci.h"
#include "msm_camera_i2c.h"
#include "msm_camera_regmap_i2c.h"
#include "msm_camera_dt_util.h"
#include "detect/fp_cam_detect.h"
#include <asm/stacktrace.h>
struct msm_camera_i2c_reg_conf;
/* registers definitions */
#define REG_ENABLE 0x01
#define REG_FLASH_LED0_BR 0x03
#define REG_FLASH_LED1_BR 0x04
#define REG_TORCH_LED0_BR 0x05
#define REG_TORCH_LED1_BR 0x06
#define REG_FLASH_TOUT 0x08
#define REG_FLAG0 0x0a
#define REG_FLAG1 0x0b
#define LM3644_MODE_BITS 0x0c
#define LM3644_STANDBY_MODE (MODE_STDBY << 2)
#define LM3644_TORCH_MODE (MODE_TORCH << 2)
#define LM3644_STROBE_FLASH_MODE LM3644_STANDBY_MODE
enum lm3644_devid {
ID_FLASH0 = 0x0,
ID_FLASH1,
ID_TORCH0,
ID_TORCH1,
ID_TORCH_DUAL,
ID_MAX
};
enum lm3644_mode {
MODE_STDBY = 0x0,
MODE_IR,
MODE_TORCH,
MODE_FLASH,
MODE_MAX
};
enum lm3644_devfile {
DFILE_FLASH0_ENABLE = 0,
DFILE_FLASH0_ONOFF,
DFILE_FLASH0_SOURCE,
DFILE_FLASH0_TIMEOUT,
DFILE_FLASH1_ENABLE,
DFILE_FLASH1_ONOFF,
DFILE_TORCH0_ENABLE,
DFILE_TORCH0_ONOFF,
DFILE_TORCH0_SOURCE,
DFILE_TORCH1_ENABLE,
DFILE_TORCH1_ONOFF,
DFILE_MAX
};
static struct workqueue_struct *lm3644_wq;
#undef CDBG
#if 0
#define CDBG(fmt, args...) pr_err(fmt, ##args)
#else
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
#endif
struct lm3644 {
/* put i2c_client at beginning of struct so our
regmap implementation can find it */
struct msm_camera_i2c_client i2c_client;
uint32_t i2c_slaveaddr;
uint32_t cci_master;
struct device *dev;
u8 brightness[ID_MAX];
struct work_struct work[ID_MAX];
struct led_classdev cdev[ID_MAX];
struct lm3644_platform_data *pdata;
struct regmap *regmap;
struct mutex lock;
unsigned int torch_current_led0;
unsigned int torch_current_led1;
};
enum lm3644_cmd_id {
CMD_ENABLE = 0,
CMD_DISABLE,
CMD_ON,
CMD_OFF,
CMD_IRMODE,
CMD_OVERRIDE,
CMD_MAX
};
/* device files to control registers */
struct lm3644_commands {
char *str;
int size;
};
struct lm3644_commands cmds[CMD_MAX] = {
[CMD_ENABLE] = {"enable", 6},
[CMD_DISABLE] = {"disable", 7},
[CMD_ON] = {"on", 2},
[CMD_OFF] = {"off", 3},
[CMD_IRMODE] = {"irmode", 6},
[CMD_OVERRIDE] = {"override", 8},
};
static struct lm3644 *_pchip;
static size_t lm3644_ctrl(struct device *dev,
const char *buf, enum lm3644_devid id,
enum lm3644_devfile dfid, size_t size);
static void lm3644_set_brightness(struct lm3644 *pchip,
unsigned int reg, unsigned int brightness)
{
if (regmap_update_bits(pchip->regmap, reg, 0xff, brightness))
dev_err(pchip->dev, "I2C access fail (reg: 0x%x)\n", reg);
}
static void lm3644_power_up(struct lm3644 *pchip)
{
msm_sensor_cci_i2c_util(&pchip->i2c_client, MSM_CCI_INIT);
}
static void lm3644_power_down(struct lm3644 *pchip)
{
msm_sensor_cci_i2c_util(&pchip->i2c_client, MSM_CCI_RELEASE);
}
void lm3644_deinit(void) {
struct lm3644 *pchip = _pchip;
if (!_pchip) {
return;
}
lm3644_power_down(pchip);
}
void lm3644_init(void)
{
struct lm3644 *pchip = _pchip;
if (!_pchip) {
return;
}
lm3644_power_up(pchip);
/* Disable both LEDs */
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x3, 0x0);
/* Set brightness to lowest values */
lm3644_set_brightness(pchip, REG_FLASH_LED0_BR, 0);
lm3644_set_brightness(pchip, REG_FLASH_LED1_BR, 0);
lm3644_set_brightness(pchip, REG_TORCH_LED0_BR, 0);
lm3644_set_brightness(pchip, REG_TORCH_LED1_BR, 0);
}
static void lm3644_read_flag(struct lm3644 *pchip)
{
int rval;
unsigned int flag0, flag1;
rval = regmap_read(pchip->regmap, REG_FLAG0, &flag0);
rval |= regmap_read(pchip->regmap, REG_FLAG1, &flag1);
if (rval < 0)
dev_err(pchip->dev, "i2c access fail (rval=%d).\n", rval) ;
dev_info(pchip->dev, "[flag1] 0x%x, [flag0] 0x%x\n",
flag1 & 0x1f, flag0);
}
static void lm3644_torch_enable_disable(struct lm3644 *pchip, int flash_id)
{
unsigned enabled = (pchip->brightness[flash_id] > 0);
unsigned flash_enable_bit = flash_id - 1;
/* enable torch mode */
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x0c, enabled?0x08:0);
/* flash0 or flash1 enable */
regmap_update_bits(pchip->regmap, REG_ENABLE, flash_enable_bit,
enabled?flash_enable_bit:0);
}
/* torch0 brightness control */
static void lm3644_deferred_torch0_brightness_set(struct work_struct *work)
{
struct lm3644 *pchip = container_of(work, struct lm3644, work[ID_TORCH0]);
if (regmap_update_bits(pchip->regmap, REG_TORCH_LED0_BR, 0x7f,
pchip->brightness[ID_TORCH0]))
dev_err(pchip->dev, "i2c access fail.\n");
lm3644_torch_enable_disable(pchip, ID_TORCH0);
lm3644_read_flag(pchip);
}
static void lm3644_torch0_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm3644 *pchip =
container_of(cdev, struct lm3644, cdev[ID_TORCH0]);
pchip->brightness[ID_TORCH0] = brightness;
queue_work(lm3644_wq, &pchip->work[ID_TORCH0]);
}
/* torch1 brightness control */
static void lm3644_deferred_torch1_brightness_set(struct work_struct *work)
{
struct lm3644 *pchip = container_of(work, struct lm3644, work[ID_TORCH1]);
if (regmap_update_bits(pchip->regmap, REG_TORCH_LED1_BR, 0x7f,
pchip->brightness[ID_TORCH1]))
dev_err(pchip->dev, "i2c access fail.\n");
lm3644_torch_enable_disable(pchip, ID_TORCH1);
lm3644_read_flag(pchip);
}
static void lm3644_torch1_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm3644 *pchip =
container_of(cdev, struct lm3644, cdev[ID_TORCH1]);
pchip->brightness[ID_TORCH1] = brightness;
queue_work(lm3644_wq, &pchip->work[ID_TORCH1]);
}
/*
* Dual LED enabled torch
*/
static void lm3644_torch_dual_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm3644 *pchip =
container_of(cdev, struct lm3644, cdev[ID_TORCH_DUAL]);
/* Only indicate LEDs are on to keep chip from being disabled.
* Values from device tree are used for brightness. */
pchip->brightness[ID_TORCH_DUAL] = brightness > 0 ? 1 : 0;
queue_work(lm3644_wq, &pchip->work[ID_TORCH_DUAL]);
}
static void lm3644_torch_dual_enable_disable(struct lm3644 *pchip)
{
unsigned enabled = (pchip->brightness[ID_TORCH_DUAL] > 0);
unsigned flash_enable_bits = (ID_TORCH0 - 1) | (ID_TORCH1 - 1);
/* enable torch mode */
regmap_update_bits(pchip->regmap, REG_ENABLE, LM3644_MODE_BITS,
enabled ? LM3644_TORCH_MODE : LM3644_STANDBY_MODE);
/* flash0 and flash1 enable */
regmap_update_bits(pchip->regmap, REG_ENABLE, flash_enable_bits,
enabled ? flash_enable_bits : 0);
}
static void lm3644_deferred_torch_dual_brightness_set(struct work_struct *work)
{
struct lm3644 *pchip = container_of(work, struct lm3644, work[ID_TORCH_DUAL]);
unsigned int enabled = pchip->brightness[ID_TORCH_DUAL] > 0;
lm3644_set_brightness(pchip, REG_TORCH_LED0_BR,
enabled ? pchip->torch_current_led0 : 0);
lm3644_set_brightness(pchip, REG_TORCH_LED1_BR,
enabled ? pchip->torch_current_led1 : 0);
lm3644_torch_dual_enable_disable(pchip);
lm3644_read_flag(pchip);
}
/*
* Flash
*/
static void lm3644_flash_enable_disable(struct lm3644 *pchip, int flash_id)
{
unsigned enabled = (pchip->brightness[flash_id] > 0);
unsigned flash_enable_bit = flash_id + 1;
/* increase flash time-out slightly */
regmap_update_bits(pchip->regmap, REG_FLASH_TOUT, 0xf, 0xf); /* 0xf -> 400ms */
/* flash strobe (we can always set this one)*/
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x20, 0x20);
/* standby mode */
regmap_update_bits(pchip->regmap, REG_ENABLE, LM3644_MODE_BITS,
LM3644_STANDBY_MODE);
/* enable flash mode */
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x0c, 0);
/* flash0 or flash1 enable */
regmap_update_bits(pchip->regmap, REG_ENABLE, flash_enable_bit,
enabled?flash_enable_bit:0);
}
/* flash0 brightness control */
static void lm3644_deferred_flash0_brightness_set(struct work_struct *work)
{
struct lm3644 *pchip = container_of(work, struct lm3644, work[ID_FLASH0]);
if (regmap_update_bits(pchip->regmap,
REG_FLASH_LED0_BR, 0xff,
pchip->brightness[ID_FLASH0]))
dev_err(pchip->dev, "i2c access fail.\n");
lm3644_flash_enable_disable(pchip, ID_FLASH0);
lm3644_read_flag(pchip);
}
static void lm3644_flash0_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm3644 *pchip =
container_of(cdev, struct lm3644, cdev[ID_FLASH0]);
pchip->brightness[ID_FLASH0] = brightness;
queue_work(lm3644_wq, &pchip->work[ID_FLASH0]);
}
/* flash1 brightness control */
static void lm3644_deferred_flash1_brightness_set(struct work_struct *work)
{
struct lm3644 *pchip = container_of(work, struct lm3644, work[ID_FLASH1]);
if (regmap_update_bits(pchip->regmap,
REG_FLASH_LED1_BR, 0x7f,
pchip->brightness[ID_FLASH1]))
dev_err(pchip->dev, "i2c access fail.\n");
lm3644_flash_enable_disable(pchip, ID_FLASH1);
lm3644_read_flag(pchip);
}
static void lm3644_flash1_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm3644 *pchip =
container_of(cdev, struct lm3644, cdev[ID_FLASH1]);
pchip->brightness[ID_FLASH1] = brightness;
queue_work(lm3644_wq, &pchip->work[ID_FLASH1]);
}
struct lm3644_devices {
struct led_classdev cdev;
work_func_t func;
};
static struct lm3644_devices lm3644_leds[ID_MAX] = {
[ID_FLASH0] = {
.cdev.name = "flash0",
.cdev.brightness = 0,
.cdev.max_brightness = 0x7f,
.cdev.brightness_set = lm3644_flash0_brightness_set,
.cdev.default_trigger = "flash0",
.func = lm3644_deferred_flash0_brightness_set},
[ID_FLASH1] = {
.cdev.name = "flash1",
.cdev.brightness = 0,
.cdev.max_brightness = 0x7f,
.cdev.brightness_set = lm3644_flash1_brightness_set,
.cdev.default_trigger = "flash1",
.func = lm3644_deferred_flash1_brightness_set},
[ID_TORCH0] = {
.cdev.name = "torch0",
.cdev.brightness = 0,
.cdev.max_brightness = 0x7f,
.cdev.brightness_set = lm3644_torch0_brightness_set,
.cdev.default_trigger = "torch0",
.func = lm3644_deferred_torch0_brightness_set},
[ID_TORCH1] = {
.cdev.name = "torch1",
.cdev.brightness = 0,
.cdev.max_brightness = 0x7f,
.cdev.brightness_set = lm3644_torch1_brightness_set,
.cdev.default_trigger = "torch0",
.func = lm3644_deferred_torch1_brightness_set},
[ID_TORCH_DUAL] = {
.cdev.name = "torch_dual",
.cdev.brightness = 0,
.cdev.max_brightness = 0x7f,
.cdev.brightness_set = lm3644_torch_dual_brightness_set,
.cdev.default_trigger = "torch_dual",
.func = lm3644_deferred_torch_dual_brightness_set},
};
static void lm3644_led_unregister(struct lm3644 *pchip, enum lm3644_devid id)
{
int icnt;
for (icnt = id; icnt > 0; icnt--)
led_classdev_unregister(&pchip->cdev[icnt - 1]);
}
static int lm3644_led_register(struct lm3644 *pchip)
{
int icnt, rval;
for (icnt = 0; icnt < ID_MAX; icnt++) {
INIT_WORK(&pchip->work[icnt], lm3644_leds[icnt].func);
pchip->cdev[icnt].name = lm3644_leds[icnt].cdev.name;
pchip->cdev[icnt].max_brightness =
lm3644_leds[icnt].cdev.max_brightness;
pchip->cdev[icnt].brightness =
lm3644_leds[icnt].cdev.brightness;
pchip->cdev[icnt].brightness_set =
lm3644_leds[icnt].cdev.brightness_set;
pchip->cdev[icnt].default_trigger =
lm3644_leds[icnt].cdev.default_trigger;
rval = led_classdev_register((struct device *)
pchip->dev, &pchip->cdev[icnt]);
if (rval < 0) {
lm3644_led_unregister(pchip, icnt);
return rval;
}
}
return 0;
}
struct lm3644_files {
enum lm3644_devid id;
struct device_attribute attr;
};
static size_t lm3644_ctrl(struct device *dev,
const char *buf, enum lm3644_devid id,
enum lm3644_devfile dfid, size_t size)
{
struct lm3644 *pchip;
enum lm3644_cmd_id icnt;
int tout, rval;
if (!dev) {
pr_err("%s: Illegal argument: dev must not be null.\n", __func__);
return -EINVAL;
}
if (!dev->parent) {
pr_err("%s: Missing parent device.\n", __func__);
return -ENXIO;
}
pchip = dev_get_drvdata(dev->parent);
mutex_lock(&pchip->lock);
for (icnt = 0; icnt < CMD_MAX; icnt++) {
if (strncmp(buf, cmds[icnt].str, cmds[icnt].size) == 0)
break;
}
switch (dfid) {
/* led 0 enable */
case DFILE_FLASH0_ENABLE:
case DFILE_TORCH0_ENABLE:
if (icnt == CMD_ENABLE)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x1,
0x1);
else if (icnt == CMD_DISABLE)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x1,
0x0);
break;
/* led 1 enable, flash override */
case DFILE_FLASH1_ENABLE:
if (icnt == CMD_ENABLE) {
rval = regmap_update_bits(pchip->regmap,
REG_FLASH_LED0_BR, 0x80, 0x0);
rval |=
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x2,
0x2);
} else if (icnt == CMD_DISABLE) {
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x2,
0x0);
} else if (icnt == CMD_OVERRIDE) {
rval = regmap_update_bits(pchip->regmap,
REG_FLASH_LED0_BR, 0x80,
0x80);
rval |=
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x2,
0x2);
}
break;
/* led 1 enable, torch override */
case DFILE_TORCH1_ENABLE:
if (icnt == CMD_ENABLE) {
rval = regmap_update_bits(pchip->regmap,
REG_TORCH_LED0_BR, 0x80, 0x0);
rval |=
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x2,
0x2);
} else if (icnt == CMD_DISABLE) {
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x2,
0x0);
} else if (icnt == CMD_OVERRIDE) {
rval = regmap_update_bits(pchip->regmap,
REG_TORCH_LED0_BR, 0x80,
0x80);
rval |=
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x2,
0x2);
}
break;
/* mode control flash/ir */
case DFILE_FLASH0_ONOFF:
case DFILE_FLASH1_ONOFF:
if (icnt == CMD_ON)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0xc,
0xc);
else if (icnt == CMD_OFF)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0xc,
0x0);
else if (icnt == CMD_IRMODE)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0xc,
0x4);
break;
/* mode control torch */
case DFILE_TORCH0_ONOFF:
case DFILE_TORCH1_ONOFF:
if (icnt == CMD_ON)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0xc,
0x8);
else if (icnt == CMD_OFF)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0xc,
0x0);
break;
/* strobe pin control */
case DFILE_FLASH0_SOURCE:
if (icnt == CMD_ON)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x20,
0x20);
else if (icnt == CMD_OFF)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x20,
0x0);
break;
case DFILE_TORCH0_SOURCE:
if (icnt == CMD_ON)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x10,
0x10);
else if (icnt == CMD_OFF)
rval =
regmap_update_bits(pchip->regmap, REG_ENABLE, 0x10,
0x0);
break;
/* flash time out */
case DFILE_FLASH0_TIMEOUT:
rval = kstrtouint((const char *)buf, 10, &tout);
if (rval < 0)
break;
rval = regmap_update_bits(pchip->regmap,
REG_FLASH_TOUT, 0x0f, tout);
break;
default:
dev_err(pchip->dev, "error : undefined dev file\n");
break;
}
lm3644_read_flag(pchip);
mutex_unlock(&pchip->lock);
return size;
}
/* flash enable control */
static ssize_t lm3644_flash0_enable_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_FLASH0, DFILE_FLASH0_ENABLE, size);
}
static ssize_t lm3644_flash1_enable_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_FLASH1, DFILE_FLASH1_ENABLE, size);
}
/* flash onoff control */
static ssize_t lm3644_flash0_onoff_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_FLASH0, DFILE_FLASH0_ONOFF, size);
}
static ssize_t lm3644_flash1_onoff_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_FLASH1, DFILE_FLASH1_ONOFF, size);
}
/* flash timeout control */
static ssize_t lm3644_flash0_timeout_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_FLASH0, DFILE_FLASH0_TIMEOUT, size);
}
/* flash source control */
static ssize_t lm3644_flash0_source_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_FLASH0, DFILE_FLASH0_SOURCE, size);
}
/* torch enable control */
static ssize_t lm3644_torch0_enable_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_FLASH0, DFILE_TORCH0_ENABLE, size);
}
static ssize_t lm3644_torch1_enable_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_TORCH1, DFILE_TORCH1_ENABLE, size);
}
/* torch onoff control */
static ssize_t lm3644_torch0_onoff_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_TORCH0, DFILE_TORCH0_ONOFF, size);
}
static ssize_t lm3644_torch1_onoff_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_TORCH1, DFILE_TORCH1_ONOFF, size);
}
/* torch source control */
static ssize_t lm3644_torch0_source_store(struct device *dev,
struct device_attribute *devAttr,
const char *buf, size_t size)
{
return lm3644_ctrl(dev, buf, ID_TORCH0, DFILE_TORCH0_SOURCE, size);
}
#define lm3644_attr(_name, _show, _store)\
{\
.attr = {\
.name = _name,\
.mode = 0644,\
},\
.show = _show,\
.store = _store,\
}
static struct lm3644_files lm3644_devfiles[DFILE_MAX] = {
[DFILE_FLASH0_ENABLE] = {
.id = ID_FLASH0,
.attr =
lm3644_attr("enable", NULL,
lm3644_flash0_enable_store),
},
[DFILE_FLASH0_ONOFF] = {
.id = ID_FLASH0,
.attr =
lm3644_attr("onoff", NULL,
lm3644_flash0_onoff_store),
},
[DFILE_FLASH0_SOURCE] = {
.id = ID_FLASH0,
.attr =
lm3644_attr("source", NULL,
lm3644_flash0_source_store),
},
[DFILE_FLASH0_TIMEOUT] = {
.id = ID_FLASH0,
.attr =
lm3644_attr("timeout", NULL,
lm3644_flash0_timeout_store),
},
[DFILE_FLASH1_ENABLE] = {
.id = ID_FLASH1,
.attr =
lm3644_attr("enable", NULL,
lm3644_flash1_enable_store),
},
[DFILE_FLASH1_ONOFF] = {
.id = ID_FLASH1,
.attr =
lm3644_attr("onoff", NULL,
lm3644_flash1_onoff_store),
},
[DFILE_TORCH0_ENABLE] = {
.id = ID_TORCH0,
.attr =
lm3644_attr("enable", NULL,
lm3644_torch0_enable_store),
},
[DFILE_TORCH0_ONOFF] = {
.id = ID_TORCH0,
.attr =
lm3644_attr("onoff", NULL,
lm3644_torch0_onoff_store),
},
[DFILE_TORCH0_SOURCE] = {
.id = ID_TORCH0,
.attr =
lm3644_attr("source", NULL,
lm3644_torch0_source_store),
},
[DFILE_TORCH1_ENABLE] = {
.id = ID_TORCH1,
.attr =
lm3644_attr("enable", NULL,
lm3644_torch1_enable_store),
},
[DFILE_TORCH1_ONOFF] = {
.id = ID_TORCH1,
.attr =
lm3644_attr("onoff", NULL,
lm3644_torch1_onoff_store),
}
};
static void lm3644_df_remove(struct lm3644 *pchip, enum lm3644_devfile dfid)
{
enum lm3644_devfile icnt;
for (icnt = dfid; icnt > 0; icnt--)
device_remove_file(pchip->cdev[lm3644_devfiles[icnt - 1].id].
dev, &lm3644_devfiles[icnt - 1].attr);
}
static int lm3644_df_create(struct lm3644 *pchip)
{
enum lm3644_devfile icnt;
int rval;
for (icnt = 0; icnt < DFILE_MAX; icnt++) {
rval =
device_create_file(pchip->cdev[lm3644_devfiles[icnt].id].
dev, &lm3644_devfiles[icnt].attr);
if (rval < 0) {
lm3644_df_remove(pchip, icnt);
return rval;
}
}
return 0;
}
static const struct regmap_config lm3644_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xff,
};
static int lm3644_fp_cam_module_detect(void)
{
switch (fp_cam_module) {
case FP_NO_CAM_MODULE:
return -EPROBE_DEFER;
case FP_CAM_MODULE_1:
return -ENODEV;
case FP_CAM_MODULE_2:
return 0;
default:
pr_err("%s: Invalid value from EEPROM: %d\n", __func__, fp_cam_module);
return -EINVAL;
}
}
static int lm3644_probe(struct platform_device *pdev)
{
struct msm_camera_cci_client *cci_client = NULL;
struct lm3644 *pchip;
int rval;
struct device_node *of_node = pdev->dev.of_node;
rval = lm3644_fp_cam_module_detect();
if (rval < 0)
return rval;
pchip = devm_kzalloc(&pdev->dev, sizeof(struct lm3644), GFP_KERNEL);
if (!pchip)
return -ENOMEM;
pchip->dev = &pdev->dev;
pchip->i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
pchip->i2c_client.cci_client = devm_kzalloc(&pdev->dev, sizeof(
struct msm_camera_cci_client), GFP_KERNEL);
if (!pchip->i2c_client.cci_client) {
return -ENOMEM;
}
rval = of_property_read_u32(of_node, "qcom,slave-addr",
&pchip->i2c_slaveaddr);
if (rval < 0) {
pr_err("%s failed rc %d\n", __func__, rval);
return rval;
}
rval = of_property_read_u32(of_node, "qcom,cci-master",
&pchip->cci_master);
if (rval < 0) {
pr_err("%s failed rc %d\n", __func__, rval);
return rval;
}
rval = of_property_read_u32(of_node, "fp,torch-current-led0",
&pchip->torch_current_led0);
if (rval < 0) {
/* Use maximum value if undefined */
pchip->torch_current_led0 =
lm3644_leds[ID_TORCH_DUAL].cdev.max_brightness;
}
rval = of_property_read_u32(of_node, "fp,torch-current-led1",
&pchip->torch_current_led1);
if (rval < 0) {
/* Use maximum value if undefined */
pchip->torch_current_led1 =
lm3644_leds[ID_TORCH_DUAL].cdev.max_brightness;
}
cci_client = pchip->i2c_client.cci_client;
cci_client->cci_subdev = msm_cci_get_subdev();
cci_client->sid = pchip->i2c_slaveaddr >> 1;
cci_client->retries = 3;
cci_client->id_map = 0;
cci_client->cci_i2c_master = pchip->cci_master;
dev_info(pchip->dev, "lm3644 leds i2c_slave addr: 0x%x, cci_i2c_master: %u\n",
cci_client->sid, cci_client->cci_i2c_master);
pchip->regmap = devm_regmap_init_msm_camera_i2c(pdev, &lm3644_regmap);
if (IS_ERR(pchip->regmap)) {
rval = PTR_ERR(pchip->regmap);
dev_err(&pdev->dev, "Failed to allocate register map: %d\n",
rval);
return rval;
}
lm3644_wq = alloc_workqueue("lm3644_wq", WQ_HIGHPRI|WQ_NON_REENTRANT|WQ_UNBOUND, 1);
if (lm3644_wq == NULL) {
return -ENOMEM;
}
mutex_init(&pchip->lock);
platform_set_drvdata(pdev, pchip);
/* led class register */
rval = lm3644_led_register(pchip);
if (rval < 0)
return rval;
/* create dev files */
rval = lm3644_df_create(pchip);
if (rval < 0) {
lm3644_led_unregister(pchip, ID_MAX);
return rval;
}
_pchip = pchip;
dev_info(pchip->dev, "lm3644 leds initialized\n");
return 0;
}
static const struct of_device_id msm_actuator_dt_match[] = {
{.compatible = "ti,lm3644", .data = NULL},
{}
};
static struct platform_driver lm3644_platform_data_driver = {
.driver = {
.name = LM3644_NAME,
.owner = THIS_MODULE,
.of_match_table = msm_actuator_dt_match,
},
.probe = lm3644_probe,
};
static int __init lm3644_init_module(void)
{
CDBG("%s:\n",__func__);
return platform_driver_register(&lm3644_platform_data_driver);
}
module_init(lm3644_init_module);
MODULE_LICENSE("GPL v2");