Hi Helen,
Please see my comments below.
Helen Koike wrote:
> On 2017-01-25 11:03 AM, Sakari Ailus wrote:
...
>>> + * the videobuf2 framework will allocate this struct based on
>>> + * buf_struct_size and use the first sizeof(struct vb2_buffer)
>>> bytes of
>>> + * memory as a vb2_buffer
>>> + */
>>> + struct vb2_v4l2_buffer vb2;
>>> + struct list_head list;
>>> +};
>>> +
>>> +static int vimc_cap_querycap(struct file *file, void *priv,
>>> + struct v4l2_capability *cap)
>>> +{
>>> + struct vimc_cap_device *vcap = video_drvdata(file);
>>> +
>>> + strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
>>> + strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
>>> + snprintf(cap->bus_info, sizeof(cap->bus_info),
>>> + "platform:%s", vcap->v4l2_dev->name);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
>>> + struct v4l2_input *i)
>>> +{
>>> + /* We only have one input */
>>> + if (i->index > 0)
>>> + return -EINVAL;
>>> +
>>> + i->type = V4L2_INPUT_TYPE_CAMERA;
>>> + strlcpy(i->name, "VIMC capture", sizeof(i->name));
>>
>> Isn't this (*INPUT IOCTLs) something that should be handled in a
>> sub-device
>> driver, such as a TV tuner?
>
>
> Can the ioctl VIDIOC_ENUMINPUT enumerate no inputs at all? Can I just
> return -EINVAL here in G_INPUT and S_INPUT as well?
> I thought I had to enumerate at least one input, and between
> V4L2_INPUT_TYPE_TUNER and V4L2_INPUT_TYPE_CAMERA, this last
> one seems more appropriated
I don't think other drivers that provide MC interface do this on video
nodes either. The VIMC driver could know what's connected to it, but
generally that's not the case.
>
>
>>
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned
>>> int *i)
>>> +{
>>> + /* We only have one input */
>>> + *i = 0;
>>> + return 0;
>>> +}
>>> +
>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned
>>> int i)
>>> +{
>>> + /* We only have one input */
>>> + return i ? -EINVAL : 0;
>>> +}
>>> +
>>> +static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
>>> + struct v4l2_format *f)
>>> +{
>>> + struct vimc_cap_device *vcap = video_drvdata(file);
>>> +
>>> + f->fmt.pix = vcap->format;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
>>> + struct v4l2_fmtdesc *f)
>>> +{
>>> + struct vimc_cap_device *vcap = video_drvdata(file);
>>> +
>>> + if (f->index > 0)
>>> + return -EINVAL;
>>> +
>>> + /* We only support one format for now */
>>> + f->pixelformat = vcap->format.pixelformat;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct v4l2_file_operations vimc_cap_fops = {
>>> + .owner = THIS_MODULE,
>>> + .open = v4l2_fh_open,
>>> + .release = vb2_fop_release,
>>> + .read = vb2_fop_read,
>>> + .poll = vb2_fop_poll,
>>> + .unlocked_ioctl = video_ioctl2,
>>> + .mmap = vb2_fop_mmap,
>>> +};
>>> +
>>> +static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
>>> + .vidioc_querycap = vimc_cap_querycap,
>>> +
>>> + .vidioc_enum_input = vimc_cap_enum_input,
>>> + .vidioc_g_input = vimc_cap_g_input,
>>> + .vidioc_s_input = vimc_cap_s_input,
>>> +
>>> + .vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
>>> + .vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
>>> + .vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
>>> + .vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
>>> +
>>> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
>>> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
>>> + .vidioc_querybuf = vb2_ioctl_querybuf,
>>> + .vidioc_qbuf = vb2_ioctl_qbuf,
>>> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
>>> + .vidioc_expbuf = vb2_ioctl_expbuf,
>>> + .vidioc_streamon = vb2_ioctl_streamon,
>>> + .vidioc_streamoff = vb2_ioctl_streamoff,
>>> +};
>>> +
>>> +static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
>>> + enum vb2_buffer_state state)
>>> +{
>>> + struct vimc_cap_buffer *vbuf, *node;
>>> +
>>> + spin_lock(&vcap->qlock);
>>> +
>>> + list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
>>> + vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
>>> + list_del(&vbuf->list);
>>> + }
>>> +
>>> + spin_unlock(&vcap->qlock);
>>> +}
>>> +
>>> +static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap,
>>> int enable)
>>> +{
>>> + int ret;
>>> + struct media_pad *pad;
>>> + struct media_entity *entity;
>>> + struct v4l2_subdev *sd;
>>> +
>>> + /* Start the stream in the subdevice direct connected */
>>> + entity = &vcap->vdev.entity;
>>> + pad = media_entity_remote_pad(&entity->pads[0]);
>>
>> You could use vcap->vdev.entity.pads here, without assigning to
>> entity. Then
>> entity would only be used to refer to the remove entity at the other
>> end of
>> the link. Up to you.
>>
>>> +
>>> + /* If we are not connected to any subdev node, it means there is
>>> nothing
>>
>> /*
>> * Multi line
>> * comment.
>> */
>>
>>> + * to activate on the pipe (e.g. we can be connected with an input
>>> + * device or we are not connected at all)
>>> + */
>>> + if (pad == NULL || !is_media_entity_v4l2_subdev(pad->entity))
>>> + return 0;
>>> +
>>> + entity = pad->entity;
>>> + sd = media_entity_to_v4l2_subdev(entity);
>>
>> And if you used pad->entity here, you could remove the entity variable
>> altogether.
>>
>>> +
>>> + ret = v4l2_subdev_call(sd, video, s_stream, enable);
>>> + if (ret && ret != -ENOIOCTLCMD)
>>> + return ret;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned
>>> int count)
>>> +{
>>> + struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>>> + struct media_entity *entity;
>>> + int ret;
>>> +
>>> + vcap->sequence = 0;
>>> +
>>> + /* Start the media pipeline */
>>> + entity = &vcap->vdev.entity;
>>> + ret = media_entity_pipeline_start(entity, &vcap->pipe);
>>> + if (ret) {
>>> + vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
>>> + return ret;
>>> + }
>>> +
>>> + /* Enable streaming from the pipe */
>>> + ret = vimc_cap_pipeline_s_stream(vcap, 1);
>>> + if (ret) {
>>> + vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
>>> + return ret;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/*
>>> + * Stop the stream engine. Any remaining buffers in the stream queue
>>> are
>>> + * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
>>> + */
>>> +static void vimc_cap_stop_streaming(struct vb2_queue *vq)
>>> +{
>>> + struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>>> +
>>> + /* Disable streaming from the pipe */
>>> + vimc_cap_pipeline_s_stream(vcap, 0);
>>> +
>>> + /* Stop the media pipeline */
>>> + media_entity_pipeline_stop(&vcap->vdev.entity);
>>> +
>>> + /* Release all active buffers */
>>> + vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
>>> +}
>>> +
>>> +static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
>>> +{
>>> + struct vimc_cap_device *vcap =
>>> vb2_get_drv_priv(vb2_buf->vb2_queue);
>>> + struct vimc_cap_buffer *buf = container_of(vb2_buf,
>>> + struct vimc_cap_buffer,
>>> + vb2.vb2_buf);
>>> +
>>> + spin_lock(&vcap->qlock);
>>> + list_add_tail(&buf->list, &vcap->buf_list);
>>> + spin_unlock(&vcap->qlock);
>>> +}
>>> +
>>> +static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int
>>> *nbuffers,
>>> + unsigned int *nplanes, unsigned int sizes[],
>>> + struct device *alloc_devs[])
>>> +{
>>> + struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>>> +
>>> + if (*nplanes)
>>> + return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
>>
>> Why? The user could later reconfigure the device to use with a buffer of
>> this size. This might not be a concern for vimc, but the code from
>> example
>> drivers tends to get copied around.
>
>
> This first version only support a fixed sizeimage, this will be changed
> in the next patch series where
> I add support for multiple sizes
Sounds good to me. Thanks.
--
Kind regards,
Sakari Ailus
[email protected]