media: i2c: add SC132GS driver

add reset gpio control

Signed-off-by: Lin Jinhan <troy.lin@rock-chips.com>
Change-Id: I3b39752bddb2035bbd61828c8c86427901c973db
This commit is contained in:
Lin Jinhan
2022-05-20 16:57:27 +08:00
committed by Tao Huang
parent 50f3fd6b3d
commit 93e191ebba
3 changed files with 51 additions and 9 deletions

View File

@@ -1713,6 +1713,17 @@ config VIDEO_MT9V111
To compile this driver as a module, choose M here: the
module will be called mt9v111.
config VIDEO_SC132GS
tristate "SmartSens SC132GS sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on MEDIA_CAMERA_SUPPORT
select V4L2_FWNODE
help
Support for the SmartSens SC132GS sensor.
To compile this driver as a module, choose M here: the
module will be called sc132gs.
config VIDEO_SC230AI
tristate "SmartSens SC230AI sensor support"
depends on I2C && VIDEO_V4L2

View File

@@ -115,6 +115,7 @@ obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
obj-$(CONFIG_VIDEO_SC132GS) += sc132gs.o
obj-$(CONFIG_VIDEO_SC230AI) += sc230ai.o
obj-$(CONFIG_VIDEO_SC301IOT) += sc301iot.o
obj-$(CONFIG_VIDEO_SC3336) += sc3336.o

View File

@@ -8,6 +8,7 @@
* V0.0X01.0X03 add enum_frame_interval function.
* V0.0X01.0X04 add quick stream on/off
* V0.0X01.0X05 add function g_mbus_config
* V0.0X01.0X06 add function reset gpio control
*/
#include <linux/clk.h>
@@ -28,7 +29,7 @@
#include <media/v4l2-subdev.h>
#include <linux/pinctrl/consumer.h>
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x05)
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x06)
#ifndef V4L2_CID_DIGITAL_GAIN
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
#endif
@@ -103,6 +104,7 @@ struct sc132gs_mode {
struct sc132gs {
struct i2c_client *client;
struct clk *xvclk;
struct gpio_desc *reset_gpio;
struct gpio_desc *pwdn_gpio;
struct regulator_bulk_data supplies[SC132GS_NUM_SUPPLIES];
struct pinctrl *pinctrl;
@@ -533,7 +535,7 @@ static long sc132gs_compat_ioctl32(struct v4l2_subdev *sd,
{
void __user *up = compat_ptr(arg);
struct rkmodule_inf *inf;
long ret;
long ret = 0;
u32 stream = 0;
switch (cmd) {
@@ -545,14 +547,18 @@ static long sc132gs_compat_ioctl32(struct v4l2_subdev *sd,
}
ret = sc132gs_ioctl(sd, cmd, inf);
if (!ret)
if (!ret) {
ret = copy_to_user(up, inf, sizeof(*inf));
if (ret)
ret = -EFAULT;
}
kfree(inf);
break;
case RKMODULE_SET_QUICK_STREAM:
ret = copy_from_user(&stream, up, sizeof(u32));
if (!ret)
ret = sc132gs_ioctl(sd, cmd, &stream);
if (copy_from_user(&stream, up, sizeof(u32)))
return -EFAULT;
ret = sc132gs_ioctl(sd, cmd, &stream);
break;
default:
ret = -ENOIOCTLCMD;
@@ -649,6 +655,7 @@ static int sc132gs_s_stream(struct v4l2_subdev *sd, int on)
{
struct sc132gs *sc132gs = to_sc132gs(sd);
struct i2c_client *client = sc132gs->client;
unsigned int fps;
int ret = 0;
mutex_lock(&sc132gs->mutex);
@@ -656,6 +663,14 @@ static int sc132gs_s_stream(struct v4l2_subdev *sd, int on)
if (on == sc132gs->streaming)
goto unlock_and_return;
fps = DIV_ROUND_CLOSEST(sc132gs->cur_mode->max_fps.denominator,
sc132gs->cur_mode->max_fps.numerator);
dev_info(&sc132gs->client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on,
sc132gs->cur_mode->width,
sc132gs->cur_mode->height,
fps);
if (on) {
ret = pm_runtime_get_sync(&client->dev);
if (ret < 0) {
@@ -761,9 +776,17 @@ static int __sc132gs_power_on(struct sc132gs *sc132gs)
goto disable_clk;
}
if (!IS_ERR(sc132gs->reset_gpio))
gpiod_set_value_cansleep(sc132gs->reset_gpio, 1);
usleep_range(1000, 2000);
if (!IS_ERR(sc132gs->pwdn_gpio))
gpiod_set_value_cansleep(sc132gs->pwdn_gpio, 1);
if (!IS_ERR(sc132gs->reset_gpio))
gpiod_set_value_cansleep(sc132gs->reset_gpio, 0);
/* 8192 cycles prior to first SCCB transaction */
delay_us = sc132gs_cal_delay(8192);
usleep_range(delay_us, delay_us * 2);
@@ -780,6 +803,9 @@ static void __sc132gs_power_off(struct sc132gs *sc132gs)
{
int ret;
if (!IS_ERR(sc132gs->reset_gpio))
gpiod_set_value_cansleep(sc132gs->reset_gpio, 1);
if (!IS_ERR(sc132gs->pwdn_gpio))
gpiod_set_value_cansleep(sc132gs->pwdn_gpio, 0);
clk_disable_unprepare(sc132gs->xvclk);
@@ -850,7 +876,7 @@ static int sc132gs_enum_frame_interval(struct v4l2_subdev *sd,
return 0;
}
static int sc132gs_g_mbus_config(struct v4l2_subdev *sd,
static int sc132gs_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id,
struct v4l2_mbus_config *config)
{
u32 val = 0;
@@ -858,7 +884,7 @@ static int sc132gs_g_mbus_config(struct v4l2_subdev *sd,
val = 1 << (SC132GS_LANES - 1) |
V4L2_MBUS_CSI2_CHANNEL_0 |
V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
config->type = V4L2_MBUS_CSI2;
config->type = V4L2_MBUS_CSI2_DPHY;
config->flags = val;
return 0;
@@ -886,7 +912,6 @@ static const struct v4l2_subdev_core_ops sc132gs_core_ops = {
static const struct v4l2_subdev_video_ops sc132gs_video_ops = {
.s_stream = sc132gs_s_stream,
.g_frame_interval = sc132gs_g_frame_interval,
.g_mbus_config = sc132gs_g_mbus_config,
};
static const struct v4l2_subdev_pad_ops sc132gs_pad_ops = {
@@ -895,6 +920,7 @@ static const struct v4l2_subdev_pad_ops sc132gs_pad_ops = {
.enum_frame_interval = sc132gs_enum_frame_interval,
.get_fmt = sc132gs_get_fmt,
.set_fmt = sc132gs_set_fmt,
.get_mbus_config = sc132gs_g_mbus_config,
};
static const struct v4l2_subdev_ops sc132gs_subdev_ops = {
@@ -1098,6 +1124,10 @@ static int sc132gs_probe(struct i2c_client *client,
return -EINVAL;
}
sc132gs->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(sc132gs->reset_gpio))
dev_warn(dev, "Failed to get reset-gpios\n");
sc132gs->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
if (IS_ERR(sc132gs->pwdn_gpio))
dev_warn(dev, "Failed to get pwdn-gpios\n");