410 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * drivers/media/i2c/lm3646.c4 * General device driver for TI lm3646, Dual FLASH LED Driver5 *6 * Copyright (C) 2014 Texas Instruments7 *8 * Contact: Daniel Jeong <gshark.jeong@gmail.com>9 * Ldd-Mlp <ldd-mlp@list.ti.com>10 */11 12#include <linux/delay.h>13#include <linux/i2c.h>14#include <linux/module.h>15#include <linux/slab.h>16#include <linux/regmap.h>17#include <linux/videodev2.h>18#include <media/i2c/lm3646.h>19#include <media/v4l2-ctrls.h>20#include <media/v4l2-device.h>21 22/* registers definitions */23#define REG_ENABLE 0x0124#define REG_TORCH_BR 0x0525#define REG_FLASH_BR 0x0526#define REG_FLASH_TOUT 0x0427#define REG_FLAG 0x0828#define REG_STROBE_SRC 0x0629#define REG_LED1_FLASH_BR 0x0630#define REG_LED1_TORCH_BR 0x0731 32#define MASK_ENABLE 0x0333#define MASK_TORCH_BR 0x7034#define MASK_FLASH_BR 0x0F35#define MASK_FLASH_TOUT 0x0736#define MASK_FLAG 0xFF37#define MASK_STROBE_SRC 0x8038 39/* Fault Mask */40#define FAULT_TIMEOUT (1<<0)41#define FAULT_SHORT_CIRCUIT (1<<1)42#define FAULT_UVLO (1<<2)43#define FAULT_IVFM (1<<3)44#define FAULT_OCP (1<<4)45#define FAULT_OVERTEMP (1<<5)46#define FAULT_NTC_TRIP (1<<6)47#define FAULT_OVP (1<<7)48 49enum led_mode {50 MODE_SHDN = 0x0,51 MODE_TORCH = 0x2,52 MODE_FLASH = 0x3,53};54 55/*56 * struct lm3646_flash57 *58 * @pdata: platform data59 * @regmap: reg. map for i2c60 * @lock: muxtex for serial access.61 * @led_mode: V4L2 LED mode62 * @ctrls_led: V4L2 controls63 * @subdev_led: V4L2 subdev64 * @mode_reg : mode register value65 */66struct lm3646_flash {67 struct device *dev;68 struct lm3646_platform_data *pdata;69 struct regmap *regmap;70 71 struct v4l2_ctrl_handler ctrls_led;72 struct v4l2_subdev subdev_led;73 74 u8 mode_reg;75};76 77#define to_lm3646_flash(_ctrl) \78 container_of(_ctrl->handler, struct lm3646_flash, ctrls_led)79 80/* enable mode control */81static int lm3646_mode_ctrl(struct lm3646_flash *flash,82 enum v4l2_flash_led_mode led_mode)83{84 switch (led_mode) {85 case V4L2_FLASH_LED_MODE_NONE:86 return regmap_write(flash->regmap,87 REG_ENABLE, flash->mode_reg | MODE_SHDN);88 case V4L2_FLASH_LED_MODE_TORCH:89 return regmap_write(flash->regmap,90 REG_ENABLE, flash->mode_reg | MODE_TORCH);91 case V4L2_FLASH_LED_MODE_FLASH:92 return regmap_write(flash->regmap,93 REG_ENABLE, flash->mode_reg | MODE_FLASH);94 }95 return -EINVAL;96}97 98/* V4L2 controls */99static int lm3646_get_ctrl(struct v4l2_ctrl *ctrl)100{101 struct lm3646_flash *flash = to_lm3646_flash(ctrl);102 unsigned int reg_val;103 int rval;104 105 if (ctrl->id != V4L2_CID_FLASH_FAULT)106 return -EINVAL;107 108 rval = regmap_read(flash->regmap, REG_FLAG, ®_val);109 if (rval < 0)110 return rval;111 112 ctrl->val = 0;113 if (reg_val & FAULT_TIMEOUT)114 ctrl->val |= V4L2_FLASH_FAULT_TIMEOUT;115 if (reg_val & FAULT_SHORT_CIRCUIT)116 ctrl->val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;117 if (reg_val & FAULT_UVLO)118 ctrl->val |= V4L2_FLASH_FAULT_UNDER_VOLTAGE;119 if (reg_val & FAULT_IVFM)120 ctrl->val |= V4L2_FLASH_FAULT_INPUT_VOLTAGE;121 if (reg_val & FAULT_OCP)122 ctrl->val |= V4L2_FLASH_FAULT_OVER_CURRENT;123 if (reg_val & FAULT_OVERTEMP)124 ctrl->val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;125 if (reg_val & FAULT_NTC_TRIP)126 ctrl->val |= V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE;127 if (reg_val & FAULT_OVP)128 ctrl->val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;129 130 return 0;131}132 133static int lm3646_set_ctrl(struct v4l2_ctrl *ctrl)134{135 struct lm3646_flash *flash = to_lm3646_flash(ctrl);136 unsigned int reg_val;137 int rval;138 139 switch (ctrl->id) {140 case V4L2_CID_FLASH_LED_MODE:141 142 if (ctrl->val != V4L2_FLASH_LED_MODE_FLASH)143 return lm3646_mode_ctrl(flash, ctrl->val);144 /* switch to SHDN mode before flash strobe on */145 return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE);146 147 case V4L2_CID_FLASH_STROBE_SOURCE:148 return regmap_update_bits(flash->regmap,149 REG_STROBE_SRC, MASK_STROBE_SRC,150 (ctrl->val) << 7);151 152 case V4L2_CID_FLASH_STROBE:153 154 /* read and check current mode of chip to start flash */155 rval = regmap_read(flash->regmap, REG_ENABLE, ®_val);156 if (rval < 0 || ((reg_val & MASK_ENABLE) != MODE_SHDN))157 return rval;158 /* flash on */159 return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_FLASH);160 161 case V4L2_CID_FLASH_STROBE_STOP:162 163 /*164 * flash mode will be turned automatically165 * from FLASH mode to SHDN mode after flash duration timeout166 * read and check current mode of chip to stop flash167 */168 rval = regmap_read(flash->regmap, REG_ENABLE, ®_val);169 if (rval < 0)170 return rval;171 if ((reg_val & MASK_ENABLE) == MODE_FLASH)172 return lm3646_mode_ctrl(flash,173 V4L2_FLASH_LED_MODE_NONE);174 return rval;175 176 case V4L2_CID_FLASH_TIMEOUT:177 return regmap_update_bits(flash->regmap,178 REG_FLASH_TOUT, MASK_FLASH_TOUT,179 LM3646_FLASH_TOUT_ms_TO_REG180 (ctrl->val));181 182 case V4L2_CID_FLASH_INTENSITY:183 return regmap_update_bits(flash->regmap,184 REG_FLASH_BR, MASK_FLASH_BR,185 LM3646_TOTAL_FLASH_BRT_uA_TO_REG186 (ctrl->val));187 188 case V4L2_CID_FLASH_TORCH_INTENSITY:189 return regmap_update_bits(flash->regmap,190 REG_TORCH_BR, MASK_TORCH_BR,191 LM3646_TOTAL_TORCH_BRT_uA_TO_REG192 (ctrl->val) << 4);193 }194 195 return -EINVAL;196}197 198static const struct v4l2_ctrl_ops lm3646_led_ctrl_ops = {199 .g_volatile_ctrl = lm3646_get_ctrl,200 .s_ctrl = lm3646_set_ctrl,201};202 203static int lm3646_init_controls(struct lm3646_flash *flash)204{205 struct v4l2_ctrl *fault;206 struct v4l2_ctrl_handler *hdl = &flash->ctrls_led;207 const struct v4l2_ctrl_ops *ops = &lm3646_led_ctrl_ops;208 209 v4l2_ctrl_handler_init(hdl, 8);210 /* flash mode */211 v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,212 V4L2_FLASH_LED_MODE_TORCH, ~0x7,213 V4L2_FLASH_LED_MODE_NONE);214 215 /* flash source */216 v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE,217 0x1, ~0x3, V4L2_FLASH_STROBE_SOURCE_SOFTWARE);218 219 /* flash strobe */220 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);221 /* flash strobe stop */222 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);223 224 /* flash strobe timeout */225 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT,226 LM3646_FLASH_TOUT_MIN,227 LM3646_FLASH_TOUT_MAX,228 LM3646_FLASH_TOUT_STEP, flash->pdata->flash_timeout);229 230 /* max flash current */231 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY,232 LM3646_TOTAL_FLASH_BRT_MIN,233 LM3646_TOTAL_FLASH_BRT_MAX,234 LM3646_TOTAL_FLASH_BRT_STEP,235 LM3646_TOTAL_FLASH_BRT_MAX);236 237 /* max torch current */238 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY,239 LM3646_TOTAL_TORCH_BRT_MIN,240 LM3646_TOTAL_TORCH_BRT_MAX,241 LM3646_TOTAL_TORCH_BRT_STEP,242 LM3646_TOTAL_TORCH_BRT_MAX);243 244 /* fault */245 fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0,246 V4L2_FLASH_FAULT_OVER_VOLTAGE247 | V4L2_FLASH_FAULT_OVER_TEMPERATURE248 | V4L2_FLASH_FAULT_SHORT_CIRCUIT249 | V4L2_FLASH_FAULT_TIMEOUT, 0, 0);250 if (fault != NULL)251 fault->flags |= V4L2_CTRL_FLAG_VOLATILE;252 253 if (hdl->error)254 return hdl->error;255 256 flash->subdev_led.ctrl_handler = hdl;257 return 0;258}259 260/* initialize device */261static const struct v4l2_subdev_ops lm3646_ops = {262 .core = NULL,263};264 265static const struct regmap_config lm3646_regmap = {266 .reg_bits = 8,267 .val_bits = 8,268 .max_register = 0xFF,269};270 271static int lm3646_subdev_init(struct lm3646_flash *flash)272{273 struct i2c_client *client = to_i2c_client(flash->dev);274 int rval;275 276 v4l2_i2c_subdev_init(&flash->subdev_led, client, &lm3646_ops);277 flash->subdev_led.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;278 strscpy(flash->subdev_led.name, LM3646_NAME,279 sizeof(flash->subdev_led.name));280 rval = lm3646_init_controls(flash);281 if (rval)282 goto err_out;283 rval = media_entity_pads_init(&flash->subdev_led.entity, 0, NULL);284 if (rval < 0)285 goto err_out;286 flash->subdev_led.entity.function = MEDIA_ENT_F_FLASH;287 return rval;288 289err_out:290 v4l2_ctrl_handler_free(&flash->ctrls_led);291 return rval;292}293 294static int lm3646_init_device(struct lm3646_flash *flash)295{296 unsigned int reg_val;297 int rval;298 299 /* read the value of mode register to reduce redundant i2c accesses */300 rval = regmap_read(flash->regmap, REG_ENABLE, ®_val);301 if (rval < 0)302 return rval;303 flash->mode_reg = reg_val & 0xfc;304 305 /* output disable */306 rval = lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE);307 if (rval < 0)308 return rval;309 310 /*311 * LED1 flash current setting312 * LED2 flash current = Total(Max) flash current - LED1 flash current313 */314 rval = regmap_update_bits(flash->regmap,315 REG_LED1_FLASH_BR, 0x7F,316 LM3646_LED1_FLASH_BRT_uA_TO_REG317 (flash->pdata->led1_flash_brt));318 319 if (rval < 0)320 return rval;321 322 /*323 * LED1 torch current setting324 * LED2 torch current = Total(Max) torch current - LED1 torch current325 */326 rval = regmap_update_bits(flash->regmap,327 REG_LED1_TORCH_BR, 0x7F,328 LM3646_LED1_TORCH_BRT_uA_TO_REG329 (flash->pdata->led1_torch_brt));330 if (rval < 0)331 return rval;332 333 /* Reset flag register */334 return regmap_read(flash->regmap, REG_FLAG, ®_val);335}336 337static int lm3646_probe(struct i2c_client *client)338{339 struct lm3646_flash *flash;340 struct lm3646_platform_data *pdata = dev_get_platdata(&client->dev);341 int rval;342 343 flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);344 if (flash == NULL)345 return -ENOMEM;346 347 flash->regmap = devm_regmap_init_i2c(client, &lm3646_regmap);348 if (IS_ERR(flash->regmap))349 return PTR_ERR(flash->regmap);350 351 /* check device tree if there is no platform data */352 if (pdata == NULL) {353 pdata = devm_kzalloc(&client->dev,354 sizeof(struct lm3646_platform_data),355 GFP_KERNEL);356 if (pdata == NULL)357 return -ENOMEM;358 /* use default data in case of no platform data */359 pdata->flash_timeout = LM3646_FLASH_TOUT_MAX;360 pdata->led1_torch_brt = LM3646_LED1_TORCH_BRT_MAX;361 pdata->led1_flash_brt = LM3646_LED1_FLASH_BRT_MAX;362 }363 flash->pdata = pdata;364 flash->dev = &client->dev;365 366 rval = lm3646_subdev_init(flash);367 if (rval < 0)368 return rval;369 370 rval = lm3646_init_device(flash);371 if (rval < 0)372 return rval;373 374 i2c_set_clientdata(client, flash);375 376 return 0;377}378 379static void lm3646_remove(struct i2c_client *client)380{381 struct lm3646_flash *flash = i2c_get_clientdata(client);382 383 v4l2_device_unregister_subdev(&flash->subdev_led);384 v4l2_ctrl_handler_free(&flash->ctrls_led);385 media_entity_cleanup(&flash->subdev_led.entity);386}387 388static const struct i2c_device_id lm3646_id_table[] = {389 { LM3646_NAME },390 {}391};392 393MODULE_DEVICE_TABLE(i2c, lm3646_id_table);394 395static struct i2c_driver lm3646_i2c_driver = {396 .driver = {397 .name = LM3646_NAME,398 },399 .probe = lm3646_probe,400 .remove = lm3646_remove,401 .id_table = lm3646_id_table,402};403 404module_i2c_driver(lm3646_i2c_driver);405 406MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");407MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");408MODULE_DESCRIPTION("Texas Instruments LM3646 Dual Flash LED driver");409MODULE_LICENSE("GPL");410