Re: [PATCH v9 2/2] Add support for OV5647 sensor.
From: Vladimir Zapolskiy <hidden>
Date: 2017-02-21 15:54:35
Also in:
linux-media, lkml
Hi Ramiro, please find some review comments below. On 02/17/2017 03:14 PM, Ramiro Oliveira wrote:
The OV5647 sensor from Omnivision supports up to 2592x1944 @ 15 fps, RAW 8 and RAW 10 output formats, and MIPI CSI-2 interface. The driver adds support for 640x480 RAW 8. Signed-off-by: Ramiro Oliveira <roliveir-HKixBCOQz3hWk0Htik3J/w@public.gmane.org> ---
[snip]
+
+struct ov5647 {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct mutex lock;
+ struct v4l2_mbus_framefmt format;
+ unsigned int width;
+ unsigned int height;
+ int power_count;
+ struct clk *xclk;
+ /* External clock frequency currently supported is 30MHz */
+ u32 xclk_freq;See a comment about 25MHz vs 30MHz below. Also I assume you can remove 'xclk_freq' from the struct fields, it can be replaced by a local variable.
+};
[snip]
+
+static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val)
+{
+ int ret;
+ unsigned char data_w[2] = { reg >> 8, reg & 0xff };
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ ret = i2c_master_send(client, data_w, 2);
+ if (ret < 0) {
+ dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",s/i2c read error/i2c write error/
+ __func__, reg); + return ret; + } + + ret = i2c_master_recv(client, val, 1); + if (ret < 0) + dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + + return ret; +
Please remove the empty line above.
+}
+
+static int ov5647_write_array(struct v4l2_subdev *sd,
+ struct regval_list *regs, int array_size)
+{
+ int i = 0, ret;Assignment of 'i' on declaration is not needed, please remove.
+
+ for (i = 0; i < array_size; i++) {
+ ret = ov5647_write(sd, regs[i].addr, regs[i].data);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel)
+{
+ u8 channel_id;
+ int ret;
+
+ ret = ov5647_read(sd, 0x4814, &channel_id);
+ if (ret < 0)
+ return ret;
+
+ channel_id &= ~(3 << 6);
+ return ov5647_write(sd, 0x4814, channel_id | (channel << 6));
+}
+
+static int ov5647_stream_on(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ ov5647_write(sd, 0x4202, 0x00);Should you add a check of the returned value?
+ + dev_dbg(&client->dev, "Stream on");
I would suggest to remove dev_dbg(), because ftrace will report to a user, when this function is called. Also dev_dbg() in the middle of two I2C transfers in a row looks as being placed improperly.
+
+ return ov5647_write(sd, 0x300D, 0x00);
+}
+
+static int ov5647_stream_off(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ ov5647_write(sd, 0x4202, 0x0f);Should you add a check of the returned value?
+ + dev_dbg(&client->dev, "Stream off");
I would suggest to remove dev_dbg(), because ftrace will report to a user, when this function is called. Also dev_dbg() in the middle of two I2C transfers in a row looks as being placed improperly.
+
+ return ov5647_write(sd, 0x300D, 0x01);
+}
+
+static int set_sw_standby(struct v4l2_subdev *sd, bool standby)
+{
+ int ret;
+ u8 rdval;
+
+ ret = ov5647_read(sd, 0x0100, &rdval);
+ if (ret < 0)
+ return ret;
+
+ if (standby)
+ rdval &= ~0x01;
+ else
+ rdval |= 0x01;
+
+ return ov5647_write(sd, 0x0100, rdval);
+}
+
+static int __sensor_init(struct v4l2_subdev *sd)
+{
+ int ret;
+ u8 resetval;
+ u8 rdval;It could be possible to put declarations of 'resetval' and 'rdval' on the same line.
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ dev_dbg(&client->dev, "sensor init\n");
+
+ ret = ov5647_read(sd, 0x0100, &rdval);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5647_write_array(sd, ov5647_640x480,
+ ARRAY_SIZE(ov5647_640x480));
+ if (ret < 0) {
+ dev_err(&client->dev, "write sensor default regs error\n");
+ return ret;
+ }
+
+ ret = ov5647_set_virtual_channel(sd, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5647_read(sd, 0x0100, &resetval);
+ if (ret < 0)
+ return ret;
+
+ if (!(resetval & 0x01)) {
+ dev_err(&client->dev, "Device was in SW standby");
+ ret = ov5647_write(sd, 0x0100, 0x01);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ov5647_write(sd, 0x4800, 0x04);
+}
+
+static int sensor_power(struct v4l2_subdev *sd, int on)
+{
+ int ret;
+ struct ov5647 *ov5647 = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ ret = 0;
+ mutex_lock(&ov5647->lock);
+
+ if (on && !ov5647->power_count) {
+ dev_dbg(&client->dev, "OV5647 power on\n");
+
+ clk_set_rate(ov5647->xclk, ov5647->xclk_freq);Now clk_set_rate() is redundant, please remove it. If once it is needed again, please move it to the .probe function, so it is called only once in the runtime.
+ + ret = clk_prepare_enable(ov5647->xclk);
I wonder would it be possible to unload the driver or to unbind the device and leave the clock unintentionally enabled? If yes, then this is a bug.
+ if (ret < 0) {
+ dev_err(&client->dev, "clk prepare enable failed\n");
+ goto out;
+ }
+
+ ret = ov5647_write_array(sd, sensor_oe_enable_regs,
+ ARRAY_SIZE(sensor_oe_enable_regs));
+ if (ret < 0) {
+ clk_disable_unprepare(ov5647->xclk);
+ dev_err(&client->dev,
+ "write sensor_oe_enable_regs error\n");
+ goto out;
+ }
+
+ ret = __sensor_init(sd);
+ if (ret < 0) {
+ clk_disable_unprepare(ov5647->xclk);
+ dev_err(&client->dev,
+ "Camera not available, check Power\n");
+ goto out;
+ }
+ } else if (!on && ov5647->power_count == 1) {
+ dev_dbg(&client->dev, "OV5647 power off\n");
+
+ dev_dbg(&client->dev, "disable oe\n");One of two dev_dbg()'s above is apparently redundant.
+ ret = ov5647_write_array(sd, sensor_oe_disable_regs, + ARRAY_SIZE(sensor_oe_disable_regs)); + + if (ret < 0) + dev_dbg(&client->dev, "disable oe failed\n"); + + ret = set_sw_standby(sd, true); + + if (ret < 0) + dev_dbg(&client->dev, "soft stby failed\n"); + + clk_disable_unprepare(ov5647->xclk); + } + + /* Update the power count. */ + ov5647->power_count += on ? 1 : -1; + WARN_ON(ov5647->power_count < 0); + +out: + mutex_unlock(&ov5647->lock); + + return ret; +} +
[snip]
+
+static int ov5647_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ov5647 *sensor;
+ int ret;
+ struct v4l2_subdev *sd;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (sensor == NULL)if (!sensor) is a bit shorter.
+ return -ENOMEM;
+
+ /* get system clock (xclk) */
+ sensor->xclk = devm_clk_get(dev, "xclk");
+ if (IS_ERR(sensor->xclk)) {
+ dev_err(dev, "could not get xclk");
+ return PTR_ERR(sensor->xclk);
+ }
+
+ sensor->xclk_freq = clk_get_rate(sensor->xclk);
+ if (sensor->xclk_freq != 25000000) {A comment in "struct ov5647" declaration says about 30MHz, which one is correct?
+ dev_err(dev, "Unsupported clock frequency: %u\n", + sensor->xclk_freq); + return -EINVAL; + } + + mutex_init(&sensor->lock); + + sd = &sensor->sd; + v4l2_i2c_subdev_init(sd, client, &subdev_ops); + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); + if (ret < 0) + goto mutex_remove; + + ret = ov5647_detect(sd); + if (ret < 0) + goto error; + + ret = v4l2_async_register_subdev(sd); + if (ret < 0) + goto error; + + dev_dbg(&client->dev, "OmniVision OV5647 camera driver probed\n"); + return 0; +error: + media_entity_cleanup(&sd->entity); +mutex_remove: + mutex_destroy(&sensor->lock); + return ret; +} +
[snip] The driver looks good in general IMO. -- With best wishes, Vladimir -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html