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]
quoted hunk ↗ jump to hunk
+ +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]
quoted hunk ↗ jump to hunk
+ +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/
quoted hunk ↗ jump to hunk
+ __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.
quoted hunk ↗ jump to hunk
+} + +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.
quoted hunk ↗ jump to hunk
+ + 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.
quoted hunk ↗ jump to hunk
+ + 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.
quoted hunk ↗ jump to hunk
+ + 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.
quoted hunk ↗ jump to hunk
+ 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.
quoted hunk ↗ jump to hunk
+ 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.
quoted hunk ↗ jump to hunk
+ 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 hunk ↗ jump to hunk
+ +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.
quoted hunk ↗ jump to hunk
+ 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?
quoted hunk ↗ jump to hunk
+ 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