Re: [PATCH v9 2/2] Add support for OV5647 sensor.
From: Ramiro Oliveira <hidden>
Date: 2017-02-21 16:42:30
Also in:
linux-media, lkml
Hi Vladimir, Thank you for your feedback On 2/21/2017 3:54 PM, Vladimir Zapolskiy wrote:
Hi Ramiro, please find some review comments below. On 02/17/2017 03:14 PM, Ramiro Oliveira wrote:quoted
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 <redacted> ---[snip]quoted
+ +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.
I'll do that.
quoted
+};[snip]quoted
+ +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/
I'm not sure I understand what you mean.
quoted
+ __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.
Ok.
quoted
+} + +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.
Ok.
quoted
+ + 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?
I'll add it.
quoted
+ + 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.
I'll remove it.
quoted
+ + 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?
I'll add it.
quoted
+ + 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.
I'll remove it.
quoted
+ + 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.
Sure.
quoted
+ 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.
Ok. I'll remove it for now.
quoted
+ + 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.
You're saying that if the driver was unloaded and the clock was left enabled when the driver was loaded again this line would cause an error? Should I disable the clock when the driver is removed?
quoted
+ 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.
I'll remove one.
quoted
+ 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]quoted
+ +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.
I'll change it.
quoted
+ 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?
25 MHz is the correct one.
quoted
+ 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
-- Best Regards Ramiro Oliveira Ramiro.Oliveira@synopsys.com