/* * imx174.c - imx174 sensor driver * * Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include "imx174_mode_tbls.h" #include "../platform/tegra/camera/camera_gpio.h" #define IMX174_MAX_COARSE_DIFF 10 #define IMX174_CHIP_ID 0x82 #define IMX174_GAIN_SHIFT 8 #define IMX174_MIN_GAIN (1 << IMX174_GAIN_SHIFT) #define IMX174_MAX_GAIN (23 << IMX174_GAIN_SHIFT) #define IMX174_MIN_FRAME_LENGTH (1125) #define IMX174_MAX_FRAME_LENGTH (0xffff) #define IMX174_MIN_EXPOSURE_COARSE (0x0001) #define IMX174_MAX_EXPOSURE_COARSE \ (IMX174_MAX_FRAME_LENGTH-IMX174_MAX_COARSE_DIFF) #define IMX174_DEFAULT_GAIN IMX174_MIN_GAIN #define IMX174_DEFAULT_FRAME_LENGTH (0x0E51) #define IMX174_DEFAULT_EXPOSURE_COARSE \ (IMX174_DEFAULT_FRAME_LENGTH-IMX174_MAX_COARSE_DIFF) #define IMX174_DEFAULT_MODE IMX174_MODE_1920X1080_30FPS #define IMX174_DEFAULT_WIDTH 1920 #define IMX174_DEFAULT_HEIGHT 1080 #define IMX174_DEFAULT_DATAFMT MEDIA_BUS_FMT_Y12_1X12 #define IMX174_DEFAULT_CLK_FREQ 37500000 #define IMX174_FRAME_LENGTH_ADDR_MSB 0x0218 #define IMX174_FRAME_LENGTH_ADDR_LSB 0x0217 #define IMX174_COARSE_TIME_SHS_ADDR_MSB 0x029B #define IMX174_COARSE_TIME_SHS_ADDR_LSB 0x029A #define IMX174_GAIN_ADDR_MSB 0x0405 #define IMX174_GAIN_ADDR_LSB 0x0404 #define IMX174_GROUP_HOLD 0x020C struct imx174 { struct camera_common_power_rail power; struct v4l2_ctrl_handler ctrl_handler; int num_ctrls; struct spi_device *spi; struct device *dev; struct v4l2_subdev *subdev; struct media_pad pad; s32 group_hold_prev; u32 prevSvr; u32 frame_length; bool group_hold_en; struct regmap *regmap; struct camera_common_data *s_data; struct camera_common_pdata *pdata; struct v4l2_ctrl *ctrls[]; }; static const struct regmap_config sensor_regmap_config = { .reg_bits = 16, .val_bits = 8, .cache_type = REGCACHE_RBTREE, }; static int imx174_g_volatile_ctrl(struct v4l2_ctrl *ctrl); static int imx174_s_ctrl(struct v4l2_ctrl *ctrl); static const struct v4l2_ctrl_ops imx174_ctrl_ops = { .g_volatile_ctrl = imx174_g_volatile_ctrl, .s_ctrl = imx174_s_ctrl, }; static struct v4l2_ctrl_config ctrl_config_list[] = { /* Do not change the name field for the controls! */ { .ops = &imx174_ctrl_ops, .id = TEGRA_CAMERA_CID_GAIN, .name = "Gain", .type = V4L2_CTRL_TYPE_INTEGER, .flags = V4L2_CTRL_FLAG_SLIDER, .min = IMX174_MIN_GAIN, .max = IMX174_MAX_GAIN, .def = IMX174_DEFAULT_GAIN, .step = 1, }, { .ops = &imx174_ctrl_ops, .id = TEGRA_CAMERA_CID_FRAME_LENGTH, .name = "Frame Length", .type = V4L2_CTRL_TYPE_INTEGER, .flags = V4L2_CTRL_FLAG_SLIDER, .min = IMX174_MIN_FRAME_LENGTH, .max = IMX174_MAX_FRAME_LENGTH, .def = IMX174_DEFAULT_FRAME_LENGTH, .step = 1, }, { .ops = &imx174_ctrl_ops, .id = TEGRA_CAMERA_CID_COARSE_TIME, .name = "Coarse Time", .type = V4L2_CTRL_TYPE_INTEGER, .flags = V4L2_CTRL_FLAG_SLIDER, .min = IMX174_MIN_EXPOSURE_COARSE, .max = IMX174_MAX_EXPOSURE_COARSE, .def = IMX174_DEFAULT_EXPOSURE_COARSE, .step = 1, }, { .ops = &imx174_ctrl_ops, .id = TEGRA_CAMERA_CID_GROUP_HOLD, .name = "Group Hold", .type = V4L2_CTRL_TYPE_INTEGER_MENU, .min = 0, .max = ARRAY_SIZE(switch_ctrl_qmenu) - 1, .menu_skip_mask = 0, .def = 0, .qmenu_int = switch_ctrl_qmenu, }, { .ops = &imx174_ctrl_ops, .id = TEGRA_CAMERA_CID_HDR_EN, .name = "HDR enable", .type = V4L2_CTRL_TYPE_INTEGER_MENU, .min = 0, .max = ARRAY_SIZE(switch_ctrl_qmenu) - 1, .menu_skip_mask = 0, .def = 0, .qmenu_int = switch_ctrl_qmenu, }, }; static inline void imx174_get_frame_length_regs(imx174_reg *regs, u16 frame_length) { regs->addr = IMX174_FRAME_LENGTH_ADDR_MSB; regs->val = (frame_length >> 8) & 0x0f; // TVE: shift 8 bits or 16 bits? (regs + 1)->addr = IMX174_FRAME_LENGTH_ADDR_LSB; (regs + 1)->val = (frame_length) & 0xff; } static inline void imx174_get_coarse_time_regs_shs(imx174_reg *regs, u16 coarse_time) { regs->addr = IMX174_COARSE_TIME_SHS_ADDR_MSB; regs->val = (coarse_time >> 8) & 0x0f; (regs + 1)->addr = IMX174_COARSE_TIME_SHS_ADDR_LSB; (regs + 1)->val = (coarse_time) & 0xff; } static inline void imx174_get_gain_reg(imx174_reg *regs, u16 gain) { regs->addr = IMX174_GAIN_ADDR_MSB; regs->val = (gain >> 8) & 0x01; (regs + 1)->addr = IMX174_GAIN_ADDR_LSB; (regs + 1)->val = (gain) & 0xff; } static int test_mode; module_param(test_mode, int, 0644); static inline int imx174_read_reg(struct camera_common_data *s_data, u16 addr, u8 *val) { /* TEV: Changed addr32 to addr16 */ struct imx174 *priv = (struct imx174 *)s_data->priv; u16 addr32 = ((0x82 << 16) | (addr)); return regmap_read(priv->regmap, addr32, (unsigned int *)val); } static int imx174_write_reg(struct camera_common_data *s_data, u16 addr, u8 val) { int err; struct imx174 *priv = (struct imx174 *)s_data->priv; err = regmap_write(priv->regmap, addr, val); if (err) dev_err(priv->dev, "%s:spi write failed, %x = %x\n", __func__, addr, val); return err; } //static int imx174_write_reg32(struct camera_common_data *s_data, // u16 addr, u8 val) //{ // int err = 0; // struct imx174 *priv = (struct imx174 *)s_data->priv; // // err = regmap_write(priv->regmap, addr, val); // if (err) // dev_err(priv->dev, "%s:spi write failed, %x = %x\n", // __func__, addr, val); // // return err; //} static int imx174_write_table(struct imx174 *priv, const imx174_reg table[]) { int cnt = 0; int status = 0; while (table[cnt].addr != IMX174_TABLE_END) { if (table[cnt].addr == IMX174_TABLE_WAIT_MS) { msleep_range(table[cnt].val); } else { status = imx174_write_reg(priv->s_data, table[cnt].addr, table[cnt].val); if (status < 0) { dev_err(priv->dev, "REG0x%X=0x%X ERROR = %d\n", table[cnt].addr, table[cnt].val, status); return status; } } cnt++; } return status; } static void imx174_gpio_set(struct imx174 *priv, unsigned int gpio, int val) { if (gpio_cansleep(gpio)) gpio_set_value_cansleep(gpio, val); else gpio_set_value(gpio, val); } static int imx174_power_on(struct camera_common_data *s_data) { struct imx174 *priv = (struct imx174 *)s_data->priv; struct camera_common_power_rail *pw = &priv->power; if (gpio_is_valid(pw->reset_gpio)) imx174_gpio_set(priv, pw->reset_gpio, 1); pw->state = SWITCH_ON; return 0; } static int imx174_power_off(struct camera_common_data *s_data) { struct imx174 *priv = (struct imx174 *)s_data->priv; struct camera_common_power_rail *pw = &priv->power; if (gpio_is_valid(pw->reset_gpio)) imx174_gpio_set(priv, pw->reset_gpio, 0); pw->state = SWITCH_OFF; return 0; } static int imx174_power_put(struct imx174 *priv) { struct camera_common_power_rail *pw = &priv->power; if (gpio_is_valid(pw->reset_gpio)) gpio_free(pw->reset_gpio); return 0; } static int imx174_power_get(struct imx174 *priv) { struct camera_common_power_rail *pw = &priv->power; struct camera_common_pdata *pdata = priv->pdata; int ret = 0; pw->reset_gpio = pdata->reset_gpio; if (gpio_is_valid(pw->reset_gpio)) { ret = gpio_request(pw->reset_gpio, "cam_reset_gpio"); if (ret < 0) { dev_dbg(priv->dev, "%s can't request reset_gpio %d\n", __func__, ret); } } pw->state = SWITCH_OFF; return 0; } static int imx174_get_framesync(struct camera_common_data *s_data, struct camera_common_framesync *vshs) { vshs->inck = 37500; vshs->xhs = 300; // I don't know what the value means vshs->xvs = 4004; vshs->fps = 30000; return 0; } static int imx174_set_gain(struct imx174 *priv, s32 val); static int imx174_set_frame_length(struct imx174 *priv, s64 val); static int imx174_set_coarse_time(struct imx174 *priv, s64 val); static int imx174_s_stream(struct v4l2_subdev *sd, int enable) { struct camera_common_data *s_data = to_camera_common_data(sd->dev); struct imx174 *priv = (struct imx174 *)s_data->priv; struct v4l2_control control; int err; int sensor_mode = s_data->mode; dev_dbg(priv->dev, "%s++ enable = %d\n", __func__, enable); if (!enable) { return imx174_write_table(priv, mode_table[IMX174_MODE_STOP_STREAM]); } err = imx174_write_table(priv, mode_table[sensor_mode]); if (err) goto exit; if (s_data->override_enable) { /* * write list of override regs for the asking frame length, * coarse integration time, and gain. Failures to write * overrides are non-fatal */ control.id = TEGRA_CAMERA_CID_GAIN; err = v4l2_g_ctrl(&priv->ctrl_handler, &control); err |= imx174_set_gain(priv, control.value); if (err) dev_dbg(priv->dev, "%s: warning gain override failed\n", __func__); control.id = TEGRA_CAMERA_CID_FRAME_LENGTH; err = v4l2_g_ctrl(&priv->ctrl_handler, &control); err |= imx174_set_frame_length(priv, control.value); if (err) dev_dbg(priv->dev, "%s: warning frame length override failed\n", __func__); control.id = TEGRA_CAMERA_CID_COARSE_TIME; err = v4l2_g_ctrl(&priv->ctrl_handler, &control); err |= imx174_set_coarse_time(priv, control.value); if (err) dev_dbg(priv->dev, "%s: warning coarse time override failed\n", __func__); } if (tegra_platform_is_silicon()) { err = imx174_write_table(priv, mode_table[IMX174_MODE_START_STREAM]); if (err) goto exit; } dev_dbg(priv->dev, "%s--\n", __func__); return 0; exit: dev_info(priv->dev, "%s: error setting stream\n", __func__); return err; } static int imx174_g_input_status(struct v4l2_subdev *sd, u32 *status) { struct camera_common_data *s_data = to_camera_common_data(sd->dev); struct imx174 *priv = (struct imx174 *)s_data->priv; struct camera_common_power_rail *pw = &priv->power; *status = pw->state == SWITCH_ON; return 0; } static struct v4l2_subdev_video_ops imx174_subdev_video_ops = { .s_stream = imx174_s_stream, .g_mbus_config = camera_common_g_mbus_config, .g_input_status = imx174_g_input_status, }; static struct v4l2_subdev_core_ops imx174_subdev_core_ops = { .s_power = camera_common_s_power, }; static int imx174_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { return camera_common_g_fmt(sd, &format->format); } static int imx174_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { int ret; if (format->which == V4L2_SUBDEV_FORMAT_TRY) ret = camera_common_try_fmt(sd, &format->format); else ret = camera_common_s_fmt(sd, &format->format); return ret; } static struct v4l2_subdev_pad_ops imx174_subdev_pad_ops = { .set_fmt = imx174_set_fmt, .get_fmt = imx174_get_fmt, .enum_mbus_code = camera_common_enum_mbus_code, .enum_frame_size = camera_common_enum_framesizes, .enum_frame_interval = camera_common_enum_frameintervals, }; static struct v4l2_subdev_ops imx174_subdev_ops = { .core = &imx174_subdev_core_ops, .video = &imx174_subdev_video_ops, .pad = &imx174_subdev_pad_ops, }; static struct of_device_id imx174_of_match[] = { {.compatible = "nvidia,imx174-spi", }, { }, }; MODULE_DEVICE_TABLE(of, imx174_of_match); static struct camera_common_sensor_ops imx174_common_ops = { .power_on = imx174_power_on, .power_off = imx174_power_off, .write_reg = imx174_write_reg, .read_reg = imx174_read_reg, .get_framesync = imx174_get_framesync, }; static int imx174_set_group_hold(struct imx174 *priv, s32 val) { dev_dbg(priv->dev, "%s: Not supported\n", __func__); return 0; } static int imx174_set_gain(struct imx174 *priv, s32 val) { imx174_reg reg_list[2]; int err; int i = 0; u16 gain; dev_dbg(priv->dev, "%s: val=%d\n", __func__, val); if (val < IMX174_MIN_GAIN) val = IMX174_MIN_GAIN; else if (val > IMX174_MAX_GAIN) val = IMX174_MAX_GAIN; gain = 1748 - (1748 * IMX174_MIN_GAIN / val); if (gain > 1957) gain = 1957; imx174_get_gain_reg(reg_list, gain); for (i = 0; i < ARRAY_SIZE(reg_list); i++) { err = imx174_write_reg(priv->s_data, reg_list[i].addr, reg_list[i].val); if (err) goto fail; } return 0; fail: dev_err(priv->dev, "%s: set Gain failed\n", __func__); return err; } static int imx174_set_frame_length(struct imx174 *priv, s64 val) { struct camera_common_data *s_data = priv->s_data; const struct sensor_mode_properties *mode = &s_data->sensor_props.sensor_modes[s_data->mode]; imx174_reg reg_list[2]; int err; u32 frame_length; int i = 0; frame_length = mode->signal_properties.pixel_clock.val * mode->control_properties.framerate_factor / mode->image_properties.line_length / val; priv->frame_length = frame_length; if (priv->frame_length > IMX174_MAX_FRAME_LENGTH) priv->frame_length = IMX174_MAX_FRAME_LENGTH; dev_dbg(priv->dev, "%s: val: %lld, , frame_length: %d\n", __func__, val, priv->frame_length); imx174_get_frame_length_regs(reg_list, priv->frame_length); for (i = 0; i < 2; i++) { err = imx174_write_reg(priv->s_data, reg_list[i].addr, reg_list[i].val); if (err) goto fail; } return 0; fail: dev_info(priv->dev, "%s: frame_length control error\n", __func__); return err; } static int imx174_set_coarse_time(struct imx174 *priv, s64 val) { const struct camera_common_data *s_data = priv->s_data; const struct sensor_mode_properties *mode = &s_data->sensor_props.sensor_modes[s_data->mode]; imx174_reg reg_list[2]; int err = 0; u32 coarse_time_shs; u32 reg_shs; int i = 0; coarse_time_shs = mode->signal_properties.pixel_clock.val * val / mode->image_properties.line_length / mode->control_properties.exposure_factor; if (priv->frame_length == 0) priv->frame_length = IMX174_MIN_FRAME_LENGTH; reg_shs = priv->frame_length - coarse_time_shs - 1; dev_dbg(priv->dev, "%s: coarse:%d, shs:%d, FL:%d\n", __func__, coarse_time_shs, reg_shs, priv->frame_length); imx174_get_coarse_time_regs_shs(reg_list, reg_shs); for (i = 0; i < 2; i++) { err = imx174_write_reg(priv->s_data, reg_list[i].addr, reg_list[i].val); if (err) goto fail; } return 0; fail: dev_dbg(priv->dev, "%s: set coarse time error\n", __func__); return err; } static int imx174_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct imx174 *priv = container_of(ctrl->handler, struct imx174, ctrl_handler); int err = 0; if (priv->power.state == SWITCH_OFF) return 0; switch (ctrl->id) { default: dev_err(priv->dev, "%s: unknown ctrl id.\n", __func__); return -EINVAL; } return err; } static int imx174_s_ctrl(struct v4l2_ctrl *ctrl) { struct imx174 *priv = container_of(ctrl->handler, struct imx174, ctrl_handler); int err = 0; if (priv->power.state == SWITCH_OFF) return 0; switch (ctrl->id) { case TEGRA_CAMERA_CID_GAIN: err = imx174_set_gain(priv, ctrl->val); break; case TEGRA_CAMERA_CID_FRAME_LENGTH: err = imx174_set_frame_length(priv, ctrl->val); break; case TEGRA_CAMERA_CID_COARSE_TIME: err = imx174_set_coarse_time(priv, ctrl->val); break; case TEGRA_CAMERA_CID_GROUP_HOLD: err = imx174_set_group_hold(priv, ctrl->val); break; case TEGRA_CAMERA_CID_HDR_EN: break; default: dev_err(priv->dev, "%s: unknown ctrl id.\n", __func__); return -EINVAL; } return err; } static int imx174_ctrls_init(struct imx174 *priv) { struct v4l2_ctrl *ctrl; int num_ctrls; int err; int i; dev_dbg(priv->dev, "%s++\n", __func__); num_ctrls = ARRAY_SIZE(ctrl_config_list); v4l2_ctrl_handler_init(&priv->ctrl_handler, num_ctrls); for (i = 0; i < num_ctrls; i++) { ctrl = v4l2_ctrl_new_custom(&priv->ctrl_handler, &ctrl_config_list[i], NULL); if (ctrl == NULL) { dev_err(priv->dev, "Failed to init %s ctrl\n", ctrl_config_list[i].name); continue; } if (ctrl_config_list[i].type == V4L2_CTRL_TYPE_STRING && ctrl_config_list[i].flags & V4L2_CTRL_FLAG_READ_ONLY) { ctrl->p_new.p_char = devm_kzalloc(priv->dev, ctrl_config_list[i].max + 1, GFP_KERNEL); } priv->ctrls[i] = ctrl; } priv->num_ctrls = num_ctrls; priv->subdev->ctrl_handler = &priv->ctrl_handler; if (priv->ctrl_handler.error) { dev_err(priv->dev, "Error %d adding controls\n", priv->ctrl_handler.error); err = priv->ctrl_handler.error; goto error; } err = v4l2_ctrl_handler_setup(&priv->ctrl_handler); if (err) { dev_err(priv->dev, "Error %d setting default controls\n", err); goto error; } return 0; error: v4l2_ctrl_handler_free(&priv->ctrl_handler); return err; } static struct camera_common_pdata *imx174_parse_dt(struct spi_device *spi) { struct device_node *np = spi->dev.of_node; struct camera_common_pdata *board_priv_pdata; const struct of_device_id *match; struct camera_common_pdata *ret = NULL; int gpio; match = of_match_device(imx174_of_match, &spi->dev); if (!match) { dev_err(&spi->dev, "Failed to find matching dt id\n"); return ERR_PTR(-ENODEV); } board_priv_pdata = devm_kzalloc(&spi->dev, sizeof(*board_priv_pdata), GFP_KERNEL); if (!board_priv_pdata) { dev_err(&spi->dev, "Failed to allocate pdata\n"); return ERR_PTR(-ENODEV); } of_property_read_string(np, "mclk", &board_priv_pdata->mclk_name); of_property_read_string(np, "avdd-reg", &board_priv_pdata->regulators.avdd); of_property_read_string(np, "dvdd-reg", &board_priv_pdata->regulators.dvdd); of_property_read_string(np, "iovdd-reg", &board_priv_pdata->regulators.iovdd); gpio = of_get_named_gpio(np, "reset-gpios", 0); if (gpio < 0) { /* reset-gpio is not absolutely needed */ if (gpio == -EPROBE_DEFER) { ret = ERR_PTR(-EPROBE_DEFER); goto error; } dev_dbg(&spi->dev, "reset gpios not in DT\n"); gpio = 0; } board_priv_pdata->reset_gpio = (unsigned int)gpio; return board_priv_pdata; error: devm_kfree(&spi->dev, board_priv_pdata); return ret; } static int imx174_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { dev_dbg(sd->dev, "%s:\n", __func__); return 0; } static const struct v4l2_subdev_internal_ops imx174_subdev_internal_ops = { .open = imx174_open, }; static const struct media_entity_operations imx174_media_ops = { .link_validate = v4l2_subdev_link_validate, }; static int imx174_probe(struct spi_device *spi) { struct camera_common_data *common_data; struct imx174 *priv; struct device_node *node = spi->dev.of_node; int err; int ret = 0; pr_info("[IMX174]: probing v4l2 sensor.\n"); if (!IS_ENABLED(CONFIG_OF) || !node) return -EINVAL; common_data = devm_kzalloc(&spi->dev, sizeof(struct camera_common_data), GFP_KERNEL); if (!common_data) { dev_err(&spi->dev, "unable to allocate memory!\n"); return -ENOMEM; } priv = devm_kzalloc(&spi->dev, sizeof(struct imx174) + sizeof(struct v4l2_ctrl *) * ARRAY_SIZE(ctrl_config_list), GFP_KERNEL); if (!priv) { dev_err(&spi->dev, "unable to allocate memory!\n"); return -ENOMEM; } spi->mode = SPI_MODE_3 | SPI_LSB_FIRST; spi->bits_per_word = 8; spi->irq = -1; err = spi_setup(spi); if (err) { dev_err(&spi->dev, "unable to setup SPI!\n"); return err; } priv->regmap = devm_regmap_init_spi(spi, &sensor_regmap_config); if (IS_ERR(priv->regmap)) { dev_err(&spi->dev, "regmap init failed: %ld\n", PTR_ERR(priv->regmap)); return -ENODEV; } priv->pdata = imx174_parse_dt(spi); if (!priv->pdata) { dev_err(&spi->dev, "unable to get platform data\n"); return -EFAULT; } common_data->ops = &imx174_common_ops; common_data->ctrl_handler = &priv->ctrl_handler; common_data->dev = &spi->dev; common_data->frmfmt = &imx174_frmfmt[0]; common_data->colorfmt = camera_common_find_datafmt( IMX174_DEFAULT_DATAFMT); common_data->power = &priv->power; common_data->ctrls = priv->ctrls; common_data->priv = (void *)priv; common_data->numctrls = ARRAY_SIZE(ctrl_config_list); common_data->numfmts = ARRAY_SIZE(imx174_frmfmt); common_data->def_mode = IMX174_DEFAULT_MODE; common_data->def_width = IMX174_DEFAULT_WIDTH; common_data->def_height = IMX174_DEFAULT_HEIGHT; common_data->fmt_width = common_data->def_width; common_data->fmt_height = common_data->def_height; common_data->def_clk_freq = IMX174_DEFAULT_CLK_FREQ; priv->dev = &spi->dev; priv->s_data = common_data; priv->subdev = &common_data->subdev; priv->subdev->dev = &spi->dev; priv->s_data->dev = &spi->dev; priv->spi = spi; err = imx174_power_get(priv); if (err) return err; err = camera_common_initialize(common_data, "imx174"); if (err) return err; v4l2_spi_subdev_init(&common_data->subdev, spi, &imx174_subdev_ops); err = imx174_ctrls_init(priv); if (err) return err; priv->subdev->internal_ops = &imx174_subdev_internal_ops; priv->subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; #if defined(CONFIG_MEDIA_CONTROLLER) priv->pad.flags = MEDIA_PAD_FL_SOURCE; priv->subdev->entity.ops = &imx174_media_ops; err = tegra_media_entity_init(&priv->subdev->entity, 1, &priv->pad, true, true); if (err < 0) { dev_err(&spi->dev, "unable to init media entity\n"); return err; } #endif err = v4l2_async_register_subdev(priv->subdev); if (err) return err; pr_info("[IMX174]: probing v4l2 sensor done\n"); return ret; } static int imx174_remove(struct spi_device *spi) { struct camera_common_data *s_data = to_camera_common_data(&spi->dev); struct imx174 *priv = (struct imx174 *)s_data->priv; v4l2_async_unregister_subdev(priv->subdev); #if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&priv->subdev->entity); #endif v4l2_ctrl_handler_free(&priv->ctrl_handler); imx174_power_put(priv); camera_common_cleanup(s_data); return 0; } static const struct spi_device_id imx174_id[] = { { "imx174", 0 }, { } }; MODULE_DEVICE_TABLE(spi, imx174_id); static struct spi_driver imx174_spi_driver = { .driver = { .name = "imx174-spi", .owner = THIS_MODULE, .of_match_table = imx174_of_match, }, .probe = imx174_probe, .remove = imx174_remove, .id_table = imx174_id, }; module_spi_driver(imx174_spi_driver); MODULE_DESCRIPTION("SPI driver for Sony IMX174"); MODULE_AUTHOR("Frank Chen"); MODULE_LICENSE("GPL v2");