Adapt lm3644 driver to Fairphone2 use case

This patch adapts to the lm3644 driver to work with the
msm trigger driver (set_brightness activates flash).
Further it uses the pm8941 to power the lm3644.  The power
is enabled whenever the driver is entered and then disabled
in a periodic check if not needed anymore.

It also configures the lm3644 for strobe mode:
 * Set LM3644 to standby mode
   Control truth table in specification suggests standby mode
   is required to trigger flash with strobe pulse.

FPIIM-778

Change-Id: I0753f9bd2e589abf6db9e7e9d6c5167e3048189d
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 8958744..1a47757 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -6,6 +6,10 @@
 
 ccflags-$(CONFIG_LEDS_QPNP) += -Idrivers/media/platform/msm/camera_v2/
 
+ccflags-$(CONFIG_LEDS_LM3644) += -Idrivers/media/platform/msm/camera_v2/
+ccflags-$(CONFIG_LEDS_LM3644) += -Idrivers/media/platform/msm/camera_v2/sensor/cci
+ccflags-$(CONFIG_LEDS_LM3644) += -Idrivers/media/platform/msm/camera_v2/sensor/io
+
 # LED Platform Drivers
 obj-$(CONFIG_LEDS_88PM860X)		+= leds-88pm860x.o
 obj-$(CONFIG_LEDS_ATMEL_PWM)		+= leds-atmel-pwm.o
diff --git a/drivers/leds/leds-lm3644.c b/drivers/leds/leds-lm3644.c
index baf2fbb..e2c7e79 100644
--- a/drivers/leds/leds-lm3644.c
+++ b/drivers/leds/leds-lm3644.c
@@ -27,6 +27,16 @@
 #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 "lm3644_pm8941_power.h"
+
+
+struct msm_camera_i2c_reg_conf;
 
 /* registers definitions */
 #define REG_ENABLE		0x01
@@ -69,9 +79,24 @@
 	DFILE_MAX
 };
 
-#define to_lm3644(_ctrl, _no) container_of(_ctrl, struct lm3644, cdev[_no])
+#undef CDBG
+#if 1
+#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;
+
+	unsigned is_powered;
+	struct timer_list power_down_timer;
+
 	struct device *dev;
 
 	u8 brightness[ID_MAX];
@@ -83,9 +108,95 @@
 	struct mutex lock;
 };
 
+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 size_t lm3644_ctrl(struct device *dev,
+			  const char *buf, enum lm3644_devid id,
+			  enum lm3644_devfile dfid, size_t size);
+
+
+#define LM3644_POWER_TIMER_DELAY_MS 1000
+
+static void lm3644_power_down_timer_func(unsigned long data);
+
+/* to be called during driver initialization */
+static void lm3644_power_init(struct lm3644 *pchip) {
+	setup_timer(&pchip->power_down_timer, lm3644_power_down_timer_func,
+			(unsigned long) pchip);
+}
+
+/* XXX: Currently we are looking for the brightness values
+ * stored in the driver; maybe we want to check the enable
+ * registers instead */
+static int lm3644_should_power_down(struct lm3644 *pchip) {
+	int i;
+	for(i = 0; i < ID_MAX ; i++) {
+		if (pchip->brightness[i] > 0)
+			return 0;
+	}
+	return 1;
+}
+
+void lm3644_schedule_powerdown_timer_func(struct lm3644 *pchip) {
+	if (pchip->is_powered) {
+		mod_timer(&pchip->power_down_timer, jiffies +
+			msecs_to_jiffies(LM3644_POWER_TIMER_DELAY_MS));
+	}
+}
+
+static void lm3644_power_down_timer_func(unsigned long data) {
+	struct lm3644 *pchip = (void*) data;
+	if (lm3644_should_power_down(pchip)) {
+		lm3644_pm8941_power_down();
+		pchip->is_powered = 0;
+	} else {
+		lm3644_pm8941_power_pet_watchdog();
+	}
+	lm3644_schedule_powerdown_timer_func(pchip);
+}
+
+static int lm3644_power_up(struct lm3644 *pchip)
+{
+	int ret = 0;
+	mutex_lock(&pchip->lock);
+	del_timer_sync(&pchip->power_down_timer);
+	if (!pchip->is_powered) {
+		if( (ret = lm3644_pm8941_power_up()) )
+			goto error;
+		pchip->is_powered = 1;
+	}
+	/* we can extend the time now for LM3644_POWER_TIMER_DELAY_MS */
+error:
+	lm3644_schedule_powerdown_timer_func(pchip);
+	mutex_unlock(&pchip->lock);
+	return ret;
+}
+
 static void lm3644_read_flag(struct lm3644 *pchip)
 {
-
 	int rval;
 	unsigned int flag0, flag1;
 
@@ -93,22 +204,34 @@
 	rval |= regmap_read(pchip->regmap, REG_FLAG1, &flag1);
 
 	if (rval < 0)
-		dev_err(pchip->dev, "i2c access fail.\n");
+		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]);
+	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]))
+	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);
 }
 
@@ -119,19 +242,21 @@
 	    container_of(cdev, struct lm3644, cdev[ID_TORCH0]);
 
 	pchip->brightness[ID_TORCH0] = brightness;
-	schedule_work(&pchip->work[ID_TORCH0]);
+	schedule_work_on(0, &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]);
+	struct lm3644 *pchip = container_of(work, struct lm3644, work[ID_TORCH1]);
+	if ( (lm3644_power_up(pchip) < 0) )
+		return;
 
-	if (regmap_update_bits(pchip->regmap,
-			       REG_TORCH_LED1_BR, 0x7f,
+	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);
 }
 
@@ -142,19 +267,37 @@
 	    container_of(cdev, struct lm3644, cdev[ID_TORCH1]);
 
 	pchip->brightness[ID_TORCH1] = brightness;
-	schedule_work(&pchip->work[ID_TORCH1]);
+	schedule_work_on(0, &pchip->work[ID_TORCH1]);
+}
+
+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;
+
+	/* flash strobe (we can always set this one, could be also done in power up?)*/
+	regmap_update_bits(pchip->regmap, REG_ENABLE, 0x20, 0x20);
+	/* 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]);
+	struct lm3644 *pchip = container_of(work, struct lm3644, work[ID_FLASH0]);
+
+	if ( lm3644_power_up(pchip) < 0 )
+		return;
 
 	if (regmap_update_bits(pchip->regmap,
-			       REG_FLASH_LED0_BR, 0x7f,
+			       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);
 }
 
@@ -165,19 +308,23 @@
 	    container_of(cdev, struct lm3644, cdev[ID_FLASH0]);
 
 	pchip->brightness[ID_FLASH0] = brightness;
-	schedule_work(&pchip->work[ID_FLASH0]);
+	schedule_work_on(0, &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]);
+	struct lm3644 *pchip = container_of(work, struct lm3644, work[ID_FLASH1]);
+
+	if ( lm3644_power_up(pchip) < 0 )
+		return;
 
 	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);
 }
 
@@ -186,9 +333,8 @@
 {
 	struct lm3644 *pchip =
 	    container_of(cdev, struct lm3644, cdev[ID_FLASH1]);
-
 	pchip->brightness[ID_FLASH1] = brightness;
-	schedule_work(&pchip->work[ID_FLASH1]);
+	schedule_work_on(0, &pchip->work[ID_FLASH1]);
 }
 
 struct lm3644_devices {
@@ -223,7 +369,7 @@
 		       .cdev.brightness = 0,
 		       .cdev.max_brightness = 0x7f,
 		       .cdev.brightness_set = lm3644_torch1_brightness_set,
-		       .cdev.default_trigger = "torch1",
+		       .cdev.default_trigger = "torch0",
 		       .func = lm3644_deferred_torch1_brightness_set},
 };
 
@@ -260,30 +406,6 @@
 	return 0;
 }
 
-/* device files to control registers */
-struct lm3644_commands {
-	char *str;
-	int size;
-};
-
-enum lm3644_cmd_id {
-	CMD_ENABLE = 0,
-	CMD_DISABLE,
-	CMD_ON,
-	CMD_OFF,
-	CMD_IRMODE,
-	CMD_OVERRIDE,
-	CMD_MAX
-};
-
-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},
-};
 
 struct lm3644_files {
 	enum lm3644_devid id;
@@ -294,10 +416,24 @@
 			  const char *buf, enum lm3644_devid id,
 			  enum lm3644_devfile dfid, size_t size)
 {
-	struct led_classdev *led_cdev = dev_get_drvdata(dev);
-	struct lm3644 *pchip = to_lm3644(led_cdev, id);
+	struct lm3644 *pchip;
 	enum lm3644_cmd_id icnt;
 	int tout, rval;
+	int rc;
+
+	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);
+
+	if ( (rc = lm3644_power_up(pchip)) < 0) {
+		return size;
+	}
 
 	mutex_lock(&pchip->lock);
 	for (icnt = 0; icnt < CMD_MAX; icnt++) {
@@ -305,6 +441,9 @@
 			break;
 	}
 
+	if (rval < 0)
+		goto lm3644_ctrl_power_error;
+
 	switch (dfid) {
 		/* led 0 enable */
 	case DFILE_FLASH0_ENABLE:
@@ -422,6 +561,7 @@
 		break;
 	}
 	lm3644_read_flag(pchip);
+lm3644_ctrl_power_error:
 	mutex_unlock(&pchip->lock);
 	return size;
 }
@@ -621,32 +761,64 @@
 	.max_register = 0xff,
 };
 
-static int lm3644_probe(struct i2c_client *client,
-			const struct i2c_device_id *id)
+static int lm3644_probe(struct platform_device *pdev)
 {
+	struct msm_camera_cci_client *cci_client = NULL;
 	struct lm3644 *pchip;
 	int rval;
-
-	/* i2c check */
-	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-		dev_err(&client->dev, "i2c functionality check fail.\n");
-		return -EOPNOTSUPP;
-	}
-
-	pchip = devm_kzalloc(&client->dev, sizeof(struct lm3644), GFP_KERNEL);
+	struct device_node *of_node = pdev->dev.of_node;
+	
+	pchip = devm_kzalloc(&pdev->dev, sizeof(struct lm3644), GFP_KERNEL);
 	if (!pchip)
 		return -ENOMEM;
 
-	pchip->dev = &client->dev;
-	pchip->regmap = devm_regmap_init_i2c(client, &lm3644_regmap);
+	pchip->dev = &pdev->dev;
+
+	lm3644_power_init(pchip);
+
+	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;
+	}
+
+
+	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(&client->dev, "Failed to allocate register map: %d\n",
+		dev_err(&pdev->dev, "Failed to allocate register map: %d\n",
 			rval);
 		return rval;
 	}
+
 	mutex_init(&pchip->lock);
-	i2c_set_clientdata(client, pchip);
+	platform_set_drvdata(pdev, pchip);
 
 	/* led class register */
 	rval = lm3644_led_register(pchip);
@@ -664,36 +836,27 @@
 	return 0;
 }
 
-static int lm3644_remove(struct i2c_client *client)
-{
-	struct lm3644 *pchip = i2c_get_clientdata(client);
 
-	lm3644_df_remove(pchip, DFILE_MAX);
-	lm3644_led_unregister(pchip, ID_MAX);
-
-	return 0;
-}
-
-static const struct i2c_device_id lm3644_id[] = {
-	{LM3644_NAME, 0},
+static const struct of_device_id msm_actuator_dt_match[] = {
+	{.compatible = "ti,lm3644", .data = NULL},
 	{}
 };
 
-MODULE_DEVICE_TABLE(i2c, lm3644_id);
-
-static struct i2c_driver lm3644_i2c_driver = {
+static struct platform_driver lm3644_platform_data_driver = {
 	.driver = {
 		   .name = LM3644_NAME,
 		   .owner = THIS_MODULE,
-		   .pm = NULL,
+		   .of_match_table = msm_actuator_dt_match,
 		   },
 	.probe = lm3644_probe,
-	.remove = lm3644_remove,
-	.id_table = lm3644_id,
 };
 
-module_i2c_driver(lm3644_i2c_driver);
+static int __init lm3644_init_module(void)
+{
+	CDBG("%s:\n",__func__);
+	return platform_driver_register(&lm3644_platform_data_driver);
+}
 
-MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3644");
-MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>");
+
+module_init(lm3644_init_module);
 MODULE_LICENSE("GPL v2");