diff options
author | Hu Kejun <william.hu@rock-chips.com> | 2018-04-09 16:11:37 +0800 |
---|---|---|
committer | Tao Huang <huangtao@rock-chips.com> | 2018-04-11 18:30:37 +0800 |
commit | 87dfc62bcb2c4fcd69ddff00b3246814029ed2af (patch) | |
tree | be19e133d4f9aa10cf36e9ecb6ecc136acbc324b | |
parent | 440f7f5fde341a2a40bb508588279dea068fc617 (diff) |
media: i2c: modify ov5695 driver for rkisp1
Change-Id: I31e73224120dcf67d65c833d767f55200a8ed6cb
Signed-off-by: Hu Kejun <william.hu@rock-chips.com>
-rw-r--r-- | drivers/media/i2c/ov5695.c | 334 |
1 files changed, 178 insertions, 156 deletions
diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index f5121737604e..43c3d8f7c732 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ov5695 driver * * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/clk.h> @@ -29,6 +25,8 @@ /* 45Mhz * 4 Binning */ #define OV5695_PIXEL_RATE (45 * 1000 * 1000 * 4) +#define OV5695_XVCLK_FREQ 24000000 + #define CHIP_ID 0x005695 #define OV5695_REG_CHIP_ID 0x300a @@ -71,6 +69,14 @@ #define OV5695_LANES 2 #define OV5695_BITS_PER_SAMPLE 10 +static const char * const ov5695_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV5695_NUM_SUPPLIES ARRAY_SIZE(ov5695_supply_names) + struct regval { u16 addr; u8 val; @@ -89,10 +95,9 @@ struct ov5695_mode { struct ov5695 { struct i2c_client *client; struct clk *xvclk; - struct regulator *avdd_regulator; - struct regulator *dovdd_regulator; - struct regulator *dvdd_regulator; struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV5695_NUM_SUPPLIES]; struct v4l2_subdev subdev; struct media_pad pad; @@ -107,6 +112,7 @@ struct ov5695 { bool streaming; const struct ov5695_mode *cur_mode; }; + #define to_ov5695(sd) container_of(sd, struct ov5695, subdev) /* @@ -137,8 +143,8 @@ static const struct regval ov5695_global_regs[] = { {0x0312, 0x01}, {0x3000, 0x00}, {0x3002, 0xa1}, - {0x0308, 0x00}, - {0x0310, 0x00}, + {0x3008, 0x00}, + {0x3010, 0x00}, {0x3022, 0x51}, {0x3106, 0x15}, {0x3107, 0x01}, @@ -395,7 +401,9 @@ static const struct regval ov5695_1296x972_regs[] = { {0x030f, 0x03}, {0x0312, 0x01}, {0x3000, 0x00}, - {0x3002, 0x21}, + {0x3002, 0xa1}, + {0x3008, 0x00}, + {0x3010, 0x00}, {0x3016, 0x32}, {0x3022, 0x51}, {0x3106, 0x15}, @@ -696,10 +704,9 @@ static const char * const ov5695_test_pattern_menu[] = { /* Write registers up to 4 at a time */ static int ov5695_write_reg(struct i2c_client *client, u16 reg, - unsigned int len, u32 val) + u32 len, u32 val) { - int buf_i; - int val_i; + u32 buf_i, val_i; u8 buf[6]; u8 *val_p; __be32 val_be; @@ -727,7 +734,8 @@ static int ov5695_write_reg(struct i2c_client *client, u16 reg, static int ov5695_write_array(struct i2c_client *client, const struct regval *regs) { - int i, ret = 0; + u32 i; + int ret = 0; for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) ret = ov5695_write_reg(client, regs[i].addr, @@ -746,7 +754,7 @@ static int ov5695_read_reg(struct i2c_client *client, u16 reg, unsigned int len, __be16 reg_addr_be = cpu_to_be16(reg); int ret; - if (len > 4) + if (len > 4 || !len) return -EINVAL; data_be_p = (u8 *)&data_be; @@ -778,8 +786,8 @@ static int ov5695_get_reso_dist(const struct ov5695_mode *mode, abs(mode->height - framefmt->height); } -static const struct ov5695_mode *ov5695_find_best_fit( - struct v4l2_subdev_format *fmt) +static const struct ov5695_mode * +ov5695_find_best_fit(struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *framefmt = &fmt->format; int dist; @@ -832,6 +840,7 @@ static int ov5695_set_fmt(struct v4l2_subdev *sd, } mutex_unlock(&ov5695->mutex); + return 0; } @@ -857,6 +866,7 @@ static int ov5695_get_fmt(struct v4l2_subdev *sd, fmt->format.field = V4L2_FIELD_NONE; } mutex_unlock(&ov5695->mutex); + return 0; } @@ -875,7 +885,7 @@ static int ov5695_enum_frame_sizes(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index > ARRAY_SIZE(supported_modes)) + if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) @@ -902,36 +912,6 @@ static int ov5695_enable_test_pattern(struct ov5695 *ov5695, u32 pattern) OV5695_REG_VALUE_08BIT, val); } -static void ov5695_set_exposure(struct ov5695 *ov5695, s32 val) -{ - /* 4 least significant bits of expsoure are fractional part */ - ov5695_write_reg(ov5695->client, OV5695_REG_EXPOSURE, - OV5695_REG_VALUE_24BIT, val << 4); -} - -static void ov5695_set_analog_gain(struct ov5695 *ov5695, s32 val) -{ - ov5695_write_reg(ov5695->client, OV5695_REG_ANALOG_GAIN, - OV5695_REG_VALUE_08BIT, val); -} - -static void ov5695_set_digi_gain(struct ov5695 *ov5695, s32 val) -{ - ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L, - OV5695_REG_VALUE_08BIT, val & OV5695_DIGI_GAIN_L_MASK); - val >>= OV5695_DIGI_GAIN_H_SHIFT; - ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H, - OV5695_REG_VALUE_08BIT, val); -} - -static void ov5695_set_vts(struct ov5695 *ov5695, s32 val) -{ - val += ov5695->cur_mode->height; - - ov5695_write_reg(ov5695->client, OV5695_REG_VTS, - OV5695_REG_VALUE_16BIT, val); -} - static int __ov5695_start_stream(struct ov5695 *ov5695) { int ret; @@ -944,15 +924,14 @@ static int __ov5695_start_stream(struct ov5695 *ov5695) return ret; /* In case these controls are set before streaming */ - ov5695_set_exposure(ov5695, ov5695->exposure->val); - ov5695_set_analog_gain(ov5695, ov5695->anal_gain->val); - ov5695_set_digi_gain(ov5695, ov5695->digi_gain->val); - ov5695_set_vts(ov5695, ov5695->vblank->val); - ov5695_enable_test_pattern(ov5695, ov5695->test_pattern->val); - - ret = ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE, - OV5695_REG_VALUE_08BIT, OV5695_MODE_STREAMING); - return ret; + mutex_unlock(&ov5695->mutex); + ret = v4l2_ctrl_handler_setup(&ov5695->ctrl_handler); + mutex_lock(&ov5695->mutex); + if (ret) + return ret; + + return ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE, + OV5695_REG_VALUE_08BIT, OV5695_MODE_STREAMING); } static int __ov5695_stop_stream(struct ov5695 *ov5695) @@ -974,18 +953,19 @@ static int ov5695_s_stream(struct v4l2_subdev *sd, int on) if (on) { ret = pm_runtime_get_sync(&client->dev); - if (ret) + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); goto unlock_and_return; + } ret = __ov5695_start_stream(ov5695); if (ret) { v4l2_err(sd, "start stream failed while write regs\n"); pm_runtime_put(&client->dev); + goto unlock_and_return; } } else { - ret = __ov5695_stop_stream(ov5695); - if (ret) - goto unlock_and_return; + __ov5695_stop_stream(ov5695); pm_runtime_put(&client->dev); } @@ -993,12 +973,20 @@ static int ov5695_s_stream(struct v4l2_subdev *sd, int on) unlock_and_return: mutex_unlock(&ov5695->mutex); + return ret; } +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov5695_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV5695_XVCLK_FREQ / 1000 / 1000); +} + static int __ov5695_power_on(struct ov5695 *ov5695) { int ret; + u32 delay_us; struct device *dev = &ov5695->client->dev; ret = clk_prepare_enable(ov5695->xvclk); @@ -1007,36 +995,27 @@ static int __ov5695_power_on(struct ov5695 *ov5695) return ret; } - gpiod_set_value_cansleep(ov5695->reset_gpio, 1); + if (!IS_ERR(ov5695->reset_gpio)) + gpiod_set_value_cansleep(ov5695->reset_gpio, 1); - /* AVDD and DOVDD may rise in any order */ - ret = regulator_enable(ov5695->avdd_regulator); + ret = regulator_bulk_enable(OV5695_NUM_SUPPLIES, ov5695->supplies); if (ret < 0) { - dev_err(dev, "Failed to enable regulator\n"); + dev_err(dev, "Failed to enable regulators\n"); goto disable_clk; } - ret = regulator_enable(ov5695->dovdd_regulator); - if (ret < 0) { - dev_err(dev, "Failed to enable DOVDD regulator\n"); - goto disable_avdd; - } - /* DVDD must rise after AVDD and DOVDD */ - ret = regulator_enable(ov5695->dvdd_regulator); - if (ret < 0) { - dev_err(dev, "Failed to enable DVDD regulator\n"); - goto disable_dovdd; - } - gpiod_set_value_cansleep(ov5695->reset_gpio, 0); + if (!IS_ERR(ov5695->reset_gpio)) + gpiod_set_value_cansleep(ov5695->reset_gpio, 0); + + if (!IS_ERR(ov5695->pwdn_gpio)) + gpiod_set_value_cansleep(ov5695->pwdn_gpio, 1); + /* 8192 cycles prior to first SCCB transaction */ - usleep_range(1000, 1500); + delay_us = ov5695_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); return 0; -disable_dovdd: - regulator_disable(ov5695->dovdd_regulator); -disable_avdd: - regulator_disable(ov5695->avdd_regulator); disable_clk: clk_disable_unprepare(ov5695->xvclk); @@ -1045,11 +1024,12 @@ disable_clk: static void __ov5695_power_off(struct ov5695 *ov5695) { + if (!IS_ERR(ov5695->pwdn_gpio)) + gpiod_set_value_cansleep(ov5695->pwdn_gpio, 0); clk_disable_unprepare(ov5695->xvclk); - gpiod_set_value_cansleep(ov5695->reset_gpio, 1); - regulator_disable(ov5695->dvdd_regulator); - regulator_disable(ov5695->dovdd_regulator); - regulator_disable(ov5695->avdd_regulator); + if (!IS_ERR(ov5695->reset_gpio)) + gpiod_set_value_cansleep(ov5695->reset_gpio, 1); + regulator_bulk_disable(OV5695_NUM_SUPPLIES, ov5695->supplies); } static int ov5695_runtime_resume(struct device *dev) @@ -1057,19 +1037,8 @@ static int ov5695_runtime_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov5695 *ov5695 = to_ov5695(sd); - int ret; - ret = __ov5695_power_on(ov5695); - if (ret) - return ret; - - if (ov5695->streaming) { - ret = __ov5695_start_stream(ov5695); - if (ret) - __ov5695_power_off(ov5695); - } - - return ret; + return __ov5695_power_on(ov5695); } static int ov5695_runtime_suspend(struct device *dev) @@ -1089,16 +1058,18 @@ static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) struct ov5695 *ov5695 = to_ov5695(sd); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov5695_mode *def_mode = &supported_modes[0]; mutex_lock(&ov5695->mutex); /* Initialize try_fmt */ - try_fmt->width = ov5695->cur_mode->width; - try_fmt->height = ov5695->cur_mode->height; + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; try_fmt->field = V4L2_FIELD_NONE; mutex_unlock(&ov5695->mutex); /* No crop or compose */ + return 0; } #endif @@ -1114,18 +1085,18 @@ static const struct v4l2_subdev_internal_ops ov5695_internal_ops = { }; #endif -static struct v4l2_subdev_video_ops ov5695_video_ops = { +static const struct v4l2_subdev_video_ops ov5695_video_ops = { .s_stream = ov5695_s_stream, }; -static struct v4l2_subdev_pad_ops ov5695_pad_ops = { +static const struct v4l2_subdev_pad_ops ov5695_pad_ops = { .enum_mbus_code = ov5695_enum_mbus_code, .enum_frame_size = ov5695_enum_frame_sizes, .get_fmt = ov5695_get_fmt, .set_fmt = ov5695_set_fmt, }; -static struct v4l2_subdev_ops ov5695_subdev_ops = { +static const struct v4l2_subdev_ops ov5695_subdev_ops = { .video = &ov5695_video_ops, .pad = &ov5695_pad_ops, }; @@ -1150,19 +1121,31 @@ static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_get_sync(&client->dev); + if (pm_runtime_get(&client->dev) <= 0) + return 0; + switch (ctrl->id) { case V4L2_CID_EXPOSURE: - ov5695_set_exposure(ov5695, ctrl->val); + /* 4 least significant bits of expsoure are fractional part */ + ret = ov5695_write_reg(ov5695->client, OV5695_REG_EXPOSURE, + OV5695_REG_VALUE_24BIT, ctrl->val << 4); break; case V4L2_CID_ANALOGUE_GAIN: - ov5695_set_analog_gain(ov5695, ctrl->val); + ret = ov5695_write_reg(ov5695->client, OV5695_REG_ANALOG_GAIN, + OV5695_REG_VALUE_08BIT, ctrl->val); break; case V4L2_CID_DIGITAL_GAIN: - ov5695_set_digi_gain(ov5695, ctrl->val); + ret = ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L, + OV5695_REG_VALUE_08BIT, + ctrl->val & OV5695_DIGI_GAIN_L_MASK); + ret |= ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H, + OV5695_REG_VALUE_08BIT, + ctrl->val >> OV5695_DIGI_GAIN_H_SHIFT); break; case V4L2_CID_VBLANK: - ov5695_set_vts(ov5695, ctrl->val); + ret = ov5695_write_reg(ov5695->client, OV5695_REG_VTS, + OV5695_REG_VALUE_16BIT, + ctrl->val + ov5695->cur_mode->height); break; case V4L2_CID_TEST_PATTERN: ret = ov5695_enable_test_pattern(ov5695, ctrl->val); @@ -1171,7 +1154,8 @@ static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl) dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", __func__, ctrl->id, ctrl->val); break; - }; + } + pm_runtime_put(&client->dev); return ret; @@ -1192,7 +1176,7 @@ static int ov5695_initialize_controls(struct ov5695 *ov5695) handler = &ov5695->ctrl_handler; mode = ov5695->cur_mode; - ret = v4l2_ctrl_handler_init(handler, 1); + ret = v4l2_ctrl_handler_init(handler, 8); if (ret) return ret; handler->lock = &ov5695->mutex; @@ -1240,32 +1224,34 @@ static int ov5695_initialize_controls(struct ov5695 *ov5695) 0, 0, ov5695_test_pattern_menu); if (handler->error) { - v4l2_ctrl_handler_free(handler); - return handler->error; + ret = handler->error; + dev_err(&ov5695->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; } ov5695->subdev.ctrl_handler = handler; return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; } static int ov5695_check_sensor_id(struct ov5695 *ov5695, struct i2c_client *client) { struct device *dev = &ov5695->client->dev; - u32 id; + u32 id = 0; int ret; - ret = __ov5695_power_on(ov5695); - if (ret) - return ret; - ov5695_read_reg(client, OV5695_REG_CHIP_ID, - OV5695_REG_VALUE_24BIT, &id); - __ov5695_power_off(ov5695); - + ret = ov5695_read_reg(client, OV5695_REG_CHIP_ID, + OV5695_REG_VALUE_24BIT, &id); if (id != CHIP_ID) { - dev_err(dev, "Wrong camera sensor id(%06x)\n", id); - return -EINVAL; + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return ret; } dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); @@ -1273,6 +1259,18 @@ static int ov5695_check_sensor_id(struct ov5695 *ov5695, return 0; } +static int ov5695_configure_regulators(struct ov5695 *ov5695) +{ + int i; + + for (i = 0; i < OV5695_NUM_SUPPLIES; i++) + ov5695->supplies[i].supply = ov5695_supply_names[i]; + + return devm_regulator_bulk_get(&ov5695->client->dev, + OV5695_NUM_SUPPLIES, + ov5695->supplies); +} + static int ov5695_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1293,101 +1291,125 @@ static int ov5695_probe(struct i2c_client *client, dev_err(dev, "Failed to get xvclk\n"); return -EINVAL; } - ret = clk_set_rate(ov5695->xvclk, 24000000); + ret = clk_set_rate(ov5695->xvclk, OV5695_XVCLK_FREQ); if (ret < 0) { - dev_err(dev, "Failed to set xvclk rate (24M)\n"); + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); return ret; } + if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov5695->reset_gpio)) { - dev_err(dev, "Failed to get reset-gpios\n"); - return -EINVAL; + dev_warn(dev, "Failed to get reset-gpios\n"); } - ov5695->avdd_regulator = devm_regulator_get(dev, "avdd"); - if (IS_ERR(ov5695->avdd_regulator)) { - dev_err(dev, "Failed to get avdd-supply\n"); - return -EINVAL; - } - ov5695->dovdd_regulator = devm_regulator_get(dev, "dovdd"); - if (IS_ERR(ov5695->dovdd_regulator)) { - dev_err(dev, "Failed to get dovdd-supply\n"); - return -EINVAL; - } - ov5695->dvdd_regulator = devm_regulator_get(dev, "dvdd"); - if (IS_ERR(ov5695->dvdd_regulator)) { - dev_err(dev, "Failed to get dvdd-supply\n"); - return -EINVAL; + ov5695->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov5695->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = ov5695_configure_regulators(ov5695); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; } mutex_init(&ov5695->mutex); sd = &ov5695->subdev; v4l2_i2c_subdev_init(sd, client, &ov5695_subdev_ops); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - sd->internal_ops = &ov5695_internal_ops; -#endif - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - ret = ov5695_initialize_controls(ov5695); if (ret) - return ret; + goto err_destroy_mutex; + + ret = __ov5695_power_on(ov5695); + if (ret) + goto err_free_handler; + ret = ov5695_check_sensor_id(ov5695, client); if (ret) - return ret; + goto err_power_off; +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov5695_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif #if defined(CONFIG_MEDIA_CONTROLLER) ov5695->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ret = media_entity_init(&sd->entity, 1, &ov5695->pad, 0); if (ret < 0) - return ret; + goto err_power_off; #endif ret = v4l2_async_register_subdev(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failed\n"); - goto clean_entity; + goto err_clean_entity; } + pm_runtime_set_active(dev); pm_runtime_enable(dev); - dev_info(dev, "Probe successfully\n"); + pm_runtime_idle(dev); return 0; -clean_entity: +err_clean_entity: #if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); #endif +err_power_off: + __ov5695_power_off(ov5695); +err_free_handler: + v4l2_ctrl_handler_free(&ov5695->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov5695->mutex); return ret; } static int ov5695_remove(struct i2c_client *client) { - struct ov5695 *ov5695 = i2c_get_clientdata(client); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5695 *ov5695 = to_ov5695(sd); - v4l2_async_unregister_subdev(&ov5695->subdev); + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov5695->ctrl_handler); + mutex_destroy(&ov5695->mutex); pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov5695_power_off(ov5695); + pm_runtime_set_suspended(&client->dev); + return 0; } +#if IS_ENABLED(CONFIG_OF) static const struct of_device_id ov5695_of_match[] = { { .compatible = "ovti,ov5695" }, {}, }; +MODULE_DEVICE_TABLE(of, ov5695_of_match); +#endif + +static const struct i2c_device_id ov5695_match_id[] = { + { "ovti,ov5695", 0 }, + { }, +}; static struct i2c_driver ov5695_i2c_driver = { .driver = { .name = "ov5695", - .owner = THIS_MODULE, .pm = &ov5695_pm_ops, - .of_match_table = ov5695_of_match + .of_match_table = of_match_ptr(ov5695_of_match), }, .probe = &ov5695_probe, .remove = &ov5695_remove, + .id_table = ov5695_match_id, }; module_i2c_driver(ov5695_i2c_driver); |