mirror of
https://github.com/torvalds/linux.git
synced 2024-12-13 22:53:20 +00:00
media: atomisp: Drop the atomisp custom lm3554 flash driver
Drop the atomisp custom lm3554 flash driver, support for flash drivers using the custom atomisp flash interface is going to be removed from the atomisp driver. If flash supports for the lm3554 turns out to be necessary on some atomisp devices in the future then this should be re-implemented using the new include/linux/led-class-flash.h APIs. The atomisp specific lm3554 driver could still be usable as a reference for how to use / driver the lm3554 for a future re-implementation using standardized APIs, but for that it can be retrieved from the git history. Reviewed-by: Andy Shevchenko <andy@kernel.org> Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
This commit is contained in:
parent
ca9ff72279
commit
f1aa35ab59
@ -56,18 +56,3 @@ config VIDEO_ATOMISP_GC0310
|
||||
help
|
||||
This is a Video4Linux2 sensor-level driver for the Galaxycore
|
||||
GC0310 0.3MP sensor.
|
||||
|
||||
#
|
||||
# Kconfig for flash drivers
|
||||
#
|
||||
|
||||
config VIDEO_ATOMISP_LM3554
|
||||
tristate "LM3554 flash light driver"
|
||||
depends on ACPI
|
||||
depends on VIDEO_DEV && I2C
|
||||
help
|
||||
This is a Video4Linux2 sub-dev driver for the LM3554
|
||||
flash light driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lm3554
|
||||
|
@ -9,8 +9,3 @@ obj-$(CONFIG_VIDEO_ATOMISP_OV2722) += atomisp-ov2722.o
|
||||
obj-$(CONFIG_VIDEO_ATOMISP_GC0310) += atomisp-gc0310.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_ATOMISP_MSRLIST_HELPER) += atomisp-libmsrlisthelper.o
|
||||
|
||||
# Makefile for flash drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_VIDEO_ATOMISP_LM3554) += atomisp-lm3554.o
|
||||
|
@ -1,955 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* LED flash driver for LM3554
|
||||
*
|
||||
* Copyright (c) 2010-2012 Intel Corporation. All Rights Reserved.
|
||||
*
|
||||
* 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/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "../include/media/lm3554.h"
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include "../include/linux/atomisp_gmin_platform.h"
|
||||
#include "../include/linux/atomisp.h"
|
||||
|
||||
/* Registers */
|
||||
|
||||
#define LM3554_TORCH_BRIGHTNESS_REG 0xA0
|
||||
#define LM3554_TORCH_MODE_SHIFT 0
|
||||
#define LM3554_TORCH_CURRENT_SHIFT 3
|
||||
#define LM3554_INDICATOR_CURRENT_SHIFT 6
|
||||
|
||||
#define LM3554_FLASH_BRIGHTNESS_REG 0xB0
|
||||
#define LM3554_FLASH_MODE_SHIFT 0
|
||||
#define LM3554_FLASH_CURRENT_SHIFT 3
|
||||
#define LM3554_STROBE_SENSITIVITY_SHIFT 7
|
||||
|
||||
#define LM3554_FLASH_DURATION_REG 0xC0
|
||||
#define LM3554_FLASH_TIMEOUT_SHIFT 0
|
||||
#define LM3554_CURRENT_LIMIT_SHIFT 5
|
||||
|
||||
#define LM3554_FLAGS_REG 0xD0
|
||||
#define LM3554_FLAG_TIMEOUT BIT(0)
|
||||
#define LM3554_FLAG_THERMAL_SHUTDOWN BIT(1)
|
||||
#define LM3554_FLAG_LED_FAULT BIT(2)
|
||||
#define LM3554_FLAG_TX1_INTERRUPT BIT(3)
|
||||
#define LM3554_FLAG_TX2_INTERRUPT BIT(4)
|
||||
#define LM3554_FLAG_LED_THERMAL_FAULT BIT(5)
|
||||
#define LM3554_FLAG_UNUSED BIT(6)
|
||||
#define LM3554_FLAG_INPUT_VOLTAGE_LOW BIT(7)
|
||||
|
||||
#define LM3554_CONFIG_REG_1 0xE0
|
||||
#define LM3554_ENVM_TX2_SHIFT 5
|
||||
#define LM3554_TX2_POLARITY_SHIFT 6
|
||||
|
||||
struct lm3554 {
|
||||
struct v4l2_subdev sd;
|
||||
|
||||
struct mutex power_lock;
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
int power_count;
|
||||
|
||||
unsigned int mode;
|
||||
int timeout;
|
||||
u8 torch_current;
|
||||
u8 indicator_current;
|
||||
u8 flash_current;
|
||||
|
||||
struct timer_list flash_off_delay;
|
||||
struct lm3554_platform_data *pdata;
|
||||
};
|
||||
|
||||
#define to_lm3554(p_sd) container_of(p_sd, struct lm3554, sd)
|
||||
|
||||
/* Return negative errno else zero on success */
|
||||
static int lm3554_write(struct lm3554 *flash, u8 addr, u8 val)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&flash->sd);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, addr, val);
|
||||
|
||||
dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val,
|
||||
ret < 0 ? "fail" : "ok");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Return negative errno else a data byte received from the device. */
|
||||
static int lm3554_read(struct lm3554 *flash, u8 addr)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&flash->sd);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, addr);
|
||||
|
||||
dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, ret,
|
||||
ret < 0 ? "fail" : "ok");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Hardware configuration
|
||||
*/
|
||||
|
||||
static int lm3554_set_mode(struct lm3554 *flash, unsigned int mode)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
val = (mode << LM3554_FLASH_MODE_SHIFT) |
|
||||
(flash->flash_current << LM3554_FLASH_CURRENT_SHIFT);
|
||||
|
||||
ret = lm3554_write(flash, LM3554_FLASH_BRIGHTNESS_REG, val);
|
||||
if (ret == 0)
|
||||
flash->mode = mode;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm3554_set_torch(struct lm3554 *flash)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
val = (flash->mode << LM3554_TORCH_MODE_SHIFT) |
|
||||
(flash->torch_current << LM3554_TORCH_CURRENT_SHIFT) |
|
||||
(flash->indicator_current << LM3554_INDICATOR_CURRENT_SHIFT);
|
||||
|
||||
return lm3554_write(flash, LM3554_TORCH_BRIGHTNESS_REG, val);
|
||||
}
|
||||
|
||||
static int lm3554_set_flash(struct lm3554 *flash)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
val = (flash->mode << LM3554_FLASH_MODE_SHIFT) |
|
||||
(flash->flash_current << LM3554_FLASH_CURRENT_SHIFT);
|
||||
|
||||
return lm3554_write(flash, LM3554_FLASH_BRIGHTNESS_REG, val);
|
||||
}
|
||||
|
||||
static int lm3554_set_duration(struct lm3554 *flash)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
val = (flash->timeout << LM3554_FLASH_TIMEOUT_SHIFT) |
|
||||
(flash->pdata->current_limit << LM3554_CURRENT_LIMIT_SHIFT);
|
||||
|
||||
return lm3554_write(flash, LM3554_FLASH_DURATION_REG, val);
|
||||
}
|
||||
|
||||
static int lm3554_set_config1(struct lm3554 *flash)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
val = (flash->pdata->envm_tx2 << LM3554_ENVM_TX2_SHIFT) |
|
||||
(flash->pdata->tx2_polarity << LM3554_TX2_POLARITY_SHIFT);
|
||||
return lm3554_write(flash, LM3554_CONFIG_REG_1, val);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Hardware trigger
|
||||
*/
|
||||
static void lm3554_flash_off_delay(struct timer_list *t)
|
||||
{
|
||||
struct lm3554 *flash = from_timer(flash, t, flash_off_delay);
|
||||
struct lm3554_platform_data *pdata = flash->pdata;
|
||||
|
||||
gpiod_set_value(pdata->gpio_strobe, 0);
|
||||
}
|
||||
|
||||
static int lm3554_hw_strobe(struct i2c_client *client, bool strobe)
|
||||
{
|
||||
int ret, timer_pending;
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
struct lm3554_platform_data *pdata = flash->pdata;
|
||||
|
||||
/*
|
||||
* An abnormal high flash current is observed when strobe off the
|
||||
* flash. Workaround here is firstly set flash current to lower level,
|
||||
* wait a short moment, and then strobe off the flash.
|
||||
*/
|
||||
|
||||
timer_pending = del_timer_sync(&flash->flash_off_delay);
|
||||
|
||||
/* Flash off */
|
||||
if (!strobe) {
|
||||
/* set current to 70mA and wait a while */
|
||||
ret = lm3554_write(flash, LM3554_FLASH_BRIGHTNESS_REG, 0);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
mod_timer(&flash->flash_off_delay,
|
||||
jiffies + msecs_to_jiffies(LM3554_TIMER_DELAY));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Flash on */
|
||||
|
||||
/*
|
||||
* If timer is killed before run, flash is not strobe off,
|
||||
* so must strobe off here
|
||||
*/
|
||||
if (timer_pending)
|
||||
gpiod_set_value(pdata->gpio_strobe, 0);
|
||||
|
||||
/* Restore flash current settings */
|
||||
ret = lm3554_set_flash(flash);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* Strobe on Flash */
|
||||
gpiod_set_value(pdata->gpio_strobe, 1);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_err(&client->dev, "failed to %s flash strobe (%d)\n",
|
||||
strobe ? "on" : "off", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* V4L2 controls
|
||||
*/
|
||||
|
||||
static int lm3554_read_status(struct lm3554 *flash)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&flash->sd);
|
||||
|
||||
/* NOTE: reading register clear fault status */
|
||||
ret = lm3554_read(flash, LM3554_FLAGS_REG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Accordingly to datasheet we read back '1' in bit 6.
|
||||
* Clear it first.
|
||||
*/
|
||||
ret &= ~LM3554_FLAG_UNUSED;
|
||||
|
||||
/*
|
||||
* Do not take TX1/TX2 signal as an error
|
||||
* because MSIC will not turn off flash, but turn to
|
||||
* torch mode according to gsm modem signal by hardware.
|
||||
*/
|
||||
ret &= ~(LM3554_FLAG_TX1_INTERRUPT | LM3554_FLAG_TX2_INTERRUPT);
|
||||
|
||||
if (ret > 0)
|
||||
dev_dbg(&client->dev, "LM3554 flag status: %02x\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm3554_s_flash_timeout(struct v4l2_subdev *sd, u32 val)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
|
||||
val = clamp(val, LM3554_MIN_TIMEOUT, LM3554_MAX_TIMEOUT);
|
||||
val = val / LM3554_TIMEOUT_STEPSIZE - 1;
|
||||
|
||||
flash->timeout = val;
|
||||
|
||||
return lm3554_set_duration(flash);
|
||||
}
|
||||
|
||||
static int lm3554_g_flash_timeout(struct v4l2_subdev *sd, s32 *val)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
|
||||
*val = (u32)(flash->timeout + 1) * LM3554_TIMEOUT_STEPSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm3554_s_flash_intensity(struct v4l2_subdev *sd, u32 intensity)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
|
||||
intensity = LM3554_CLAMP_PERCENTAGE(intensity);
|
||||
intensity = LM3554_PERCENT_TO_VALUE(intensity, LM3554_FLASH_STEP);
|
||||
|
||||
flash->flash_current = intensity;
|
||||
|
||||
return lm3554_set_flash(flash);
|
||||
}
|
||||
|
||||
static int lm3554_g_flash_intensity(struct v4l2_subdev *sd, s32 *val)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
|
||||
*val = LM3554_VALUE_TO_PERCENT((u32)flash->flash_current,
|
||||
LM3554_FLASH_STEP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm3554_s_torch_intensity(struct v4l2_subdev *sd, u32 intensity)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
|
||||
intensity = LM3554_CLAMP_PERCENTAGE(intensity);
|
||||
intensity = LM3554_PERCENT_TO_VALUE(intensity, LM3554_TORCH_STEP);
|
||||
|
||||
flash->torch_current = intensity;
|
||||
|
||||
return lm3554_set_torch(flash);
|
||||
}
|
||||
|
||||
static int lm3554_g_torch_intensity(struct v4l2_subdev *sd, s32 *val)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
|
||||
*val = LM3554_VALUE_TO_PERCENT((u32)flash->torch_current,
|
||||
LM3554_TORCH_STEP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm3554_s_indicator_intensity(struct v4l2_subdev *sd, u32 intensity)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
|
||||
intensity = LM3554_CLAMP_PERCENTAGE(intensity);
|
||||
intensity = LM3554_PERCENT_TO_VALUE(intensity, LM3554_INDICATOR_STEP);
|
||||
|
||||
flash->indicator_current = intensity;
|
||||
|
||||
return lm3554_set_torch(flash);
|
||||
}
|
||||
|
||||
static int lm3554_g_indicator_intensity(struct v4l2_subdev *sd, s32 *val)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
|
||||
*val = LM3554_VALUE_TO_PERCENT((u32)flash->indicator_current,
|
||||
LM3554_INDICATOR_STEP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm3554_s_flash_strobe(struct v4l2_subdev *sd, u32 val)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
|
||||
return lm3554_hw_strobe(client, val);
|
||||
}
|
||||
|
||||
static int lm3554_s_flash_mode(struct v4l2_subdev *sd, u32 new_mode)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
unsigned int mode;
|
||||
|
||||
switch (new_mode) {
|
||||
case ATOMISP_FLASH_MODE_OFF:
|
||||
mode = LM3554_MODE_SHUTDOWN;
|
||||
break;
|
||||
case ATOMISP_FLASH_MODE_FLASH:
|
||||
mode = LM3554_MODE_FLASH;
|
||||
break;
|
||||
case ATOMISP_FLASH_MODE_INDICATOR:
|
||||
mode = LM3554_MODE_INDICATOR;
|
||||
break;
|
||||
case ATOMISP_FLASH_MODE_TORCH:
|
||||
mode = LM3554_MODE_TORCH;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return lm3554_set_mode(flash, mode);
|
||||
}
|
||||
|
||||
static int lm3554_g_flash_mode(struct v4l2_subdev *sd, s32 *val)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
*val = flash->mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm3554_g_flash_status(struct v4l2_subdev *sd, s32 *val)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
int value;
|
||||
|
||||
value = lm3554_read_status(flash);
|
||||
if (value < 0)
|
||||
return value;
|
||||
|
||||
if (value & LM3554_FLAG_TIMEOUT)
|
||||
*val = ATOMISP_FLASH_STATUS_TIMEOUT;
|
||||
else if (value > 0)
|
||||
*val = ATOMISP_FLASH_STATUS_HW_ERROR;
|
||||
else
|
||||
*val = ATOMISP_FLASH_STATUS_OK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm3554_g_flash_status_register(struct v4l2_subdev *sd, s32 *val)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
int ret;
|
||||
|
||||
ret = lm3554_read(flash, LM3554_FLAGS_REG);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm3554_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct lm3554 *dev =
|
||||
container_of(ctrl->handler, struct lm3554, ctrl_handler);
|
||||
int ret = 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_FLASH_TIMEOUT:
|
||||
ret = lm3554_s_flash_timeout(&dev->sd, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_INTENSITY:
|
||||
ret = lm3554_s_flash_intensity(&dev->sd, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_TORCH_INTENSITY:
|
||||
ret = lm3554_s_torch_intensity(&dev->sd, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_INDICATOR_INTENSITY:
|
||||
ret = lm3554_s_indicator_intensity(&dev->sd, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_STROBE:
|
||||
ret = lm3554_s_flash_strobe(&dev->sd, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_MODE:
|
||||
ret = lm3554_s_flash_mode(&dev->sd, ctrl->val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm3554_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct lm3554 *dev =
|
||||
container_of(ctrl->handler, struct lm3554, ctrl_handler);
|
||||
int ret = 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_FLASH_TIMEOUT:
|
||||
ret = lm3554_g_flash_timeout(&dev->sd, &ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_INTENSITY:
|
||||
ret = lm3554_g_flash_intensity(&dev->sd, &ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_TORCH_INTENSITY:
|
||||
ret = lm3554_g_torch_intensity(&dev->sd, &ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_INDICATOR_INTENSITY:
|
||||
ret = lm3554_g_indicator_intensity(&dev->sd, &ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_MODE:
|
||||
ret = lm3554_g_flash_mode(&dev->sd, &ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_STATUS:
|
||||
ret = lm3554_g_flash_status(&dev->sd, &ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_FLASH_STATUS_REGISTER:
|
||||
ret = lm3554_g_flash_status_register(&dev->sd, &ctrl->val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops ctrl_ops = {
|
||||
.s_ctrl = lm3554_s_ctrl,
|
||||
.g_volatile_ctrl = lm3554_g_volatile_ctrl
|
||||
};
|
||||
|
||||
static const struct v4l2_ctrl_config lm3554_controls[] = {
|
||||
{
|
||||
.ops = &ctrl_ops,
|
||||
.id = V4L2_CID_FLASH_TIMEOUT,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Flash Timeout",
|
||||
.min = 0x0,
|
||||
.max = LM3554_MAX_TIMEOUT,
|
||||
.step = 0x01,
|
||||
.def = LM3554_DEFAULT_TIMEOUT,
|
||||
.flags = 0,
|
||||
},
|
||||
{
|
||||
.ops = &ctrl_ops,
|
||||
.id = V4L2_CID_FLASH_INTENSITY,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Flash Intensity",
|
||||
.min = LM3554_MIN_PERCENT,
|
||||
.max = LM3554_MAX_PERCENT,
|
||||
.step = 0x01,
|
||||
.def = LM3554_FLASH_DEFAULT_BRIGHTNESS,
|
||||
.flags = 0,
|
||||
},
|
||||
{
|
||||
.ops = &ctrl_ops,
|
||||
.id = V4L2_CID_FLASH_TORCH_INTENSITY,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Torch Intensity",
|
||||
.min = LM3554_MIN_PERCENT,
|
||||
.max = LM3554_MAX_PERCENT,
|
||||
.step = 0x01,
|
||||
.def = LM3554_TORCH_DEFAULT_BRIGHTNESS,
|
||||
.flags = 0,
|
||||
},
|
||||
{
|
||||
.ops = &ctrl_ops,
|
||||
.id = V4L2_CID_FLASH_INDICATOR_INTENSITY,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Indicator Intensity",
|
||||
.min = LM3554_MIN_PERCENT,
|
||||
.max = LM3554_MAX_PERCENT,
|
||||
.step = 0x01,
|
||||
.def = LM3554_INDICATOR_DEFAULT_BRIGHTNESS,
|
||||
.flags = 0,
|
||||
},
|
||||
{
|
||||
.ops = &ctrl_ops,
|
||||
.id = V4L2_CID_FLASH_STROBE,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flash Strobe",
|
||||
.min = 0,
|
||||
.max = 1,
|
||||
.step = 1,
|
||||
.def = 0,
|
||||
.flags = 0,
|
||||
},
|
||||
{
|
||||
.ops = &ctrl_ops,
|
||||
.id = V4L2_CID_FLASH_MODE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Flash Mode",
|
||||
.min = 0,
|
||||
.max = 100,
|
||||
.step = 1,
|
||||
.def = ATOMISP_FLASH_MODE_OFF,
|
||||
.flags = 0,
|
||||
},
|
||||
{
|
||||
.ops = &ctrl_ops,
|
||||
.id = V4L2_CID_FLASH_STATUS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Flash Status",
|
||||
.min = ATOMISP_FLASH_STATUS_OK,
|
||||
.max = ATOMISP_FLASH_STATUS_TIMEOUT,
|
||||
.step = 1,
|
||||
.def = ATOMISP_FLASH_STATUS_OK,
|
||||
.flags = 0,
|
||||
},
|
||||
{
|
||||
.ops = &ctrl_ops,
|
||||
.id = V4L2_CID_FLASH_STATUS_REGISTER,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Flash Status Register",
|
||||
.min = 0,
|
||||
.max = 255,
|
||||
.step = 1,
|
||||
.def = 0,
|
||||
.flags = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* V4L2 subdev core operations
|
||||
*/
|
||||
|
||||
/* Put device into known state. */
|
||||
static int lm3554_setup(struct lm3554 *flash)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&flash->sd);
|
||||
int ret;
|
||||
|
||||
/* clear the flags register */
|
||||
ret = lm3554_read(flash, LM3554_FLAGS_REG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(&client->dev, "Fault info: %02x\n", ret);
|
||||
|
||||
ret = lm3554_set_config1(flash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = lm3554_set_duration(flash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = lm3554_set_torch(flash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = lm3554_set_flash(flash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* read status */
|
||||
ret = lm3554_read_status(flash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int __lm3554_s_power(struct lm3554 *flash, int power)
|
||||
{
|
||||
struct lm3554_platform_data *pdata = flash->pdata;
|
||||
int ret;
|
||||
|
||||
/*initialize flash driver*/
|
||||
gpiod_set_value(pdata->gpio_reset, power);
|
||||
usleep_range(100, 100 + 1);
|
||||
|
||||
if (power) {
|
||||
/* Setup default values. This makes sure that the chip
|
||||
* is in a known state.
|
||||
*/
|
||||
ret = lm3554_setup(flash);
|
||||
if (ret < 0) {
|
||||
__lm3554_s_power(flash, 0);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm3554_s_power(struct v4l2_subdev *sd, int power)
|
||||
{
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&flash->power_lock);
|
||||
|
||||
if (flash->power_count == !power) {
|
||||
ret = __lm3554_s_power(flash, !!power);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
flash->power_count += power ? 1 : -1;
|
||||
WARN_ON(flash->power_count < 0);
|
||||
|
||||
done:
|
||||
mutex_unlock(&flash->power_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_core_ops lm3554_core_ops = {
|
||||
.s_power = lm3554_s_power,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops lm3554_ops = {
|
||||
.core = &lm3554_core_ops,
|
||||
};
|
||||
|
||||
static int lm3554_detect(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_err(&client->dev, "lm3554_detect i2c error\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Power up the flash driver and reset it */
|
||||
ret = lm3554_s_power(&flash->sd, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to power on lm3554 LED flash\n");
|
||||
} else {
|
||||
dev_dbg(&client->dev, "Successfully detected lm3554 LED flash\n");
|
||||
lm3554_s_power(&flash->sd, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm3554_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
||||
{
|
||||
return lm3554_s_power(sd, 1);
|
||||
}
|
||||
|
||||
static int lm3554_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
||||
{
|
||||
return lm3554_s_power(sd, 0);
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_internal_ops lm3554_internal_ops = {
|
||||
.registered = lm3554_detect,
|
||||
.open = lm3554_open,
|
||||
.close = lm3554_close,
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* I2C driver
|
||||
*/
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int lm3554_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
|
||||
struct lm3554 *flash = to_lm3554(subdev);
|
||||
int rval;
|
||||
|
||||
if (flash->power_count == 0)
|
||||
return 0;
|
||||
|
||||
rval = __lm3554_s_power(flash, 0);
|
||||
|
||||
dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok");
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static int lm3554_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
|
||||
struct lm3554 *flash = to_lm3554(subdev);
|
||||
int rval;
|
||||
|
||||
if (flash->power_count == 0)
|
||||
return 0;
|
||||
|
||||
rval = __lm3554_s_power(flash, 1);
|
||||
|
||||
dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok");
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define lm3554_suspend NULL
|
||||
#define lm3554_resume NULL
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static int lm3554_gpio_init(struct i2c_client *client)
|
||||
{
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
struct lm3554_platform_data *pdata = flash->pdata;
|
||||
int ret;
|
||||
|
||||
if (!pdata->gpio_reset)
|
||||
return -EINVAL;
|
||||
|
||||
ret = gpiod_direction_output(pdata->gpio_reset, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!pdata->gpio_strobe)
|
||||
return -EINVAL;
|
||||
|
||||
ret = gpiod_direction_output(pdata->gpio_strobe, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lm3554_gpio_uninit(struct i2c_client *client)
|
||||
{
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
struct lm3554_platform_data *pdata = flash->pdata;
|
||||
int ret;
|
||||
|
||||
ret = gpiod_direction_output(pdata->gpio_strobe, 0);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev,
|
||||
"gpio request/direction_output fail for gpio_strobe");
|
||||
|
||||
ret = gpiod_direction_output(pdata->gpio_reset, 0);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev,
|
||||
"gpio request/direction_output fail for gpio_reset");
|
||||
}
|
||||
|
||||
static void *lm3554_platform_data_func(struct i2c_client *client)
|
||||
{
|
||||
static struct lm3554_platform_data platform_data;
|
||||
|
||||
platform_data.gpio_reset = gpiod_get_index(&client->dev,
|
||||
NULL, 2, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(platform_data.gpio_reset))
|
||||
return ERR_CAST(platform_data.gpio_reset);
|
||||
platform_data.gpio_strobe = gpiod_get_index(&client->dev,
|
||||
NULL, 0, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(platform_data.gpio_strobe))
|
||||
return ERR_CAST(platform_data.gpio_strobe);
|
||||
platform_data.gpio_torch = gpiod_get_index(&client->dev,
|
||||
NULL, 1, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(platform_data.gpio_torch))
|
||||
return ERR_CAST(platform_data.gpio_torch);
|
||||
|
||||
/* Set to TX2 mode, then ENVM/TX2 pin is a power amplifier sync input:
|
||||
* ENVM/TX pin asserted, flash forced into torch;
|
||||
* ENVM/TX pin desserted, flash set back;
|
||||
*/
|
||||
platform_data.envm_tx2 = 1;
|
||||
platform_data.tx2_polarity = 0;
|
||||
|
||||
/* set peak current limit to be 1000mA */
|
||||
platform_data.current_limit = 0;
|
||||
|
||||
return &platform_data;
|
||||
}
|
||||
|
||||
static int lm3554_probe(struct i2c_client *client)
|
||||
{
|
||||
int err = 0;
|
||||
struct lm3554 *flash;
|
||||
unsigned int i;
|
||||
|
||||
flash = kzalloc(sizeof(*flash), GFP_KERNEL);
|
||||
if (!flash)
|
||||
return -ENOMEM;
|
||||
|
||||
flash->pdata = lm3554_platform_data_func(client);
|
||||
if (IS_ERR(flash->pdata)) {
|
||||
err = PTR_ERR(flash->pdata);
|
||||
goto free_flash;
|
||||
}
|
||||
|
||||
v4l2_i2c_subdev_init(&flash->sd, client, &lm3554_ops);
|
||||
flash->sd.internal_ops = &lm3554_internal_ops;
|
||||
flash->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
flash->mode = ATOMISP_FLASH_MODE_OFF;
|
||||
flash->timeout = LM3554_MAX_TIMEOUT / LM3554_TIMEOUT_STEPSIZE - 1;
|
||||
err =
|
||||
v4l2_ctrl_handler_init(&flash->ctrl_handler,
|
||||
ARRAY_SIZE(lm3554_controls));
|
||||
if (err) {
|
||||
dev_err(&client->dev, "error initialize a ctrl_handler.\n");
|
||||
goto unregister_subdev;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lm3554_controls); i++)
|
||||
v4l2_ctrl_new_custom(&flash->ctrl_handler, &lm3554_controls[i],
|
||||
NULL);
|
||||
|
||||
if (flash->ctrl_handler.error) {
|
||||
dev_err(&client->dev, "ctrl_handler error.\n");
|
||||
err = flash->ctrl_handler.error;
|
||||
goto free_handler;
|
||||
}
|
||||
|
||||
flash->sd.ctrl_handler = &flash->ctrl_handler;
|
||||
err = media_entity_pads_init(&flash->sd.entity, 0, NULL);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "error initialize a media entity.\n");
|
||||
goto free_handler;
|
||||
}
|
||||
|
||||
flash->sd.entity.function = MEDIA_ENT_F_FLASH;
|
||||
|
||||
mutex_init(&flash->power_lock);
|
||||
|
||||
timer_setup(&flash->flash_off_delay, lm3554_flash_off_delay, 0);
|
||||
|
||||
err = lm3554_gpio_init(client);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "gpio request/direction_output fail.\n");
|
||||
goto cleanup_media;
|
||||
}
|
||||
|
||||
err = atomisp_register_i2c_module(&flash->sd, NULL, LED_FLASH);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "fail to register atomisp i2c module.\n");
|
||||
goto uninit_gpio;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
uninit_gpio:
|
||||
lm3554_gpio_uninit(client);
|
||||
cleanup_media:
|
||||
media_entity_cleanup(&flash->sd.entity);
|
||||
free_handler:
|
||||
v4l2_ctrl_handler_free(&flash->ctrl_handler);
|
||||
unregister_subdev:
|
||||
v4l2_device_unregister_subdev(&flash->sd);
|
||||
free_flash:
|
||||
kfree(flash);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void lm3554_remove(struct i2c_client *client)
|
||||
{
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct lm3554 *flash = to_lm3554(sd);
|
||||
|
||||
media_entity_cleanup(&flash->sd.entity);
|
||||
v4l2_ctrl_handler_free(&flash->ctrl_handler);
|
||||
v4l2_device_unregister_subdev(sd);
|
||||
|
||||
atomisp_gmin_remove_subdev(sd);
|
||||
|
||||
timer_shutdown_sync(&flash->flash_off_delay);
|
||||
|
||||
lm3554_gpio_uninit(client);
|
||||
|
||||
kfree(flash);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops lm3554_pm_ops = {
|
||||
.suspend = lm3554_suspend,
|
||||
.resume = lm3554_resume,
|
||||
};
|
||||
|
||||
static const struct acpi_device_id lm3554_acpi_match[] = {
|
||||
{ "INTCF1C" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, lm3554_acpi_match);
|
||||
|
||||
static struct i2c_driver lm3554_driver = {
|
||||
.driver = {
|
||||
.name = "lm3554",
|
||||
.pm = &lm3554_pm_ops,
|
||||
.acpi_match_table = lm3554_acpi_match,
|
||||
},
|
||||
.probe = lm3554_probe,
|
||||
.remove = lm3554_remove,
|
||||
};
|
||||
module_i2c_driver(lm3554_driver);
|
||||
|
||||
MODULE_AUTHOR("Jing Tao <jing.tao@intel.com>");
|
||||
MODULE_DESCRIPTION("LED flash driver for LM3554");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,132 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* include/media/lm3554.h
|
||||
*
|
||||
* Copyright (c) 2010-2012 Intel Corporation. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
*/
|
||||
#ifndef _LM3554_H_
|
||||
#define _LM3554_H_
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
#define LM3554_ID 3554
|
||||
|
||||
#define v4l2_queryctrl_entry_integer(_id, _name,\
|
||||
_minimum, _maximum, _step, \
|
||||
_default_value, _flags) \
|
||||
{\
|
||||
.id = (_id), \
|
||||
.type = V4L2_CTRL_TYPE_INTEGER, \
|
||||
.name = _name, \
|
||||
.minimum = (_minimum), \
|
||||
.maximum = (_maximum), \
|
||||
.step = (_step), \
|
||||
.default_value = (_default_value),\
|
||||
.flags = (_flags),\
|
||||
}
|
||||
#define v4l2_queryctrl_entry_boolean(_id, _name,\
|
||||
_default_value, _flags) \
|
||||
{\
|
||||
.id = (_id), \
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN, \
|
||||
.name = _name, \
|
||||
.minimum = 0, \
|
||||
.maximum = 1, \
|
||||
.step = 1, \
|
||||
.default_value = (_default_value),\
|
||||
.flags = (_flags),\
|
||||
}
|
||||
|
||||
#define s_ctrl_id_entry_integer(_id, _name, \
|
||||
_minimum, _maximum, _step, \
|
||||
_default_value, _flags, \
|
||||
_s_ctrl, _g_ctrl) \
|
||||
{\
|
||||
.qc = v4l2_queryctrl_entry_integer(_id, _name,\
|
||||
_minimum, _maximum, _step,\
|
||||
_default_value, _flags), \
|
||||
.s_ctrl = _s_ctrl, \
|
||||
.g_ctrl = _g_ctrl, \
|
||||
}
|
||||
|
||||
#define s_ctrl_id_entry_boolean(_id, _name, \
|
||||
_default_value, _flags, \
|
||||
_s_ctrl, _g_ctrl) \
|
||||
{\
|
||||
.qc = v4l2_queryctrl_entry_boolean(_id, _name,\
|
||||
_default_value, _flags), \
|
||||
.s_ctrl = _s_ctrl, \
|
||||
.g_ctrl = _g_ctrl, \
|
||||
}
|
||||
|
||||
/* Value settings for Flash Time-out Duration*/
|
||||
#define LM3554_DEFAULT_TIMEOUT 512U
|
||||
#define LM3554_MIN_TIMEOUT 32U
|
||||
#define LM3554_MAX_TIMEOUT 1024U
|
||||
#define LM3554_TIMEOUT_STEPSIZE 32U
|
||||
|
||||
/* Flash modes */
|
||||
#define LM3554_MODE_SHUTDOWN 0
|
||||
#define LM3554_MODE_INDICATOR 1
|
||||
#define LM3554_MODE_TORCH 2
|
||||
#define LM3554_MODE_FLASH 3
|
||||
|
||||
/* timer delay time */
|
||||
#define LM3554_TIMER_DELAY 5
|
||||
|
||||
/* Percentage <-> value macros */
|
||||
#define LM3554_MIN_PERCENT 0U
|
||||
#define LM3554_MAX_PERCENT 100U
|
||||
#define LM3554_CLAMP_PERCENTAGE(val) \
|
||||
clamp(val, LM3554_MIN_PERCENT, LM3554_MAX_PERCENT)
|
||||
|
||||
#define LM3554_VALUE_TO_PERCENT(v, step) (((((unsigned long)(v)) * (step)) + 50) / 100)
|
||||
#define LM3554_PERCENT_TO_VALUE(p, step) (((((unsigned long)(p)) * 100) + (step >> 1)) / (step))
|
||||
|
||||
/* Product specific limits
|
||||
* TODO: get these from platform data */
|
||||
#define LM3554_FLASH_MAX_LVL 0x0F /* 1191mA */
|
||||
|
||||
/* Flash brightness, input is percentage, output is [0..15] */
|
||||
#define LM3554_FLASH_STEP \
|
||||
((100ul * (LM3554_MAX_PERCENT) + ((LM3554_FLASH_MAX_LVL) >> 1)) / ((LM3554_FLASH_MAX_LVL)))
|
||||
#define LM3554_FLASH_DEFAULT_BRIGHTNESS \
|
||||
LM3554_VALUE_TO_PERCENT(13, LM3554_FLASH_STEP)
|
||||
|
||||
/* Torch brightness, input is percentage, output is [0..7] */
|
||||
#define LM3554_TORCH_STEP 1250
|
||||
#define LM3554_TORCH_DEFAULT_BRIGHTNESS \
|
||||
LM3554_VALUE_TO_PERCENT(2, LM3554_TORCH_STEP)
|
||||
|
||||
/* Indicator brightness, input is percentage, output is [0..3] */
|
||||
#define LM3554_INDICATOR_STEP 2500
|
||||
#define LM3554_INDICATOR_DEFAULT_BRIGHTNESS \
|
||||
LM3554_VALUE_TO_PERCENT(1, LM3554_INDICATOR_STEP)
|
||||
|
||||
/*
|
||||
* lm3554_platform_data - Flash controller platform data
|
||||
*/
|
||||
struct lm3554_platform_data {
|
||||
struct gpio_desc *gpio_torch;
|
||||
struct gpio_desc *gpio_strobe;
|
||||
struct gpio_desc *gpio_reset;
|
||||
|
||||
unsigned int current_limit;
|
||||
unsigned int envm_tx2;
|
||||
unsigned int tx2_polarity;
|
||||
};
|
||||
|
||||
#endif /* _LM3554_H_ */
|
Loading…
Reference in New Issue
Block a user