From patchwork Mon Jan 19 08:25:23 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Jeong X-Patchwork-Id: 5655531 Return-Path: X-Original-To: patchwork-linux-media@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 357A69F2ED for ; Mon, 19 Jan 2015 08:25:59 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 6EC652037B for ; Mon, 19 Jan 2015 08:25:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 5EB38201EC for ; Mon, 19 Jan 2015 08:25:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751356AbbASIZj (ORCPT ); Mon, 19 Jan 2015 03:25:39 -0500 Received: from mail-pd0-f177.google.com ([209.85.192.177]:49917 "EHLO mail-pd0-f177.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751244AbbASIZi (ORCPT ); Mon, 19 Jan 2015 03:25:38 -0500 Received: by mail-pd0-f177.google.com with SMTP id y13so10369617pdi.8; Mon, 19 Jan 2015 00:25:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=+ddSuBRDREZTVpa39aN3SwX5t/BA79EHPMW8vKZXK5g=; b=FzaCCQDX+IuefEeVvNAgfokSKBVP4XRR6MlGgltwPEOYpmzhpg2CYkY/SaCFnTIyCC wLNCAaKNne72Hjc2LVxF0uZHeUx4JuA4x/gdc9FifK6gKeDnCADU72LIGCmdGTTYfDD0 BSwrDCdsAynUD6U/eg+kIzF3qxWIxopXWNM2tKay08n6+ssUMQaPT4LQ5r14vLtgpZQm g08pDGd/oYaSyBGF0IkJHrm9t6z5gqCW70x80oaGl8gd0GEmUJn0yb9eyo64TKl2kky5 Zm5Bp2KGQ28A7he1lJgl8Hu1rGVlTrakF6EPD0FMYgGzsaaWKhxAixynr+qRLisu/QF4 HEpQ== X-Received: by 10.68.129.134 with SMTP id nw6mr33751819pbb.132.1421655938113; Mon, 19 Jan 2015 00:25:38 -0800 (PST) Received: from localhost.localdomain ([211.36.156.142]) by mx.google.com with ESMTPSA id fa10sm393821pdb.55.2015.01.19.00.25.33 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 19 Jan 2015 00:25:36 -0800 (PST) From: Daniel Jeong To: Mauro Carvalho Chehab , Sakari Ailus , Andy Shevchenko Cc: Hans Verkuil , Daniel Jeong , , Subject: [PATCH] media: i2c: add new driver for single string flash. Date: Mon, 19 Jan 2015 17:25:23 +0900 Message-Id: <1421655923-20466-1-git-send-email-gshark.jeong@gmail.com> X-Mailer: git-send-email 1.7.9.5 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds the driver for the single string flash products of TI. Several single string flash controllers of TI have similar register map and bit data. This driver supports four products,lm3556, lm3561, lm3642 and lm3648. Signed-off-by: Daniel Jeong --- drivers/media/i2c/Kconfig | 9 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ti-ss-flash.c | 744 +++++++++++++++++++++++++++++++++++++++ include/media/ti-ss-flash.h | 33 ++ 4 files changed, 787 insertions(+) create mode 100644 drivers/media/i2c/ti-ss-flash.c create mode 100644 include/media/ti-ss-flash.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 205d713..886c1fb 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -638,6 +638,15 @@ config VIDEO_LM3646 This is a driver for the lm3646 dual flash controllers. It controls flash, torch LEDs. +config VIDEO_TI_SS_FLASH + tristate "TI Single String Flash driver support" + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + select REGMAP_I2C + ---help--- + This is a driver for the signle string flash controllers of TI. + It supports LM3556, LM3561, LM3642 and LM3648. + comment "Video improvement chips" config VIDEO_UPD64031A diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 98589001..0e523ec 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o obj-$(CONFIG_VIDEO_LM3560) += lm3560.o obj-$(CONFIG_VIDEO_LM3646) += lm3646.o +obj-$(CONFIG_VIDEO_TI_SS_FLASH) += ti-ss-flash.o obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o diff --git a/drivers/media/i2c/ti-ss-flash.c b/drivers/media/i2c/ti-ss-flash.c new file mode 100644 index 0000000..035aeba --- /dev/null +++ b/drivers/media/i2c/ti-ss-flash.c @@ -0,0 +1,744 @@ +/* + * drivers/media/i2c/ti-ss-flash.c + * General device driver for Single String FLASH LED Drivers of TI + * It covers lm3556, lm3561, lm3642 and lm3648. + * + * Copyright (C) 2015 Texas Instruments + * + * Contact: Daniel Jeong + * Ldd-Mlp + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* operation mode */ +enum led_opmode { + OPMODE_SHDN = 0x0, + OPMODE_INDI_IR, + OPMODE_TORCH, + OPMODE_FLASH, +}; + +/* + * register data + * @reg : register + * @mask : mask bits + * @shift : bit shift of data + */ +struct ctrl_reg { + unsigned int reg; + unsigned int mask; + unsigned int shift; +}; + +/* + * unit data + * @min : min value of brightness or timeout + * brightness : uA + * timeout : ms + * @step : step value of brightness or timeout + * brightness : uA + * timeout : ms + * @knee: knee point of step of brightness/timeout + * brightness : uA + * timeout : ms + * @knee_step : step value of brightness or timeout after knee point + * brightness : uA + * timeout : ms + * @max : max value of brightness or timeout + * brightness : uA + * timeout : ms + * @ctrl : register info to control brightness or timeout + */ +struct ssflash_config { + unsigned int min; + unsigned int step; + unsigned int knee; + unsigned int knee_step; + unsigned int max; + struct ctrl_reg ctrl; +}; + +/* + * @reg : fault register + * @mask : fault mask bit + * @v4l2_fault : bit mapping to V4L2_FLASH_FAULT_ + * refer to include//uapi/linux/v4l2-controls.h + */ +struct ssflash_fault { + unsigned int reg; + unsigned int mask; + unsigned int v4l2_fault; +}; + +#define NUM_V4L2_FAULT 9 + +/* + * ssflash data + * @name: device name + * @mode: operation mode control data + * @flash_br: flash brightness register and bit data + * @timeout: timeout control data + * @strobe: strobe control data + * @torch_br: torch brightness register and bit data + * @fault: fault data + * @func: initialize function + */ +struct ssflash_data { + char *name; + struct ctrl_reg mode; + struct ssflash_config flash_br; + struct ssflash_config timeout; + struct ctrl_reg strobe; + + struct ssflash_config torch_br; + struct ssflash_fault fault[NUM_V4L2_FAULT]; + + int (*func)(struct regmap *regmap); +}; + +/* + * struct ssflash_flash + * @dev: device + * @regmap: reg map for interface + * @ctrls_led: V4L2 contols + * @subdev_led: V4L2 subdev + * @data : chip control data + * @mode_reg: value of mode register + */ +struct ssflash_flash { + struct device *dev; + struct regmap *regmap; + + struct v4l2_ctrl_handler ctrls_led; + struct v4l2_subdev subdev_led; + const struct ssflash_data *data; + u8 mode_reg; +}; + +#define to_ssflash_flash(_ctrl) \ + container_of(_ctrl->handler, struct ssflash_flash, ctrls_led) + +static const struct ssflash_data flash_lm3556 = { + .name = LM3556_NAME, + .mode = { + .reg = 0x0a, .mask = 0x03, .shift = 0 + }, + .flash_br = { + .min = 93750, .step = 93750, + .max = 1500000, + .ctrl = { + .reg = 0x09, .mask = 0xf, .shift = 0 + }, + }, + .timeout = { + .min = 100, .step = 100, + .max = 800, + .ctrl = { + .reg = 0x08, .mask = 0x07, .shift = 0 + } + }, + .torch_br = { + .min = 46880, .step = 46880, + .max = 375000, + .ctrl = { + .reg = 0x09, .mask = 0x70, .shift = 4 + } + }, + .strobe = { + .reg = 0x0a, .mask = 0x20, .shift = 5 + }, + .fault = { + [0] = { + .reg = 0x0b, .mask = 0x01, + .v4l2_fault = V4L2_FLASH_FAULT_TIMEOUT, + }, + [1] = { + .reg = 0x0b, .mask = 0x02, + .v4l2_fault = V4L2_FLASH_FAULT_OVER_TEMPERATURE, + }, + [2] = { + .reg = 0x0b, .mask = 0x04, + .v4l2_fault = V4L2_FLASH_FAULT_SHORT_CIRCUIT, + }, + [3] = { + .reg = 0x0b, .mask = 0x08, + .v4l2_fault = V4L2_FLASH_FAULT_OVER_VOLTAGE, + }, + + [4] = { + .reg = 0x0b, .mask = 0x10, + .v4l2_fault = V4L2_FLASH_FAULT_UNDER_VOLTAGE, + }, + [5] = { + .reg = 0x0b, .mask = 0x20, + .v4l2_fault = V4L2_FLASH_FAULT_INPUT_VOLTAGE, + }, + }, + .func = NULL +}; + +static const struct ssflash_data flash_lm3561 = { + .name = LM3561_NAME, + .mode = { + .reg = 0x10, .mask = 0x03, .shift = 0 + }, + .flash_br = { + .min = 36000, .step = 37600, + .max = 600000, + .ctrl = { + .reg = 0xb0, .mask = 0xf, .shift = 0 + }, + }, + .timeout = { + .min = 32, .step = 32, + .max = 1024, + .ctrl = { + .reg = 0xc0, .mask = 0x1f, .shift = 0 + } + }, + .torch_br = { + .min = 18000, .step = 18800, + .max = 149600, + .ctrl = { + .reg = 0xa0, .mask = 0x07, .shift = 0 + } + }, + .strobe = { + .reg = 0xe0, .mask = 0x04, .shift = 2 + }, + .fault = { + [0] = { + .reg = 0xd0, .mask = 0x01, + .v4l2_fault = V4L2_FLASH_FAULT_TIMEOUT, + }, + [1] = { + .reg = 0xd0, .mask = 0x02, + .v4l2_fault = V4L2_FLASH_FAULT_OVER_TEMPERATURE, + }, + [2] = { + .reg = 0xd0, .mask = 0x04, + .v4l2_fault = V4L2_FLASH_FAULT_SHORT_CIRCUIT, + }, + [3] = { + .reg = 0xd0, .mask = 0x20, + .v4l2_fault = V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE, + }, + [4] = { + .reg = 0xd0, .mask = 0x80, + .v4l2_fault = V4L2_FLASH_FAULT_INPUT_VOLTAGE, + }, + }, + .func = NULL +}; + +static const struct ssflash_data flash_lm3642 = { + .name = LM3642_NAME, + .mode = { + .reg = 0x0a, .mask = 0x03, .shift = 0 + }, + .flash_br = { + .min = 93750, .step = 93750, + .max = 1500000, + .ctrl = { + .reg = 0x09, .mask = 0xf, .shift = 0 + }, + }, + .timeout = { + .min = 100, .step = 100, + .max = 800, + .ctrl = { + .reg = 0x08, .mask = 0x07, .shift = 0 + } + }, + .torch_br = { + .min = 46880, .step = 46880, + .max = 375000, + .ctrl = { + .reg = 0x09, .mask = 0x70, .shift = 4 + } + }, + .strobe = { + .reg = 0x0a, .mask = 0x20, .shift = 5 + }, + .fault = { + [0] = { + .reg = 0x0b, .mask = 0x01, + .v4l2_fault = V4L2_FLASH_FAULT_TIMEOUT, + }, + [1] = { + .reg = 0x0b, .mask = 0x02, + .v4l2_fault = V4L2_FLASH_FAULT_OVER_TEMPERATURE, + }, + [2] = { + .reg = 0x0b, .mask = 0x04, + .v4l2_fault = V4L2_FLASH_FAULT_SHORT_CIRCUIT, + }, + [3] = { + .reg = 0x0b, .mask = 0x20, + .v4l2_fault = V4L2_FLASH_FAULT_INPUT_VOLTAGE, + }, + }, + .func = NULL +}; + +static int ssflash_lm3648_init(struct regmap *regmap) +{ + int rval; + + /* enable LED */ + rval = regmap_update_bits(regmap, 0x01, 0x03, 0x03); + /* bit[7:6] must be set to 10b for proper operation */ + rval |= regmap_update_bits(regmap, 0x03, 0xc0, 0x80); + /* bit[7] must be set to 1b for proper operation */ + rval |= regmap_update_bits(regmap, 0x05, 0x80, 0x80); + if (rval < 0) + return rval; + return 0; +} + +static const struct ssflash_data flash_lm3648 = { + .name = LM3648_NAME, + .mode = { + .reg = 0x01, .mask = 0x0c, .shift = 2 + }, + .flash_br = { + .min = 21800, .step = 23450, + .max = 1500000, + .ctrl = { + .reg = 0x03, .mask = 0x3f, .shift = 0 + }, + }, + .timeout = { + .min = 10, .step = 10, + .knee = 100, .knee_step = 50, + .max = 1024, + .ctrl = { + .reg = 0x08, .mask = 0x0f, .shift = 0 + } + }, + .torch_br = { + .min = 1954, .step = 2800, + .max = 357600, + .ctrl = { + .reg = 0x5, .mask = 0x7f, .shift = 0 + } + }, + .strobe = { + .reg = 0x1, .mask = 0x20, .shift = 5 + }, + .fault = { + [0] = { + .reg = 0x0a, .mask = 0x01, + .v4l2_fault = V4L2_FLASH_FAULT_TIMEOUT, + }, + [1] = { + .reg = 0x0a, .mask = 0x04, + .v4l2_fault = V4L2_FLASH_FAULT_OVER_TEMPERATURE, + }, + [2] = { + .reg = 0x0a, .mask = 0x70, + .v4l2_fault = V4L2_FLASH_FAULT_SHORT_CIRCUIT, + }, + [3] = { + .reg = 0x0b, .mask = 0x04, + .v4l2_fault = V4L2_FLASH_FAULT_INPUT_VOLTAGE, + }, + [4] = { + .reg = 0x0b, .mask = 0x01, + .v4l2_fault = V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE, + }, + }, + .func = ssflash_lm3648_init +}; + +static u8 ssflash_conv_val_to_reg(unsigned int val, + unsigned int min, unsigned int step, + unsigned int knee, unsigned int knee_step, + unsigned int max) +{ + if (val >= max) + return 0xff; + + if (knee <= min) + return (u8)(val < min ? 0 : (val - min)/step); + return (u8)val < min ? 0 : + (val < knee ? (val-min)/step : + (val-knee)/knee_step + (knee-min)/step); +} + +/* mode control */ +static int ssflash_mode_ctrl(struct ssflash_flash *flash, + enum v4l2_flash_led_mode v4l_led_mode) +{ + const struct ssflash_data *data = flash->data; + enum led_opmode opmode; + + switch (v4l_led_mode) { + case V4L2_FLASH_LED_MODE_NONE: + opmode = OPMODE_SHDN; + break; + case V4L2_FLASH_LED_MODE_TORCH: + opmode = OPMODE_TORCH; + break; + case V4L2_FLASH_LED_MODE_FLASH: + opmode = OPMODE_FLASH; + break; + default: + return -EINVAL; + } + return regmap_update_bits(flash->regmap, data->mode.reg, + data->mode.mask, opmode << data->mode.shift); +} + +/* torch brightness control */ +static int ssflash_torch_brt_ctrl + (struct ssflash_flash *flash, unsigned int brt) +{ + const struct ssflash_data *data = flash->data; + int rval; + u8 br_bits; + + br_bits = ssflash_conv_val_to_reg(brt, + data->torch_br.min, data->torch_br.step, + data->torch_br.knee, data->torch_br.knee_step, + data->torch_br.max); + + rval = regmap_update_bits(flash->regmap, + data->torch_br.ctrl.reg, + data->torch_br.ctrl.mask, + br_bits << data->torch_br.ctrl.shift); + + return rval; +} + +/* flash brightness control */ +static int ssflash_flash_brt_ctrl(struct ssflash_flash *flash, + unsigned int brt) +{ + const struct ssflash_data *data = flash->data; + int rval; + u8 br_bits; + + br_bits = ssflash_conv_val_to_reg(brt, + data->flash_br.min, + data->flash_br.step, + data->flash_br.knee, + data->flash_br.knee_step, + data->flash_br.max); + + rval = regmap_update_bits(flash->regmap, + data->flash_br.ctrl.reg, + data->flash_br.ctrl.mask, + br_bits << data->flash_br.ctrl.shift); + + return rval; +} + +/* fault status */ +static int ssflash_get_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ssflash_flash *flash = to_ssflash_flash(ctrl); + const struct ssflash_data *data = flash->data; + int rval = -EINVAL; + + if (ctrl->id == V4L2_CID_FLASH_FAULT) { + int icnt; + s32 fault = 0; + unsigned int reg_val; + unsigned int reg = 0xfff; + + for (icnt = 0; icnt < NUM_V4L2_FAULT; icnt++) { + if (data->fault[icnt].mask == 0x0) + continue; + if (data->fault[icnt].reg != reg) { + reg = data->fault[icnt].reg; + rval = regmap_read( + flash->regmap, reg, ®_val); + if (rval < 0) + goto out; + } + if (rval & data->fault[icnt].mask) + fault |= data->fault[icnt].v4l2_fault; + } + ctrl->cur.val = fault; + } + +out: + return rval; +} + +static int ssflash_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ssflash_flash *flash = to_ssflash_flash(ctrl); + const struct ssflash_data *data = flash->data; + u8 tout_bits; + unsigned int reg_val; + int rval = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_FLASH_LED_MODE: + if (ctrl->val != V4L2_FLASH_LED_MODE_FLASH) + return ssflash_mode_ctrl(flash, ctrl->val); + /* switch to SHDN mode before flash strobe on */ + return ssflash_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE); + + case V4L2_CID_FLASH_TORCH_INTENSITY: + return ssflash_torch_brt_ctrl(flash, ctrl->val); + + case V4L2_CID_FLASH_INTENSITY: + return ssflash_flash_brt_ctrl(flash, ctrl->val); + + case V4L2_CID_FLASH_STROBE_SOURCE: + return regmap_update_bits(flash->regmap, + data->strobe.reg, data->strobe.mask, + (ctrl->val) << data->strobe.shift); + + case V4L2_CID_FLASH_STROBE: + /* read and check current mode of chip to start flash */ + rval = regmap_read(flash->regmap, data->mode.reg, ®_val); + if (rval < 0 || + (((reg_val & data->mode.mask)>>data->mode.shift) + != OPMODE_SHDN)) + return rval; + /* flash on */ + return ssflash_mode_ctrl(flash, + V4L2_FLASH_LED_MODE_FLASH); + + case V4L2_CID_FLASH_STROBE_STOP: + /* + * flash mode will be turned automatically + * from FLASH mode to SHDN mode after flash duration timeout + * read and check current mode of chip to stop flash + */ + rval = regmap_read(flash->regmap, data->mode.reg, ®_val); + if (rval < 0) + return rval; + if (((reg_val & data->mode.mask) + >> data->mode.shift) == OPMODE_FLASH) + return ssflash_mode_ctrl(flash, + V4L2_FLASH_LED_MODE_NONE); + return rval; + + case V4L2_CID_FLASH_TIMEOUT: + tout_bits = ssflash_conv_val_to_reg(ctrl->val, + data->timeout.min, + data->timeout.step, + data->timeout.knee, + data->timeout.knee_step, + data->timeout.max); + + return regmap_update_bits(flash->regmap, + data->timeout.ctrl.reg, + data->timeout.ctrl.mask, + tout_bits << data->timeout.ctrl.shift); + } + + return rval; +} + +static int ssflash_led_get_ctrl(struct v4l2_ctrl *ctrl) +{ + return ssflash_get_ctrl(ctrl); +} + +static int ssflash_led_set_ctrl(struct v4l2_ctrl *ctrl) +{ + return ssflash_set_ctrl(ctrl); +} + +static const struct v4l2_ctrl_ops ssflash_led_ctrl_ops = { + .g_volatile_ctrl = ssflash_led_get_ctrl, + .s_ctrl = ssflash_led_set_ctrl, +}; + +static int ssflash_init_controls(struct ssflash_flash *flash) +{ + struct v4l2_ctrl *fault; + struct v4l2_ctrl_handler *hdl = &flash->ctrls_led; + const struct v4l2_ctrl_ops *ops = &ssflash_led_ctrl_ops; + const struct ssflash_data *data = flash->data; + s64 fault_max = 0; + int icnt; + + v4l2_ctrl_handler_init(hdl, 8); + /* flash mode */ + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE, + V4L2_FLASH_LED_MODE_TORCH, ~0x7, + V4L2_FLASH_LED_MODE_NONE); + /* flash source */ + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE, + 0x1, ~0x3, V4L2_FLASH_STROBE_SOURCE_SOFTWARE); + /* flash strobe */ + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); + /* flash strobe stop */ + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); + /* flash strobe timeout */ + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT, + data->timeout.min, + data->timeout.max, + data->timeout.step, + data->timeout.max); + /* flash brt */ + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY, + data->flash_br.min, + data->flash_br.max, + data->flash_br.step, + data->flash_br.max); + /* torch brt */ + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY, + data->torch_br.min, + data->torch_br.max, + data->torch_br.step, + data->torch_br.max); + /* fault */ + for (icnt = 0; icnt < NUM_V4L2_FAULT; icnt++) + fault_max |= data->fault[icnt].v4l2_fault; + fault = v4l2_ctrl_new_std(hdl, + ops, V4L2_CID_FLASH_FAULT, 0, fault_max, 0, 0); + if (fault != NULL) + fault->flags |= V4L2_CTRL_FLAG_VOLATILE; + + if (hdl->error) + return hdl->error; + + flash->subdev_led.ctrl_handler = hdl; + return 0; +} + +/* initialize device */ +static const struct v4l2_subdev_ops ssflash_ops = { + .core = NULL, +}; + +static const struct regmap_config ssflash_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, +}; + +static int ssflash_subdev_init(struct ssflash_flash *flash) +{ + struct i2c_client *client = to_i2c_client(flash->dev); + int rval; + + v4l2_i2c_subdev_init(&flash->subdev_led, client, &ssflash_ops); + flash->subdev_led.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + strcpy(flash->subdev_led.name, flash->data->name); + + rval = ssflash_init_controls(flash); + if (rval) + goto err_out; + + rval = media_entity_init(&flash->subdev_led.entity, 0, NULL, 0); + if (rval < 0) + goto err_out; + + flash->subdev_led.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + return rval; + +err_out: + v4l2_ctrl_handler_free(&flash->ctrls_led); + return rval; +} + +static int ssflash_init_device(struct ssflash_flash *flash) +{ + int rval; + + /* output disable */ + rval = ssflash_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE); + if (rval < 0) + return rval; + + if (flash->data->func != NULL) { + rval = flash->data->func(flash->regmap); + if (rval < 0) + return rval; + } + + return rval; +} + +static int ssflash_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct ssflash_flash *flash; + int rval; + + flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); + if (flash == NULL) + return -ENOMEM; + + flash->regmap = devm_regmap_init_i2c(client, &ssflash_regmap); + if (IS_ERR(flash->regmap)) { + rval = PTR_ERR(flash->regmap); + return rval; + } + + flash->dev = &client->dev; + flash->data = (const struct ssflash_data *)devid->driver_data; + + rval = ssflash_subdev_init(flash); + if (rval < 0) + return rval; + + rval = ssflash_init_device(flash); + if (rval < 0) + return rval; + + i2c_set_clientdata(client, flash); + + return 0; +} + +static int ssflash_remove(struct i2c_client *client) +{ + struct ssflash_flash *flash = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(&flash->subdev_led); + v4l2_ctrl_handler_free(&flash->ctrls_led); + media_entity_cleanup(&flash->subdev_led.entity); + + return 0; +} + +static const struct i2c_device_id ssflash_id_table[] = { + {LM3556_NAME, (unsigned long)&flash_lm3556}, + {LM3561_NAME, (unsigned long)&flash_lm3561}, + {LM3642_NAME, (unsigned long)&flash_lm3642}, + {LM3648_NAME, (unsigned long)&flash_lm3648}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ssflash_id_table); + +static struct i2c_driver ssflash_i2c_driver = { + .driver = { + .name = SINGLE_STRING_FLASH_NAME, + }, + .probe = ssflash_probe, + .remove = ssflash_remove, + .id_table = ssflash_id_table, +}; + +module_i2c_driver(ssflash_i2c_driver); + +MODULE_AUTHOR("Daniel Jeong "); +MODULE_AUTHOR("Ldd Mlp "); +MODULE_DESCRIPTION("Texas Instruments single string LED flash driver"); +MODULE_LICENSE("GPL"); diff --git a/include/media/ti-ss-flash.h b/include/media/ti-ss-flash.h new file mode 100644 index 0000000..505b33f --- /dev/null +++ b/include/media/ti-ss-flash.h @@ -0,0 +1,33 @@ +/* + * include/media/ti-ss-flash.h + * + * Copyright (C) 2014 Texas Instruments + * + * Contact: Daniel Jeong + * Ldd-Mlp + * + * 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. + */ + +#ifndef __TI_SS_FLASH_H__ +#define __TI_SS_FLASH_H__ + +#include + +#define LM3556_NAME "lm3556" +#define LM3556_I2C_ADDR (0x63) + +#define LM3561_NAME "lm3561" +#define LM3561_I2C_ADDR (0x53) + +#define LM3642_NAME "lm3642" +#define LM3642_I2C_ADDR (0x63) + +#define LM3648_NAME "lm3648" +#define LM3648_I2C_ADDR (0x63) + +#define SINGLE_STRING_FLASH_NAME "ti_ss_flash" + +#endif /* __TI_SS_FLASH_H__ */