diff --git a/drivers/media/i2c/jaguar1_drv/Makefile b/drivers/media/i2c/jaguar1_drv/Makefile index 879a0fc795b3..12b00eb7251e 100644 --- a/drivers/media/i2c/jaguar1_drv/Makefile +++ b/drivers/media/i2c/jaguar1_drv/Makefile @@ -2,4 +2,4 @@ subdir-ccflags-y += -DNC_DEBUG obj-y := jaguar1_drv.o jaguar1_i2c.o \ jaguar1_video.o jaguar1_coax_protocol.o \ jaguar1_motion.o jaguar1_video_eq.o \ - jaguar1_mipi.o + jaguar1_mipi.o jaguar1_v4l2.o diff --git a/drivers/media/i2c/jaguar1_drv/jaguar1_drv.c b/drivers/media/i2c/jaguar1_drv/jaguar1_drv.c index 7ae3b0362310..35e18ae6aae3 100644 --- a/drivers/media/i2c/jaguar1_drv/jaguar1_drv.c +++ b/drivers/media/i2c/jaguar1_drv/jaguar1_drv.c @@ -55,6 +55,7 @@ #include "jaguar1_ioctl.h" #include "jaguar1_video_eq.h" #include "jaguar1_mipi.h" +#include "jaguar1_drv.h" #ifdef FOR_IMX6 #include "imx_mipi.h" #endif @@ -690,6 +691,29 @@ static long jaguar1_ioctl(struct file *file, unsigned int cmd, unsigned long arg return 0; } +void jaguar1_start(video_init_all *video_init) +{ + down(&jaguar1_lock); + vd_set_all(video_init); + up(&jaguar1_lock); +} + +void jaguar1_stop(void) +{ + video_input_init video_val; + + down(&jaguar1_lock); + arb_disable(0); + gpio_i2c_write(0x60, 0xff, 0x20); + + // ARB RESET High + gpio_i2c_write(0x60, 0x40, 0x11); + usleep_range(3000, 5000); + gpio_i2c_write(0x60, 0x40, 0x00); + vd_jaguar1_sw_reset(&video_val); + up(&jaguar1_lock); +} + /******************************************************************************* * Description : i2c client initial * Argurments : void @@ -743,14 +767,15 @@ static struct miscdevice jaguar1_dev = { *******************************************************************************/ static int __init jaguar1_module_init(void) { - video_init_all sVideoall; int ret = 0; - int ch; #ifdef FMT_SETTING_SAMPLE int dev_num = 0; #endif #ifdef STREAM_ON_DEFLAULT + video_init_all sVideoall; + int ch; + jaguar1_mclk= 3; init = true; fmt = 2; @@ -779,6 +804,7 @@ static int __init jaguar1_module_init(void) down(&jaguar1_lock); video_decoder_init(); +#ifdef STREAM_ON_DEFLAULT if(init) { for(ch=0;ch 3)) { @@ -55,8 +55,7 @@ static void arb_enable(int dev_num) printk("VDEC_ARBITER_INIT done 0x%X\n", en_param); } - -static void arb_disable(int dev_num) +void arb_disable(int dev_num) { gpio_i2c_write(jaguar1_i2c_addr[dev_num], 0xff, 0x20); gpio_i2c_write(jaguar1_i2c_addr[dev_num], 0x00, 0x00); diff --git a/drivers/media/i2c/jaguar1_drv/jaguar1_mipi.h b/drivers/media/i2c/jaguar1_drv/jaguar1_mipi.h index e62601e5c10e..327376e4b82e 100644 --- a/drivers/media/i2c/jaguar1_drv/jaguar1_mipi.h +++ b/drivers/media/i2c/jaguar1_drv/jaguar1_mipi.h @@ -31,8 +31,11 @@ extern unsigned int jaguar1_mclk; extern unsigned int jaguar1_lane; void arb_init(int dev_num); +void arb_enable(int dev_num); +void arb_disable(int dev_num); int mipi_datatype_set(unsigned char data_type); void mipi_tx_init(int dev_num); void mipi_video_format_set(video_input_init *dev_ch_info); void disable_parallel(int dev_num); + #endif diff --git a/drivers/media/i2c/jaguar1_drv/jaguar1_v4l2.c b/drivers/media/i2c/jaguar1_drv/jaguar1_v4l2.c new file mode 100644 index 000000000000..cf238b65cfb3 --- /dev/null +++ b/drivers/media/i2c/jaguar1_drv/jaguar1_v4l2.c @@ -0,0 +1,828 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * jaguar1 driver + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jaguar1_common.h" +#include "jaguar1_video.h" +#include "jaguar1_coax_protocol.h" +#include "jaguar1_motion.h" +#include "jaguar1_ioctl.h" +#include "jaguar1_video_eq.h" +#include "jaguar1_mipi.h" +#include "jaguar1_drv.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define JAGUAR1_XVCLK_FREQ 24000000 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define OF_CAMERA_MODULE_REGULATORS "rockchip,regulator-names" +#define OF_CAMERA_MODULE_REGULATOR_VOLTAGES "rockchip,regulator-voltages" + +#define JAGUAR1_NAME "jaguar1" + +struct jaguar1_gpio { + int pltfrm_gpio; + const char *label; + enum of_gpio_flags active_low; +}; + +struct jaguar1_regulator { + struct regulator *regulator; + u32 min_uV; + u32 max_uV; +}; + +struct jaguar1_regulators { + u32 cnt; + struct jaguar1_regulator *regulator; +}; + +struct jaguar1_pixfmt { + u32 code; +}; + +struct jaguar1_framesize { + u16 width; + u16 height; + enum NC_VIVO_CH_FORMATDEF fmt_idx; +}; + +struct jaguar1 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *rst_gpio; + struct gpio_desc *rst2_gpio; + struct gpio_desc *pd_gpio; + struct gpio_desc *pd2_gpio; + struct gpio_desc *pwd_gpio; + struct gpio_desc *pwd2_gpio; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct mutex mutex; + bool power_on; + struct jaguar1_regulators regulators; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + + struct v4l2_mbus_framefmt format; + const struct jaguar1_framesize *frame_size; + int streaming; +}; + +#define to_jaguar1(sd) container_of(sd, struct jaguar1, subdev) + +static const struct jaguar1_framesize jaguar1_framesizes[] = { + { + .width = 1280, + .height = 720, + .fmt_idx = AHD20_720P_25P, + }, + { + .width = 1920, + .height = 1080, + .fmt_idx = AHD20_1080P_25P, + } +}; + +static const struct jaguar1_pixfmt jaguar1_formats[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + } +}; + +static int __jaguar1_power_on(struct jaguar1 *jaguar1) +{ + u32 i; + int ret; + struct jaguar1_regulator *regulator; + struct device *dev = &jaguar1->client->dev; + + if (!IS_ERR_OR_NULL(jaguar1->pins_default)) { + ret = pinctrl_select_state(jaguar1->pinctrl, + jaguar1->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins. ret=%d\n", ret); + } + + ret = clk_prepare_enable(jaguar1->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (jaguar1->regulators.regulator) { + for (i = 0; i < jaguar1->regulators.cnt; i++) { + regulator = jaguar1->regulators.regulator + i; + if (IS_ERR(regulator->regulator)) + continue; + regulator_set_voltage( + regulator->regulator, + regulator->min_uV, + regulator->max_uV); + if (regulator_enable(regulator->regulator)) { + dev_err(dev, + "regulator_enable failed!\n"); + goto disable_clk; + } + } + } + usleep_range(3000, 5000); + + if (!IS_ERR(jaguar1->pwd_gpio)) { + gpiod_direction_output(jaguar1->pwd_gpio, 1); + usleep_range(3000, 5000); + } + + if (!IS_ERR(jaguar1->pwd2_gpio)) { + gpiod_direction_output(jaguar1->pwd2_gpio, 1); + usleep_range(3000, 5000); + } + + if (!IS_ERR(jaguar1->pd_gpio)) { + gpiod_direction_output(jaguar1->pd_gpio, 1); + usleep_range(1500, 2000); + } + + if (!IS_ERR(jaguar1->pd2_gpio)) { + gpiod_direction_output(jaguar1->pd2_gpio, 1); + usleep_range(1500, 2000); + } + + if (!IS_ERR(jaguar1->rst_gpio)) { + gpiod_direction_output(jaguar1->rst_gpio, 0); + usleep_range(1500, 2000); + gpiod_direction_output(jaguar1->rst_gpio, 1); + } + + if (!IS_ERR(jaguar1->rst2_gpio)) { + gpiod_direction_output(jaguar1->rst2_gpio, 0); + usleep_range(1500, 2000); + gpiod_direction_output(jaguar1->rst2_gpio, 1); + } + + return 0; + +disable_clk: + clk_disable_unprepare(jaguar1->xvclk); + + return ret; +} + +static void __jaguar1_power_off(struct jaguar1 *jaguar1) +{ + u32 i; + int ret; + struct jaguar1_regulator *regulator; + struct device *dev = &jaguar1->client->dev; + + if (!IS_ERR(jaguar1->pd_gpio)) + gpiod_direction_output(jaguar1->pd_gpio, 0); + + if (!IS_ERR(jaguar1->pd2_gpio)) + gpiod_direction_output(jaguar1->pd2_gpio, 0); + + clk_disable_unprepare(jaguar1->xvclk); + + if (!IS_ERR(jaguar1->rst_gpio)) + gpiod_direction_output(jaguar1->rst_gpio, 0); + + if (!IS_ERR(jaguar1->rst2_gpio)) + gpiod_direction_output(jaguar1->rst2_gpio, 0); + + if (!IS_ERR(jaguar1->pwd_gpio)) + gpiod_direction_output(jaguar1->pwd_gpio, 0); + + if (!IS_ERR(jaguar1->pwd2_gpio)) + gpiod_direction_output(jaguar1->pwd2_gpio, 0); + + if (!IS_ERR_OR_NULL(jaguar1->pins_sleep)) { + ret = pinctrl_select_state(jaguar1->pinctrl, + jaguar1->pins_sleep); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + if (jaguar1->regulators.regulator) { + for (i = 0; i < jaguar1->regulators.cnt; i++) { + regulator = jaguar1->regulators.regulator + i; + if (IS_ERR(regulator->regulator)) + continue; + regulator_disable(regulator->regulator); + } + } +} + +static int jaguar1_power(struct v4l2_subdev *sd, int on) +{ + struct jaguar1 *jaguar1 = to_jaguar1(sd); + int ret = 0; + + mutex_lock(&jaguar1->mutex); + + /* If the power state is not modified - no work to do. */ + if (jaguar1->power_on == !!on) + goto exit; + + if (on) { + ret = __jaguar1_power_on(jaguar1); + if (ret < 0) + goto exit; + + jaguar1->power_on = true; + } else { + __jaguar1_power_off(jaguar1); + jaguar1->power_on = false; + } + +exit: + mutex_unlock(&jaguar1->mutex); + + return ret; +} + +static void jaguar1_get_default_format(struct v4l2_mbus_framefmt *format) +{ + format->width = jaguar1_framesizes[0].width; + format->height = jaguar1_framesizes[0].height; + format->colorspace = V4L2_COLORSPACE_SRGB; + format->code = jaguar1_formats[0].code; + format->field = V4L2_FIELD_NONE; +} + +static int jaguar1_stream(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct jaguar1 *jaguar1 = to_jaguar1(sd); + video_init_all video_init; + enum NC_VIVO_CH_FORMATDEF fmt_idx; + int ch; + + dev_dbg(&client->dev, "%s: on %d\n", __func__, on); + mutex_lock(&jaguar1->mutex); + on = !!on; + + if (jaguar1->streaming == on) + goto unlock; + + if (on) { + fmt_idx = jaguar1->frame_size->fmt_idx; + for (ch = 0; ch < 4; ch++) { + video_init.ch_param[ch].ch = ch; + video_init.ch_param[ch].format = fmt_idx; + video_init.ch_param[ch].input = SINGLE_ENDED; + video_init.ch_param[ch].interface = YUV_422; + } + jaguar1_start(&video_init); + } else { + jaguar1_stop(); + } + + jaguar1->streaming = on; + +unlock: + mutex_unlock(&jaguar1->mutex); + + return 0; +} + +static int jaguar1_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + dev_dbg(&client->dev, "%s:\n", __func__); + + if (code->index >= ARRAY_SIZE(jaguar1_formats)) + return -EINVAL; + + code->code = jaguar1_formats[code->index].code; + + return 0; +} + +static int jaguar1_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i = ARRAY_SIZE(jaguar1_formats); + + dev_dbg(&client->dev, "%s:\n", __func__); + + if (fse->index >= ARRAY_SIZE(jaguar1_framesizes)) + return -EINVAL; + + while (--i) + if (fse->code == jaguar1_formats[i].code) + break; + + fse->code = jaguar1_formats[i].code; + + fse->min_width = jaguar1_framesizes[fse->index].width; + fse->max_width = fse->min_width; + fse->max_height = jaguar1_framesizes[fse->index].height; + fse->min_height = fse->max_height; + + return 0; +} + +static int jaguar1_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct jaguar1 *jaguar1 = to_jaguar1(sd); + + dev_dbg(&client->dev, "%s enter\n", __func__); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_get_try_format(sd, cfg, 0); + mutex_lock(&jaguar1->mutex); + fmt->format = *mf; + mutex_unlock(&jaguar1->mutex); + return 0; +#else + return -ENOTTY; +#endif + } + + mutex_lock(&jaguar1->mutex); + fmt->format = jaguar1->format; + mutex_unlock(&jaguar1->mutex); + + dev_info(&client->dev, "%s: %x %dx%d\n", __func__, + jaguar1->format.code, jaguar1->format.width, + jaguar1->format.height); + + return 0; +} + +static void __jaguar1_try_frame_size(struct v4l2_mbus_framefmt *mf, + const struct jaguar1_framesize **size) +{ + const struct jaguar1_framesize *fsize = &jaguar1_framesizes[0]; + const struct jaguar1_framesize *match = NULL; + int i = ARRAY_SIZE(jaguar1_framesizes); + unsigned int min_err = UINT_MAX; + + while (i--) { + unsigned int err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + if (err < min_err) { + min_err = err; + match = fsize; + } + fsize++; + } + + if (!match) + match = &jaguar1_framesizes[0]; + + mf->width = match->width; + mf->height = match->height; + + if (size) + *size = match; +} + +static int jaguar1_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int index = ARRAY_SIZE(jaguar1_formats); + struct v4l2_mbus_framefmt *mf = &fmt->format; + const struct jaguar1_framesize *size = NULL; + struct jaguar1 *jaguar1 = to_jaguar1(sd); + int ret = 0; + + dev_dbg(&client->dev, "%s enter\n", __func__); + + __jaguar1_try_frame_size(mf, &size); + + while (--index >= 0) + if (jaguar1_formats[index].code == mf->code) + break; + + if (index < 0) + return -EINVAL; + + mf->colorspace = V4L2_COLORSPACE_SRGB; + mf->code = jaguar1_formats[index].code; + mf->field = V4L2_FIELD_NONE; + + mutex_lock(&jaguar1->mutex); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *mf = fmt->format; +#else + return -ENOTTY; +#endif + } else { + if (jaguar1->streaming) { + mutex_unlock(&jaguar1->mutex); + return -EBUSY; + } + + jaguar1->frame_size = size; + jaguar1->format = fmt->format; + } + + mutex_unlock(&jaguar1->mutex); + return ret; +} + +static void jaguar1_get_module_inf(struct jaguar1 *jaguar1, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, JAGUAR1_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, jaguar1->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, jaguar1->len_name, sizeof(inf->base.lens)); +} + +static long jaguar1_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct jaguar1 *jaguar1 = to_jaguar1(sd); + long ret = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + jaguar1_get_module_inf(jaguar1, (struct rkmodule_inf *)arg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long jaguar1_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = jaguar1_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = jaguar1_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int jaguar1_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct jaguar1 *jaguar1 = to_jaguar1(sd); + + return __jaguar1_power_on(jaguar1); +} + +static int jaguar1_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct jaguar1 *jaguar1 = to_jaguar1(sd); + + __jaguar1_power_off(jaguar1); + + return 0; +} + +static const struct dev_pm_ops jaguar1_pm_ops = { + SET_RUNTIME_PM_OPS(jaguar1_runtime_suspend, + jaguar1_runtime_resume, NULL) +}; + +static const struct v4l2_subdev_video_ops jaguar1_video_ops = { + .s_stream = jaguar1_stream, +}; + +static const struct v4l2_subdev_pad_ops jaguar1_subdev_pad_ops = { + .enum_mbus_code = jaguar1_enum_mbus_code, + .enum_frame_size = jaguar1_enum_frame_sizes, + .get_fmt = jaguar1_get_fmt, + .set_fmt = jaguar1_set_fmt, +}; + +static const struct v4l2_subdev_core_ops jaguar1_core_ops = { + .s_power = jaguar1_power, + .ioctl = jaguar1_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = jaguar1_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_ops jaguar1_subdev_ops = { + .core = &jaguar1_core_ops, + .video = &jaguar1_video_ops, + .pad = &jaguar1_subdev_pad_ops, +}; + +static int jaguar1_analyze_dts(struct jaguar1 *jaguar1) +{ + int ret; + int elem_size, elem_index; + const char *str = ""; + struct property *prop; + struct jaguar1_regulator *regulator; + struct device *dev = &jaguar1->client->dev; + struct device_node *np = of_node_get(dev->of_node); + + jaguar1->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(jaguar1->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + ret = clk_set_rate(jaguar1->xvclk, JAGUAR1_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + return ret; + } + if (clk_get_rate(jaguar1->xvclk) != JAGUAR1_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + + jaguar1->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(jaguar1->pinctrl)) { + jaguar1->pins_default = + pinctrl_lookup_state(jaguar1->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(jaguar1->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + jaguar1->pins_sleep = + pinctrl_lookup_state(jaguar1->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(jaguar1->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + elem_size = of_property_count_elems_of_size( + np, + OF_CAMERA_MODULE_REGULATOR_VOLTAGES, + sizeof(u32)); + prop = of_find_property( + np, + OF_CAMERA_MODULE_REGULATORS, + NULL); + if (elem_size > 0 && !IS_ERR_OR_NULL(prop)) { + jaguar1->regulators.regulator = + devm_kzalloc(&jaguar1->client->dev, + elem_size * sizeof(struct jaguar1_regulator), + GFP_KERNEL); + if (!jaguar1->regulators.regulator) + dev_err(dev, "could not malloc jaguar1_regulator\n"); + + jaguar1->regulators.cnt = elem_size; + + str = NULL; + elem_index = 0; + regulator = jaguar1->regulators.regulator; + do { + str = of_prop_next_string(prop, str); + if (!str) { + dev_err(dev, "%s is not match %s in dts\n", + OF_CAMERA_MODULE_REGULATORS, + OF_CAMERA_MODULE_REGULATOR_VOLTAGES); + break; + } + regulator->regulator = + devm_regulator_get_optional(dev, str); + if (IS_ERR(regulator->regulator)) + dev_err(dev, "devm_regulator_get %s failed\n", + str); + of_property_read_u32_index( + np, + OF_CAMERA_MODULE_REGULATOR_VOLTAGES, + elem_index++, + ®ulator->min_uV); + regulator->max_uV = regulator->min_uV; + regulator++; + } while (--elem_size); + } + + jaguar1->pd_gpio = devm_gpiod_get(dev, "pd", GPIOD_OUT_LOW); + if (IS_ERR(jaguar1->pd_gpio)) + dev_warn(dev, "can not find pd-gpios, error %ld\n", + PTR_ERR(jaguar1->pd_gpio)); + + jaguar1->pd2_gpio = devm_gpiod_get(dev, "pd2", GPIOD_OUT_LOW); + if (IS_ERR(jaguar1->pd2_gpio)) + dev_warn(dev, "can not find pd2-gpios, error %ld\n", + PTR_ERR(jaguar1->pd2_gpio)); + + jaguar1->rst_gpio = devm_gpiod_get(dev, "rst", GPIOD_OUT_LOW); + if (IS_ERR(jaguar1->rst_gpio)) + dev_warn(dev, "can not find rst-gpios, error %ld\n", + PTR_ERR(jaguar1->rst_gpio)); + + jaguar1->rst2_gpio = devm_gpiod_get(dev, "rst2", GPIOD_OUT_LOW); + if (IS_ERR(jaguar1->rst2_gpio)) + dev_warn(dev, "can not find rst2-gpios, error %ld\n", + PTR_ERR(jaguar1->rst2_gpio)); + + jaguar1->pwd_gpio = devm_gpiod_get(dev, "pwd", GPIOD_OUT_HIGH); + if (IS_ERR(jaguar1->pwd_gpio)) + dev_warn(dev, "can not find pwd-gpios, error %ld\n", + PTR_ERR(jaguar1->pwd_gpio)); + + jaguar1->pwd2_gpio = devm_gpiod_get(dev, "pwd2", GPIOD_OUT_HIGH); + if (IS_ERR(jaguar1->pwd2_gpio)) + dev_warn(dev, "can not find pwd2-gpios, error %ld\n", + PTR_ERR(jaguar1->pwd2_gpio)); + + return 0; +} + +static int jaguar1_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct jaguar1 *jaguar1; + struct v4l2_subdev *sd; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + jaguar1 = devm_kzalloc(dev, sizeof(*jaguar1), GFP_KERNEL); + if (!jaguar1) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &jaguar1->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &jaguar1->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &jaguar1->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &jaguar1->len_name); + if (ret) { + dev_err(dev, "could not get %s!\n", RKMODULE_CAMERA_LENS_NAME); + return -EINVAL; + } + + jaguar1->client = client; + + ret = jaguar1_analyze_dts(jaguar1); + if (ret) { + dev_err(dev, "Failed to analyze dts\n"); + return ret; + } + + mutex_init(&jaguar1->mutex); + jaguar1_get_default_format(&jaguar1->format); + jaguar1->frame_size = &jaguar1_framesizes[0]; + + sd = &jaguar1->subdev; + v4l2_i2c_subdev_init(sd, client, &jaguar1_subdev_ops); + + __jaguar1_power_on(jaguar1); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; +} + +static int jaguar1_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct jaguar1 *jaguar1 = to_jaguar1(sd); + + mutex_destroy(&jaguar1->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __jaguar1_power_off(jaguar1); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id jaguar1_of_match[] = { + { .compatible = "jaguar1-v4l2" }, + {}, +}; +MODULE_DEVICE_TABLE(of, jaguar1_of_match); +#endif + +static const struct i2c_device_id jaguar1_match_id[] = { + { "jaguar1-v4l2", 0 }, + { }, +}; + +static struct i2c_driver jaguar1_i2c_driver = { + .driver = { + .name = JAGUAR1_NAME, + .pm = &jaguar1_pm_ops, + .of_match_table = of_match_ptr(jaguar1_of_match), + }, + .probe = &jaguar1_probe, + .remove = &jaguar1_remove, + .id_table = jaguar1_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&jaguar1_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&jaguar1_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("jaguar1 sensor driver"); +MODULE_LICENSE("GPL v2");