Re: [PATCH 2/2] media: i2c: Add driver for On Semiconductor MT9M114 camera sensor

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Laurent,

On Wed, May 13, 2020 at 03:36:40PM +0300, Laurent Pinchart wrote:
> Hi Sakari,
> 
> On Wed, May 13, 2020 at 02:53:56PM +0300, Sakari Ailus wrote:
> > On Wed, May 13, 2020 at 03:00:27AM +0300, Laurent Pinchart wrote:
> > > On Wed, May 13, 2020 at 01:55:29AM +0300, Sakari Ailus wrote:
> > > > On Tue, May 12, 2020 at 02:34:56AM +0300, Laurent Pinchart wrote:
> > > > > The MT9M114 is a CMOS camera sensor that combines a 1296x976 pixel array
> > > > > with a 10-bit dynamic range together with an internal ISP. The driver
> > > > > exposes two subdevs, one for the pixel array and one for the ISP (named
> > > > > IFP for Image Flow Processor). Major supported features are
> > > > > 
> > > > > - Full configuration of analog crop and binning in the pixel array
> > > > > - Full configuration of scaling in the ISP
> > > > > - Automatic exposure and white balance
> > > > > - Manual exposure and analog gain
> > > > > - Horizontal and vertical flip
> > > > > 
> > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
> > > > > ---
> > > > >  MAINTAINERS                 |    1 +
> > > > >  drivers/media/i2c/Kconfig   |   10 +
> > > > >  drivers/media/i2c/Makefile  |    1 +
> > > > >  drivers/media/i2c/mt9m114.c | 2161 +++++++++++++++++++++++++++++++++++
> > > > >  4 files changed, 2173 insertions(+)
> > > > >  create mode 100644 drivers/media/i2c/mt9m114.c
> > > 
> > > [snip]
> > > 
> > > > > diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c
> > > > > new file mode 100644
> > > > > index 000000000000..7f70ae2865b8
> > > > > --- /dev/null
> > > > > +++ b/drivers/media/i2c/mt9m114.c
> > > 
> > > [snip]
> > > 
> > > > > +static int mt9m114_pa_s_ctrl(struct v4l2_ctrl *ctrl)
> > > > > +{
> > > > > +	struct mt9m114 *sensor = pa_ctrl_to_mt9m114(ctrl);
> > > > > +	int ret = 0;
> > > > > +
> > > > > +	switch (ctrl->id) {
> > > > > +	case V4L2_CID_EXPOSURE_ABSOLUTE:
> > > > > +		mt9m114_write(sensor,
> > > > > +			      MT9M114_CAM_SENSOR_CONTROL_COARSE_INTEGRATION_TIME,
> > > > > +			      ctrl->val / MT9M114_LINE_LENGTH, &ret);
> > > > 
> > > > Hmm. I'd introduce a separate control for the fine exposure time. We'll
> > > > need it in any case, and this would also allow setting just the coarse
> > > > exposure time.
> > > > 
> > > > What do you think?
> > > 
> > > I've re-read the documentation of the V4L2_CID_EXPOSURE_ABSOLUTE
> > > control, and it's documented as being expressed in multiples of 100µs.
> > 
> > It says "should". Indeed this is not the case for raw sensors. We could
> > update the documentation, I think.
> > 
> > > Clearly not a good fit here :-S The ov9650 driver suffers from the same
> > > problem. All the other sensor drievrs use V4L2_CID_EXPOSURE, whose unit
> > > is not defined. Do we need to introduce V4L2_CID_EXPOSURE_COARSE and
> > > V4L2_CID_EXPOSURE_FINE ? It would get messy with so many ways to control
> > 
> > I'm not opposed to that in principle. But what do we do with all the
> > current drivers in that case? They use V4L2_CID_EXPOSURE_ABSOLUTE.
> 
> No, all sensor drivers except one use V4L2_CID_EXPOSURE, not
> V4L2_CID_EXPOSURE_ABSOLUTE. It's thus V4L2_CID_EXPOSURE that we would
> need to document as being expressed in lines for sensors (but in that
> case we'll likely have a large number of drivers that misuse it).

Right, we had two controls for exposure indeed. I think there could be
drivers that use V4L2_CID_EXPOSURE with another unit. Worth checking
perhaps. Either way, we could add that unit to the documentation of the
V4L2_CID_EXPOSURE as a recommendation or a requirement.

> 
> > > the exposure time :-S Or should we document V4L2_CID_EXPOSURE as being
> > > expressed in lines for new drivers, and add V4L2_CID_EXPOSURE_FINE to be
> > > expressed in pixels ? What would two separate control for coarse and
> > > fine exposures bring us, compared to expressing the exposure time in
> > > pixels ?
> > 
> > It takes time to do the writes over the I²C bus. At higher frame rates it
> > become increasingly risky, and the least risk is indeed often preferred.
> 
> What do you propose then ? Adding V4L2_CID_EXPOSURE_FINE ? In addition
> to V4L2_CID_EXPOSURE ? How should V4L2_CID_EXPOSURE be documented ? And
> how should V4L2_CID_EXPOSURE_FINE be documented for that matter ?

The unit of the new control would need to be pixels.

> 
> > > > > +		mt9m114_write(sensor,
> > > > > +			      MT9M114_CAM_SENSOR_CONTROL_FINE_INTEGRATION_TIME,
> > > > > +			      ctrl->val % MT9M114_LINE_LENGTH, &ret);
> > > > > +		break;
> > > > > +
> > > > > +	case V4L2_CID_ANALOGUE_GAIN:
> > > > > +		/*
> > > > > +		 * The CAM_SENSOR_CONTROL_ANALOG_GAIN contains linear analog
> > > > > +		 * gain values that are mapped to the GLOBAL_GAIN register
> > > > > +		 * values by the sensor firmware.
> > > > > +		 */
> > > > > +		mt9m114_write(sensor, MT9M114_CAM_SENSOR_CONTROL_ANALOG_GAIN,
> > > > > +			      ctrl->val, &ret);
> > > > > +		break;
> > > > > +
> > > > > +	default:
> > > > > +		return -EINVAL;
> > > > > +	}
> > > > > +
> > > > > +	return ret;
> > > > > +}
> > > 
> > > [snip]
> > > 
> > > > > +static int mt9m114_pa_get_fmt(struct v4l2_subdev *sd,
> > > > > +			      struct v4l2_subdev_pad_config *cfg,
> > > > > +			      struct v4l2_subdev_format *fmt)
> > > > > +{
> > > > > +	struct mt9m114 *sensor = pa_to_mt9m114(sd);
> > > > > +
> > > > > +	fmt->format = *__mt9m114_pa_get_pad_format(sensor, cfg, fmt->pad,
> > > > > +						   fmt->which);
> > > > 
> > > > I believe these need to be serialised with e.g. a mutex. Same for set
> > > > below.
> > > 
> > > You're right, I'll fix that. All this is a bit inefficient though, as
> > > the ioctl are already serialized in subdev_do_ioctl_lock(), so there
> > 
> > They are not, as lock is always NULL. Drivers are still responsible for
> > doing this. This would seem to need some kind of a rework; acquiring a
> > mutex should be done to the calls independently of whether they are done
> > through IOCTLs or from other drivers.
> 
> My point is that there are two layers of locking, which isn't a nice
> implementation. Locking in subdev drivers is too complicated in general.
> Out of scope for this patch series of course.

Agreed. The locking could be moved from drivers to the framework, but it
wouldn't be a trivial task. As of now, the framework does not provide
locking for sub-device nodes.

> 
> > > would be double-locking, but that's required when the subdev operations
> > > are called within the kernel. Oh well... :-(
> > > 
> > > > > +	return 0;
> > > > > +}
> > > > > +
> > > > > +static int mt9m114_pa_set_fmt(struct v4l2_subdev *sd,
> > > > > +			      struct v4l2_subdev_pad_config *cfg,
> > > > > +			      struct v4l2_subdev_format *fmt)
> > > > > +{
> > > > > +	struct mt9m114 *sensor = pa_to_mt9m114(sd);
> > > > > +	struct v4l2_mbus_framefmt *format;
> > > > > +	struct v4l2_rect *crop;
> > > > > +	unsigned int hscale;
> > > > > +	unsigned int vscale;
> > > > > +
> > > > > +	crop = __mt9m114_pa_get_pad_crop(sensor, cfg, fmt->pad, fmt->which);
> > > > > +	format = __mt9m114_pa_get_pad_format(sensor, cfg, fmt->pad, fmt->which);
> > > > > +
> > > > > +	/* The sensor can bin horizontally and vertically. */
> > > > > +	hscale = DIV_ROUND_CLOSEST(crop->width, fmt->format.width ? : 1);
> > > > > +	vscale = DIV_ROUND_CLOSEST(crop->height, fmt->format.height ? : 1);
> > > > > +	format->width = crop->width / clamp(hscale, 1U, 2U);
> > > > > +	format->height = crop->height / clamp(vscale, 1U, 2U);
> > > > > +
> > > > > +	fmt->format = *format;
> > > > > +
> > > > > +	return 0;
> > > > > +}
> > > > > +
> > > > > +static int mt9m114_pa_get_selection(struct v4l2_subdev *sd,
> > > > > +				    struct v4l2_subdev_pad_config *cfg,
> > > > > +				    struct v4l2_subdev_selection *sel)
> > > > > +{
> > > > > +	struct mt9m114 *sensor = pa_to_mt9m114(sd);
> > > > > +
> > > > > +	switch (sel->target) {
> > > > > +	case V4L2_SEL_TGT_CROP:
> > > > > +		sel->r = *__mt9m114_pa_get_pad_crop(sensor, cfg, sel->pad,
> > > > > +						    sel->which);
> > > > > +		return 0;
> > > > > +
> > > > > +	case V4L2_SEL_TGT_CROP_DEFAULT:
> > > > > +	case V4L2_SEL_TGT_CROP_BOUNDS:
> > > > > +		sel->r.left = 0;
> > > > > +		sel->r.top = 0;
> > > > > +		sel->r.width = MT9M114_PIXEL_ARRAY_WIDTH;
> > > > > +		sel->r.height = MT9M114_PIXEL_ARRAY_HEIGHT;
> > > > > +		return 0;
> > > > > +
> > > > 
> > > > Could you add NATIVE_SIZE target?
> > > 
> > > Sure. The sensor has optical dark pixels, but I don't see a way in the
> > > datasheet to read the dark lines out (they're used by the internal ISP).
> > > I will thus set the native size as equal to the crop bounds.
> > > 
> > > The V4L2 documentation could really benefit from clarifying the native
> > > size and crop bounds targets by the way, it's awfully underspecified
> > > (and as a result I would guess that 99% of the drivers get it wrong).
> > 
> > The crop bounds are effectively the same in this case. But the crop bounds
> > (and crop) targets are used in a lot of different cases, too.
> > 
> > This is indeed a little grey area as sensor implementations differ. Those
> > black pixels may still affect timing, but some devices probably don't even
> > document them.
> 
> That doesn't tell me how you think the crop bounds and native size
> should be defined when there are dark pixels :-) We can discuss it
> separately, but I think it requires a discussion.

Right now, I guess I'd just use what the sensor transmits on the bus, and
ignore what's inside. We don't have a way to report the rest to the user
space anyway, and that generally would only have a very slight impact on
timing.

> 
> > ...
> > 
> > > > > +static int mt9m114_parse_dt(struct mt9m114 *sensor)
> > > > > +{
> > > > > +	struct fwnode_handle *fwnode = dev_fwnode(&sensor->client->dev);
> > > > > +	struct fwnode_handle *ep;
> > > > > +	int ret;
> > > > > +
> > > > > +	if (!fwnode)
> > > > > +		return -ENXIO;
> > > > > +
> > > > > +	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
> > > > > +	if (!ep) {
> > > > > +		dev_err(&sensor->client->dev, "No endpoint found\n");
> > > > > +		return -EINVAL;
> > > > > +	}
> > > > > +
> > > > > +	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg);
> > > > 
> > > > Please initialise the bus type.
> > > 
> > > Are you fine initializing it to V4L2_MBUS_UNKNOWN ? I don't want to loop
> > > over v4l2_fwnode_endpoint_alloc_parse() for all supported bus types.
> > 
> > Feel free to propose alternatives. Either way, that is probably the most
> > simple thing you can do in a driver.
> > 
> > We don't want to add DT properties just to cover deficiencies in driver
> > implementation.
> 
> The implementation here doesn't depend on additional DT properties as
> far as I can tell. I thus don't see the problem with the current code.

Feel free to keep it as-is if it works with the redundant DT properties
removed.

...

> > > > > +static struct i2c_driver mt9m114_driver = {
> > > > > +	.driver = {
> > > > > +		.owner	= THIS_MODULE,
> > > > > +		.name	= "mt9m114",
> > > > > +	},
> > > > > +	.probe		= mt9m114_probe,
> > > > > +	.remove		= mt9m114_remove,
> > > > > +	.id_table	= mt9m114_id,
> > > > 
> > > > No OF or ACPI ID table? Really?
> > > 
> > > I have no idea what ACPI IDs this device would have, and OF isn't
> > > required, the I2C subsystem strips the vendor prefix from the compatible
> > > string and matches against i2c_driver.id_table.
> > 
> > If this driver is intended to work on a DT based system, it needs to have a
> > compatible string associated with it. The I²C ID table is meant to be used
> > with e.g. platform data.
> 
> The driver works as-is on DT-based systems, that's what I have tested it
> with :-)

Please switch to use of_device_id table and the compatible string.

-- 
Kind regards,

Sakari Ailus



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux