Re: [PATCH v2 4/6] media: rockchip: add a driver for the rockchip camera interface (cif)

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

 



Hi Michael,

On Thu, Jan 09, 2025 at 02:05:54PM +0100, Michael Riesch wrote:
> Hi Sakari,
> 
> Thanks a lot for your review!

You're welcome!

> 
> On 1/9/25 11:07, Sakari Ailus wrote:
> > Hi Michael,
> > 
> > Thanks for the update.
> > 
> > On Tue, Dec 17, 2024 at 04:55:16PM +0100, Michael Riesch wrote:
...
> >> +static int cif_subdev_notifier_register(struct cif_device *cif_dev, int index)
> > 
> > This function name is misleading. It does not register a notifier, it
> > prepares one though.
> 
> cif_subdev_notifier_add(..) ?

Sounds good.

> >> +{
> >> +	struct v4l2_async_notifier *ntf = &cif_dev->notifier;
> >> +	struct v4l2_fwnode_endpoint *vep;
> >> +	struct cif_remote *remote;
> >> +	struct device *dev = cif_dev->dev;
> >> +	struct fwnode_handle *ep;
> >> +	int ret;
> >> +
> >> +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), index, 0,
> >> +					     FWNODE_GRAPH_ENDPOINT_NEXT);
> >> +	if (!ep)
> >> +		return -ENODEV;
> >> +
> >> +	if (index == 0) {
> > 
> > index seems to be always 0.
> 
> Right now, yes. The soon-to-be-added MIPI paths shall bring different
> indices into play.

Ack.

> 
> > 
> >> +		vep = &cif_dev->dvp.vep;
> >> +
> >> +		vep->bus_type = V4L2_MBUS_UNKNOWN;
> >> +		ret = v4l2_fwnode_endpoint_parse(ep, vep);
> >> +		if (ret)
> >> +			goto complete;
> >> +
> >> +		if (vep->bus_type != V4L2_MBUS_BT656 &&
> >> +		    vep->bus_type != V4L2_MBUS_PARALLEL) {
> >> +			v4l2_err(&cif_dev->v4l2_dev, "unsupported bus type\n");
> >> +			goto complete;
> >> +		}
> >> +
> >> +		remote = v4l2_async_nf_add_fwnode_remote(ntf, ep,
> >> +							 struct cif_remote);
> >> +		if (IS_ERR(remote)) {
> >> +			ret = PTR_ERR(remote);
> >> +			goto complete;
> >> +		}
> >> +
> >> +		cif_dev->dvp.stream.remote = remote;
> >> +		remote->stream = &cif_dev->dvp.stream;
> >> +	} else {
> >> +		ret = -ENODEV;
> >> +		goto complete;
> >> +	}
> >> +
> >> +complete:
> >> +	fwnode_handle_put(ep);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static void cif_subdev_notifier_unregister(struct cif_device *cif_dev,
> >> +					   int index)
> > 
> > This seems to be redundant.
> 
> Ack.
> 
> > 
> >> +{
> >> +}
> >> +
> >> +static int cif_entities_register(struct cif_device *cif_dev)
> > 
> > This function appears to be misnamed.
> 
> Hm. The function registers the different entities of this hardware, such
> as the DVP. However, I am not emotionally attached to this name and any
> suggestions are welcome :-)

Ok, fair enough: one of the things it does is to register the video device
but it does a whole lot of other stuff unrelated to it. How about just
cif_register()?

> 
> > 
> >> +{
> >> +	struct v4l2_async_notifier *ntf = &cif_dev->notifier;
> >> +	struct device *dev = cif_dev->dev;
> >> +	int ret;
> >> +
> >> +	v4l2_async_nf_init(ntf, &cif_dev->v4l2_dev);
> >> +	ntf->ops = &cif_subdev_notifier_ops;
> >> +
> >> +	if (cif_dev->match_data->dvp) {
> >> +		ret = cif_subdev_notifier_register(cif_dev, 0);
> >> +		if (ret) {
> >> +			dev_err(dev,
> >> +				"failed to register notifier for dvp: %d\n",
> >> +				ret);
> >> +			goto err;
> >> +		}
> >> +
> >> +		ret = cif_dvp_register(cif_dev);
> >> +		if (ret) {
> >> +			dev_err(dev, "failed to register dvp: %d\n", ret);
> >> +			goto err_dvp_notifier_unregister;
> >> +		}
> >> +	}
> >> +
> >> +	ret = v4l2_async_nf_register(ntf);
> >> +	if (ret)
> >> +		goto err_notifier_cleanup;
> >> +
> >> +	return 0;
> >> +
> >> +err_notifier_cleanup:
> >> +	if (cif_dev->match_data->dvp)
> >> +		cif_dvp_unregister(cif_dev);
> >> +err_dvp_notifier_unregister:
> >> +	if (cif_dev->match_data->dvp)
> >> +		cif_subdev_notifier_unregister(cif_dev, 0);
> >> +	v4l2_async_nf_cleanup(ntf);
> >> +err:
> >> +	return ret;
> >> +}
> >> +
> >> +static void cif_entities_unregister(struct cif_device *cif_dev)

And similarly here.

> >> +{
> >> +	v4l2_async_nf_unregister(&cif_dev->notifier);
> >> +	v4l2_async_nf_cleanup(&cif_dev->notifier);
> >> +
> >> +	if (cif_dev->match_data->dvp) {
> >> +		cif_subdev_notifier_unregister(cif_dev, 0);
> >> +		cif_dvp_unregister(cif_dev);
> >> +	}
> >> +}
> >> +
> >> +static irqreturn_t cif_isr(int irq, void *ctx)
> >> +{
> >> +	irqreturn_t ret = IRQ_NONE;
> >> +
> >> +	if (cif_dvp_isr(irq, ctx) == IRQ_HANDLED)
> >> +		ret = IRQ_HANDLED;
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int cif_probe(struct platform_device *pdev)
> >> +{
> >> +	struct device *dev = &pdev->dev;
> >> +	struct cif_device *cif_dev;
> >> +	u32 cif_clk_delaynum = 0;
> >> +	int ret, irq, i;
> >> +
> >> +	cif_dev = devm_kzalloc(dev, sizeof(*cif_dev), GFP_KERNEL);
> >> +	if (!cif_dev)
> >> +		return -ENOMEM;
> >> +
> >> +	cif_dev->match_data = of_device_get_match_data(dev);
> >> +	if (!cif_dev->match_data)
> >> +		return -ENODEV;
> >> +
> >> +	dev_set_drvdata(dev, cif_dev);
> >> +	cif_dev->dev = dev;
> >> +
> >> +	cif_dev->base_addr = devm_platform_ioremap_resource(pdev, 0);
> >> +	if (IS_ERR(cif_dev->base_addr))
> >> +		return PTR_ERR(cif_dev->base_addr);
> >> +
> >> +	irq = platform_get_irq(pdev, 0);
> >> +	if (irq < 0)
> >> +		return irq;
> >> +
> >> +	ret = devm_request_irq(dev, irq, cif_isr, IRQF_SHARED,
> >> +			       dev_driver_string(dev), dev);
> >> +	if (ret)
> >> +		return dev_err_probe(dev, ret, "failed to request irq\n");
> >> +	cif_dev->irq = irq;
> > 
> > Where do you need the irq field?
> 
> Hm. Seems to be a leftover. Should we check irq == cif_dev->irq in the
> ISR or is this unnecessary?

You've got the device context already, I don't think you need to check the
irq.

> >> +	cif_dev->media_dev.dev = dev;
> >> +	strscpy(cif_dev->media_dev.model, CIF_DRIVER_NAME,
> >> +		sizeof(cif_dev->media_dev.model));
> >> +	media_device_init(&cif_dev->media_dev);
> >> +
> >> +	cif_dev->v4l2_dev.mdev = &cif_dev->media_dev;
> >> +	cif_dev->v4l2_dev.notify = cif_notify;
> >> +	strscpy(cif_dev->v4l2_dev.name, CIF_DRIVER_NAME,
> >> +		sizeof(cif_dev->v4l2_dev.name));
> > 
> > Do you need to set the name? v4l2_device_register() assigns a default one.
> 
> I guess we can use the default, then.
> 
> We do need to set the model of the media device, though, right?

Correct.

> >> +static int cif_stream_start_streaming(struct vb2_queue *queue,
> >> +				      unsigned int count)
> >> +{
> >> +	struct cif_stream *stream = queue->drv_priv;
> >> +	struct cif_device *cif_dev = stream->cif_dev;
> >> +	struct v4l2_subdev *sd = stream->remote->sd;
> >> +	int ret;
> >> +
> >> +	stream->frame_idx = 0;
> >> +	stream->frame_phase = 0;
> >> +
> >> +	ret = video_device_pipeline_start(&stream->vdev, &stream->pipeline);
> >> +	if (ret) {
> >> +		dev_err(cif_dev->dev, "failed to start pipeline %d\n", ret);
> >> +		goto err_out;
> >> +	}
> >> +
> >> +	ret = pm_runtime_resume_and_get(cif_dev->dev);
> >> +	if (ret < 0) {
> >> +		dev_err(cif_dev->dev, "failed to get runtime pm, %d\n", ret);
> >> +		goto err_pipeline_stop;
> >> +	}
> >> +
> >> +	ret = cif_stream_init_buffers(stream);
> >> +	if (ret)
> >> +		goto err_runtime_put;
> >> +
> >> +	if (stream->start_streaming) {
> >> +		ret = stream->start_streaming(stream);
> >> +		if (ret < 0)
> >> +			goto err_runtime_put;
> >> +	}
> >> +
> >> +	ret = v4l2_subdev_call(sd, video, s_stream, 1);
> > 
> > Could you use v4l2_subdev_enable_streams() instead for this?
> > 
> > See e.g. drivers/media/pci/intel/ipu6 for an example.
> 
> This should be pretty much a 1:1 replacement I guess? But with support
> for streams?

Yes, and I'd prefer to get rid of the s_stream() op altogether. That may
take a long time though.

> >> +static int cif_stream_enum_framesizes(struct file *file, void *fh,
> >> +				      struct v4l2_frmsizeenum *fsize)
> >> +{
> >> +	struct cif_stream *stream = video_drvdata(file);
> >> +	struct v4l2_subdev_frame_size_enum fse = {
> >> +		.index = fsize->index,
> >> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> >> +	};
> >> +	struct v4l2_subdev *sd = stream->remote->sd;
> >> +	const struct cif_output_fmt *fmt;
> >> +	int ret;
> >> +
> >> +	fmt = cif_stream_find_output_fmt(stream, fsize->pixel_format);
> >> +	if (!fmt)
> >> +		return -EINVAL;
> >> +
> >> +	fse.code = fmt->mbus_code;
> >> +
> >> +	ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse);
> > 
> > You shouldn't get this information from the sensor driver but just convey
> > what this device supports.
> 
> OK, but what does this device support? In principle, there is a
> continuous range of frame sizes up to a certain maximum size. But in
> practice, it depends on the subdevice as there is no scaler in the DVP
> path. Thus, every frame size but the one that the subdevice delivers
> will result in a broken stream?

Could you use CIF_MAX_WIDTH and CIF_MAX_HEIGHT?

> 
> If this does not return the only possible frame size, is this callback
> useful at all?

In order not to configure an output size for the sensor that can't be
received by this block?

> >> diff --git a/drivers/media/platform/rockchip/cif/cif-stream.h b/drivers/media/platform/rockchip/cif/cif-stream.h
> >> new file mode 100644
> >> index 000000000000..5bfa260eda83
> >> --- /dev/null
> >> +++ b/drivers/media/platform/rockchip/cif/cif-stream.h
> >> @@ -0,0 +1,24 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Rockchip Camera Interface (CIF) Driver
> >> + *
> >> + * Copyright (C) 2024 Michael Riesch <michael.riesch@xxxxxxxxxxxxxx>
> >> + */
> >> +
> >> +#ifndef _CIF_STREAM_H
> >> +#define _CIF_STREAM_H
> >> +
> >> +#include "cif-common.h"
> >> +
> >> +struct cif_stream_config {
> >> +	const char *name;
> >> +};
> >> +
> >> +void cif_stream_pingpong(struct cif_stream *stream);
> >> +
> >> +int cif_stream_register(struct cif_device *cif_dev, struct cif_stream *stream,
> >> +			const struct cif_stream_config *config);
> >> +
> >> +void cif_stream_unregister(struct cif_stream *stream);
> > 
> > There are other CIFs out there. I think it'd be good to use either some
> > Rockchip specific prefix (rk maybe?) or a namespace.
> 
> Are there? In drivers/media or in general?

I may recall something that may be after all related to this.

Either way, "cif" is a non-specific short string and it's entirely
conceivable it'll easily conflict with something else.

> 
> And would that apply to all the method and struct names in this driver
> or to the driver as well (location, file names)?
> 
> The name has been discussed several times during the 13 iterations of
> the PX30 VIP series and I believe it changed from (cif//rkcif_) in
> downstream -> (vip//vip_) in Maximes work -> (cif/cif-/cif_) in Mehdis
> work, where the tuple is (driver directory/filename prefix/function and
> structs prefix).
> 
> Hence I am a bit reluctant to change the naming convention yet again.
> That said, I'll be prepared to change it JUST ONE MORE TIME to any tuple
> you suggest -- but this really should be the end of the name bikeshedding.

I don't care about the internal naming but the global symbols. Using a
namespace is another option.

-- 
Kind regards,

Sakari Ailus




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux