On 09/22/2015 01:52 AM, Hans Verkuil wrote:
Hi Bryan,

Thanks for this v3 patch series. It looks very good now. I have a few comments,
I think they are trivial to add and then I would just wait for the new MC code
to be merged. I hope it will be soon, but it's a bit unpredictable.

On 21-09-15 20:55, Bryan Wu wrote:
NVIDIA Tegra processor contains a powerful Video Input (VI) hardware
controller which can support up to 6 MIPI CSI camera sensors.

This patch adds a V4L2 media controller and capture driver to support
Tegra VI hardware. It's verified with Tegra built-in test pattern
generator.

Signed-off-by: Bryan Wu <pe...@nvidia.com>
Reviewed-by: Hans Verkuil <hans.verk...@cisco.com>
---
  drivers/media/platform/Kconfig               |   1 +
  drivers/media/platform/Makefile              |   2 +
  drivers/media/platform/tegra/Kconfig         |  10 +
  drivers/media/platform/tegra/Makefile        |   3 +
  drivers/media/platform/tegra/tegra-channel.c | 782 +++++++++++++++++++++++++++
  drivers/media/platform/tegra/tegra-core.c    | 252 +++++++++
  drivers/media/platform/tegra/tegra-core.h    | 162 ++++++
  drivers/media/platform/tegra/tegra-csi.c     | 566 +++++++++++++++++++
  drivers/media/platform/tegra/tegra-vi.c      | 581 ++++++++++++++++++++
  drivers/media/platform/tegra/tegra-vi.h      | 209 +++++++
  10 files changed, 2568 insertions(+)
  create mode 100644 drivers/media/platform/tegra/Kconfig
  create mode 100644 drivers/media/platform/tegra/Makefile
  create mode 100644 drivers/media/platform/tegra/tegra-channel.c
  create mode 100644 drivers/media/platform/tegra/tegra-core.c
  create mode 100644 drivers/media/platform/tegra/tegra-core.h
  create mode 100644 drivers/media/platform/tegra/tegra-csi.c
  create mode 100644 drivers/media/platform/tegra/tegra-vi.c
  create mode 100644 drivers/media/platform/tegra/tegra-vi.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index f6bed19..553867f 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -119,6 +119,7 @@ source "drivers/media/platform/exynos4-is/Kconfig"
  source "drivers/media/platform/s5p-tv/Kconfig"
  source "drivers/media/platform/am437x/Kconfig"
  source "drivers/media/platform/xilinx/Kconfig"
+source "drivers/media/platform/tegra/Kconfig"
endif # V4L_PLATFORM_DRIVERS
<snip>

diff --git a/drivers/media/platform/tegra/tegra-channel.c 
b/drivers/media/platform/tegra/tegra-channel.c
new file mode 100644
index 0000000..37a7017
--- /dev/null
+++ b/drivers/media/platform/tegra/tegra-channel.c
@@ -0,0 +1,782 @@
+/*
+ * NVIDIA Tegra Video Input Device
+ *
+ * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Author: Bryan Wu <pe...@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/atomic.h>
+#include <linux/bitmap.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/host1x.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/lcm.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <soc/tegra/pmc.h>
+
+#include "tegra-vi.h"
+
+#define TEGRA_VI_SYNCPT_WAIT_TIMEOUT                   200
+
+/* VI registers */
+#define TEGRA_VI_CFG_VI_INCR_SYNCPT                    0x000
+#define   VI_CFG_VI_INCR_SYNCPT_COND(x)                        (((x) & 0xff) 
<< 8)
+#define   VI_CSI_PP_LINE_START(port)                   (4 + (port) * 4)
+#define   VI_CSI_PP_FRAME_START(port)                  (5 + (port) * 4)
+#define   VI_CSI_MW_REQ_DONE(port)                     (6 + (port) * 4)
+#define   VI_CSI_MW_ACK_DONE(port)                     (7 + (port) * 4)
+
+#define TEGRA_VI_CFG_VI_INCR_SYNCPT_CNTRL              0x004
+#define TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR              0x008
+#define TEGRA_VI_CFG_CTXSW                             0x020
+#define TEGRA_VI_CFG_INTSTATUS                         0x024
+#define TEGRA_VI_CFG_PWM_CONTROL                       0x038
+#define TEGRA_VI_CFG_PWM_HIGH_PULSE                    0x03c
+#define TEGRA_VI_CFG_PWM_LOW_PULSE                     0x040
+#define TEGRA_VI_CFG_PWM_SELECT_PULSE_A                        0x044
+#define TEGRA_VI_CFG_PWM_SELECT_PULSE_B                        0x048
+#define TEGRA_VI_CFG_PWM_SELECT_PULSE_C                        0x04c
+#define TEGRA_VI_CFG_PWM_SELECT_PULSE_D                        0x050
+#define TEGRA_VI_CFG_VGP1                              0x064
+#define TEGRA_VI_CFG_VGP2                              0x068
+#define TEGRA_VI_CFG_VGP3                              0x06c
+#define TEGRA_VI_CFG_VGP4                              0x070
+#define TEGRA_VI_CFG_VGP5                              0x074
+#define TEGRA_VI_CFG_VGP6                              0x078
+#define TEGRA_VI_CFG_INTERRUPT_MASK                    0x08c
+#define TEGRA_VI_CFG_INTERRUPT_TYPE_SELECT             0x090
+#define TEGRA_VI_CFG_INTERRUPT_POLARITY_SELECT         0x094
+#define TEGRA_VI_CFG_INTERRUPT_STATUS                  0x098
+#define TEGRA_VI_CFG_VGP_SYNCPT_CONFIG                 0x0ac
+#define TEGRA_VI_CFG_VI_SW_RESET                       0x0b4
+#define TEGRA_VI_CFG_CG_CTRL                           0x0b8
+#define   VI_CG_2ND_LEVEL_EN                           0x1
+#define TEGRA_VI_CFG_VI_MCCIF_FIFOCTRL                 0x0e4
+#define TEGRA_VI_CFG_TIMEOUT_WCOAL_VI                  0x0e8
+#define TEGRA_VI_CFG_DVFS                              0x0f0
+#define TEGRA_VI_CFG_RESERVE                           0x0f4
+#define TEGRA_VI_CFG_RESERVE_1                         0x0f8
+
+/* CSI registers */
+#define TEGRA_VI_CSI_BASE(x)                           (0x100 + (x) * 0x100)
+
+#define TEGRA_VI_CSI_SW_RESET                          0x000
+#define TEGRA_VI_CSI_SINGLE_SHOT                       0x004
+#define   SINGLE_SHOT_CAPTURE                          0x1
+#define TEGRA_VI_CSI_SINGLE_SHOT_STATE_UPDATE          0x008
+#define TEGRA_VI_CSI_IMAGE_DEF                         0x00c
+#define   BYPASS_PXL_TRANSFORM_OFFSET                  24
+#define   IMAGE_DEF_FORMAT_OFFSET                      16
+#define   IMAGE_DEF_DEST_MEM                           0x1
+#define TEGRA_VI_CSI_RGB2Y_CTRL                                0x010
+#define TEGRA_VI_CSI_MEM_TILING                                0x014
+#define TEGRA_VI_CSI_IMAGE_SIZE                                0x018
+#define   IMAGE_SIZE_HEIGHT_OFFSET                     16
+#define TEGRA_VI_CSI_IMAGE_SIZE_WC                     0x01c
+#define TEGRA_VI_CSI_IMAGE_DT                          0x020
+#define TEGRA_VI_CSI_SURFACE0_OFFSET_MSB               0x024
+#define TEGRA_VI_CSI_SURFACE0_OFFSET_LSB               0x028
+#define TEGRA_VI_CSI_SURFACE1_OFFSET_MSB               0x02c
+#define TEGRA_VI_CSI_SURFACE1_OFFSET_LSB               0x030
+#define TEGRA_VI_CSI_SURFACE2_OFFSET_MSB               0x034
+#define TEGRA_VI_CSI_SURFACE2_OFFSET_LSB               0x038
+#define TEGRA_VI_CSI_SURFACE0_BF_OFFSET_MSB            0x03c
+#define TEGRA_VI_CSI_SURFACE0_BF_OFFSET_LSB            0x040
+#define TEGRA_VI_CSI_SURFACE1_BF_OFFSET_MSB            0x044
+#define TEGRA_VI_CSI_SURFACE1_BF_OFFSET_LSB            0x048
+#define TEGRA_VI_CSI_SURFACE2_BF_OFFSET_MSB            0x04c
+#define TEGRA_VI_CSI_SURFACE2_BF_OFFSET_LSB            0x050
+#define TEGRA_VI_CSI_SURFACE0_STRIDE                   0x054
+#define TEGRA_VI_CSI_SURFACE1_STRIDE                   0x058
+#define TEGRA_VI_CSI_SURFACE2_STRIDE                   0x05c
+#define TEGRA_VI_CSI_SURFACE_HEIGHT0                   0x060
+#define TEGRA_VI_CSI_ISPINTF_CONFIG                    0x064
+#define TEGRA_VI_CSI_ERROR_STATUS                      0x084
+#define TEGRA_VI_CSI_ERROR_INT_MASK                    0x088
+#define TEGRA_VI_CSI_WD_CTRL                           0x08c
+#define TEGRA_VI_CSI_WD_PERIOD                         0x090
+
+/* Channel registers */
+static void tegra_channel_write(struct tegra_channel *chan,
+                               unsigned int addr, u32 val)
+{
+       writel(val, chan->vi->iomem + addr);
+}
+
+/* CSI registers */
+static void csi_write(struct tegra_channel *chan, unsigned int addr, u32 val)
+{
+       writel(val, chan->csi + addr);
+}
+
+static u32 csi_read(struct tegra_channel *chan, unsigned int addr)
+{
+       return readl(chan->csi + addr);
+}
+
+/* CSI channel IO Rail IDs */
+static const int tegra_io_rail_csi_ids[] = {
+       TEGRA_IO_RAIL_CSIA,
+       TEGRA_IO_RAIL_CSIB,
+       TEGRA_IO_RAIL_CSIC,
+       TEGRA_IO_RAIL_CSID,
+       TEGRA_IO_RAIL_CSIE,
+       TEGRA_IO_RAIL_CSIF,
+};
+
+void tegra_channel_fmts_bitmap_init(struct tegra_channel *chan,
+                                   struct tegra_vi_graph_entity *entity)
+{
+       int ret, index;
+       struct v4l2_subdev *subdev = entity->subdev;
+       struct v4l2_subdev_mbus_code_enum code = {
+               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+       };
+       struct v4l2_subdev_format fmt;
+
+       bitmap_zero(chan->fmts_bitmap, MAX_FORMAT_NUM);
+
+       while (1) {
+               ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
+                                      NULL, &code);
+               if (ret < 0)
+                       /* no more formats */
+                       break;
+
+               index = tegra_core_get_idx_by_code(code.code);
+               if (index >= 0)
+                       bitmap_set(chan->fmts_bitmap, index, 1);
+
+               code.index++;
+       }
+
+       /* Get colorspace format infor from subdev */
+       ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+       if (ret >= 0)
+               chan->format.colorspace = fmt.format.colorspace;
+
+       return;
+}
+
+/* 
-----------------------------------------------------------------------------
+ * Tegra channel frame setup and capture operations */
+
+static int tegra_channel_capture_setup(struct tegra_channel *chan)
+{
+       u32 height = chan->format.height;
+       u32 width = chan->format.width;
+       u32 format = chan->fmtinfo->img_fmt;
+       u32 data_type = chan->fmtinfo->img_dt;
+       u32 word_count = tegra_core_get_word_count(width, chan->fmtinfo);
+
+       csi_write(chan, TEGRA_VI_CSI_ERROR_STATUS, 0xFFFFFFFF);
+       csi_write(chan, TEGRA_VI_CSI_IMAGE_DEF,
+                 ((chan->vi->pg_mode ? 1 : 0) << BYPASS_PXL_TRANSFORM_OFFSET) |
+                 (format << IMAGE_DEF_FORMAT_OFFSET) |
+                 IMAGE_DEF_DEST_MEM);
+       csi_write(chan, TEGRA_VI_CSI_IMAGE_DT, data_type);
+       csi_write(chan, TEGRA_VI_CSI_IMAGE_SIZE_WC, word_count);
+       csi_write(chan, TEGRA_VI_CSI_IMAGE_SIZE,
+                 (height << IMAGE_SIZE_HEIGHT_OFFSET) | width);
+       return 0;
+}
+
+static void tegra_channel_capture_error(struct tegra_channel *chan)
+{
+       u32 val;
+
+       val = csi_read(chan, TEGRA_VI_CSI_ERROR_STATUS);
+       dev_err(&chan->video.dev, "TEGRA_VI_CSI_ERROR_STATUS 0x%08x\n", val);
+}
+
+static int tegra_channel_capture_frame(struct tegra_channel *chan,
+                                      struct tegra_channel_buffer *buf)
+{
+       struct vb2_buffer *vb = &buf->buf;
+       int err = 0;
+       u32 thresh, value, frame_start;
+       int bytes_per_line = chan->format.bytesperline;
+
+       /* Program buffer address by using surface 0 */
+       csi_write(chan, TEGRA_VI_CSI_SURFACE0_OFFSET_MSB, 0x0);
+       csi_write(chan, TEGRA_VI_CSI_SURFACE0_OFFSET_LSB, buf->addr);
+       csi_write(chan, TEGRA_VI_CSI_SURFACE0_STRIDE, bytes_per_line);
+
+       /* Program syncpoint */
+       frame_start = VI_CSI_PP_FRAME_START(chan->port);
+       value = VI_CFG_VI_INCR_SYNCPT_COND(frame_start) |
+               host1x_syncpt_id(chan->sp);
+       tegra_channel_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, value);
+
+       csi_write(chan, TEGRA_VI_CSI_SINGLE_SHOT, SINGLE_SHOT_CAPTURE);
+
+       /* Use syncpoint to wake up */
+       thresh = host1x_syncpt_incr_max(chan->sp, 1);
+       err = host1x_syncpt_wait(chan->sp, thresh,
+                                TEGRA_VI_SYNCPT_WAIT_TIMEOUT, &value);
+       if (err) {
+               dev_err(&chan->video.dev, "frame start syncpt timeout!\n");
+               tegra_channel_capture_error(chan);
+       }
+
+       /* Captured one frame */
+       spin_lock(&chan->queued_lock);
No need for this spin_lock here. The spin_lock protects the chan->capture list,
but you're not using that here. So it can just be dropped.

OK, let me drop them


+       vb->v4l2_buf.sequence = chan->sequence++;
+       vb->v4l2_buf.field = V4L2_FIELD_NONE;
+       v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
+       vb2_set_plane_payload(vb, 0, chan->format.sizeimage);
+       vb2_buffer_done(vb, err < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+       spin_unlock(&chan->queued_lock);
+
+       return err;
+}
+
+static int tegra_channel_kthread_capture(void *data)
+{
+       struct tegra_channel *chan = data;
+       struct tegra_channel_buffer *buf;
+
+       set_freezable();
+
+       while (1) {
+               try_to_freeze();
+wait_again:
+               wait_event_interruptible(chan->wait,
+                                        !list_empty(&chan->capture) ||
+                                        kthread_should_stop());
+               if (kthread_should_stop())
+                       break;
+
+               spin_lock(&chan->queued_lock);
+               if (list_empty(&chan->capture)) {
+                       spin_unlock(&chan->queued_lock);
+                       goto wait_again;
Rather than a goto, just use 'continue' here.
Yeah, fixed.

+               }
+               buf = list_entry(chan->capture.next,
+                                struct tegra_channel_buffer, queue);
+               list_del_init(&buf->queue);
+               spin_unlock(&chan->queued_lock);
+
+               tegra_channel_capture_frame(chan, buf);
+       }
+
+       return 0;
+}
+
+/* 
-----------------------------------------------------------------------------
+ * videobuf2 queue operations
+ */
+
+static int
+tegra_channel_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+                         unsigned int *nbuffers, unsigned int *nplanes,
+                         unsigned int sizes[], void *alloc_ctxs[])
+{
+       struct tegra_channel *chan = vb2_get_drv_priv(vq);
+
+       /* Make sure the image size is large enough. */
+       if (fmt && fmt->fmt.pix.sizeimage < chan->format.sizeimage)
+               return -EINVAL;
+
+       *nplanes = 1;
+
+       sizes[0] = fmt ? fmt->fmt.pix.sizeimage : chan->format.sizeimage;
+       alloc_ctxs[0] = chan->alloc_ctx;
+
+       return 0;
+}
+
+static int tegra_channel_buffer_prepare(struct vb2_buffer *vb)
+{
+       struct tegra_channel *chan = vb2_get_drv_priv(vb->vb2_queue);
+       struct tegra_channel_buffer *buf = to_tegra_channel_buffer(vb);
+
+       buf->chan = chan;
+       buf->addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+       return 0;
+}
+
+static void tegra_channel_buffer_queue(struct vb2_buffer *vb)
+{
+       struct tegra_channel *chan = vb2_get_drv_priv(vb->vb2_queue);
+       struct tegra_channel_buffer *buf = to_tegra_channel_buffer(vb);
+
+       /* Put buffer into the capture queue */
+       spin_lock(&chan->queued_lock);
+       list_add_tail(&buf->queue, &chan->capture);
+       spin_unlock(&chan->queued_lock);
+
+       /* Wait up kthread for capture */
+       wake_up_interruptible(&chan->wait);
+}
+
+static int tegra_channel_set_stream(struct tegra_channel *chan, bool on)
+{
+       struct media_entity *entity;
+       struct media_pad *pad = &chan->pad;
+       struct v4l2_subdev *subdev;
+       int ret = 0;
+
+       entity = &chan->video.entity;
+
+       while (1) {
+               pad = media_entity_remote_pad(pad);
+               if (pad == NULL ||
+                   media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+                       break;
+
+               entity = pad->entity;
+               subdev = media_entity_to_v4l2_subdev(entity);
+               subdev->host_priv = chan;
+               ret = v4l2_subdev_call(subdev, video, s_stream, on);
+               if (on && ret < 0 && ret != -ENOIOCTLCMD)
+                       return ret;
+       }
+       return ret;
+}
+
+static int tegra_channel_start_streaming(struct vb2_queue *vq, u32 count)
+{
+       struct tegra_channel *chan = vb2_get_drv_priv(vq);
+       struct media_pipeline *pipe = chan->video.entity.pipe;
+       struct tegra_channel_buffer *buf, *nbuf;
+       int ret = 0;
+
+       if (!chan->vi->pg_mode && !chan->vi->has_sensors)
+               dev_warn(&chan->video.dev,
+                       "is not in TPG mode and might not have \
+                        any sensor connected!\n");
I wouldn't do this here. I think a better place is where chan->vi->has_sensors 
is
assigned: if it is false, then use dev_info to inform that this channel has no
sensors (or really, no other subdevs) connected.

That way the information about this is logged, but you don't get this warning in
the log every time you stream. That would be annoying, especially if having no
sensors is perfectly fine.
OK, I will fix this.

+
+       /* The first open then turn on power*/
+       if (atomic_add_return(1, &chan->vi->power_on_refcnt) == 1) {
+               tegra_vi_power_on(chan->vi);
+
+               usleep_range(5, 100);
+               tegra_channel_write(chan, TEGRA_VI_CFG_CG_CTRL,
+                                   VI_CG_2ND_LEVEL_EN);
+               usleep_range(10, 15);
+       }
+
+       /* Disable DPD */
+       ret = tegra_io_rail_power_on(chan->io_id);
+       if (ret < 0) {
+               dev_err(&chan->video.dev,
+                       "failed to power on CSI rail: %d\n", ret);
+               goto error_power_on;
+       }
+
+       /* Clean up status */
+       csi_write(chan, TEGRA_VI_CSI_ERROR_STATUS, 0xFFFFFFFF);
+
+       ret = media_entity_pipeline_start(&chan->video.entity, pipe);
+       if (ret < 0)
+               goto error_pipeline_start;
+
+       /* Start the pipeline. */
+       ret = tegra_channel_set_stream(chan, true);
+       if (ret < 0)
+               goto error_set_stream;
+
+       /* Note: Program VI registers after TPG, sensors and CSI streaming */
+       ret = tegra_channel_capture_setup(chan);
+       if (ret < 0)
+               goto error_capture_setup;
+
+       chan->sequence = 0;
+
+       /* Start kthread to capture data to buffer */
+       chan->kthread_capture = kthread_run(tegra_channel_kthread_capture, chan,
+                                           chan->video.name);
+       if (!IS_ERR(chan->kthread_capture))
+               return 0;
+
+       dev_err(&chan->video.dev, "failed to start kthread for capture!\n");
+       ret = PTR_ERR(chan->kthread_capture);
+
+error_capture_setup:
+       tegra_channel_set_stream(chan, false);
+error_set_stream:
+       media_entity_pipeline_stop(&chan->video.entity);
+error_pipeline_start:
+       tegra_io_rail_power_off(chan->io_id);
+error_power_on:
+       /* Return all queued buffers back to vb2 */
+       spin_lock(&chan->queued_lock);
+       vq->start_streaming_called = 0;
This assignment shouldn't be needed anymore. I'm pretty sure you can drop it.
If you still need it, then let me know why.
Sure, I will remove this.

+       list_for_each_entry_safe(buf, nbuf, &chan->capture, queue) {
+               vb2_buffer_done(&buf->buf, VB2_BUF_STATE_QUEUED);
+               list_del(&buf->queue);
+       }
+       spin_unlock(&chan->queued_lock);
Returning these buffers can be split off into a separate function that can
be called by both stop and start_streaming. Only the state argument differs.

OK, fixed.

+       return ret;
+}
+
+static void tegra_channel_stop_streaming(struct vb2_queue *vq)
+{
+       struct tegra_channel *chan = vb2_get_drv_priv(vq);
+       struct tegra_channel_buffer *buf, *nbuf;
+       u32 thresh, value, mw_ack_done;
+       int err;
+
+       /* Stop the kthread for capture */
+       kthread_stop(chan->kthread_capture);
+       chan->kthread_capture = NULL;
+
+       /* Program syncpoint */
+       mw_ack_done = VI_CSI_MW_ACK_DONE(chan->port);
+       value = VI_CFG_VI_INCR_SYNCPT_COND(mw_ack_done) |
+               host1x_syncpt_id(chan->sp);
+       tegra_channel_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, value);
+
+       /* Use syncpoint to wake up */
+       thresh = host1x_syncpt_incr_max(chan->sp, 1);
+       err = host1x_syncpt_wait(chan->sp, thresh,
+                       TEGRA_VI_SYNCPT_WAIT_TIMEOUT, &value);
+       if (err)
+               dev_err(&chan->video.dev, "MW_ACK_DONE syncpoint time out!\n");
+
+       media_entity_pipeline_stop(&chan->video.entity);
+
+       tegra_channel_set_stream(chan, false);
+
+       tegra_io_rail_power_off(chan->io_id);
+
+       /* The last release then turn off power */
+       if (atomic_dec_and_test(&chan->vi->power_on_refcnt))
+               tegra_vi_power_off(chan->vi);
+
+       /* Give back all queued buffers to videobuf2. */
+       spin_lock(&chan->queued_lock);
+       list_for_each_entry_safe(buf, nbuf, &chan->capture, queue) {
+               vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
+               list_del(&buf->queue);
+       }
+       spin_unlock(&chan->queued_lock);
+}
+
+static const struct vb2_ops tegra_channel_queue_qops = {
+       .queue_setup = tegra_channel_queue_setup,
+       .buf_prepare = tegra_channel_buffer_prepare,
+       .buf_queue = tegra_channel_buffer_queue,
+       .wait_prepare = vb2_ops_wait_prepare,
+       .wait_finish = vb2_ops_wait_finish,
+       .start_streaming = tegra_channel_start_streaming,
+       .stop_streaming = tegra_channel_stop_streaming,
+};
+
+/* 
-----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int
+tegra_channel_querycap(struct file *file, void *fh, struct v4l2_capability 
*cap)
+{
+       struct v4l2_fh *vfh = file->private_data;
+       struct tegra_channel *chan = to_tegra_channel(vfh->vdev);
+
+       cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+       strlcpy(cap->driver, "tegra-video", sizeof(cap->driver));
+       strlcpy(cap->card, chan->video.name, sizeof(cap->card));
+       snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s:%u",
+                dev_name(chan->vi->dev), chan->port);
+
+       return 0;
+}
+
+static int
+tegra_channel_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+       struct v4l2_fh *vfh = file->private_data;
+       struct tegra_channel *chan = to_tegra_channel(vfh->vdev);
+       unsigned int index = 0, i;
+       unsigned long *fmts_bitmap = NULL;
+
+       if (chan->vi->pg_mode)
+               fmts_bitmap = chan->vi->tpg_fmts_bitmap;
+       else
+               fmts_bitmap = chan->fmts_bitmap;
+
+       if (f->index > bitmap_weight(fmts_bitmap, MAX_FORMAT_NUM) - 1)
+               return -EINVAL;
+
+       for (i = 0; i < f->index + 1; i++, index++)
+               index = find_next_bit(fmts_bitmap, MAX_FORMAT_NUM, index);
+
+       f->pixelformat = tegra_core_get_fourcc_by_idx(index - 1);
+
+       return 0;
+}
+
+static int
+tegra_channel_get_format(struct file *file, void *fh, struct v4l2_format 
*format)
+{
+       struct v4l2_fh *vfh = file->private_data;
+       struct tegra_channel *chan = to_tegra_channel(vfh->vdev);
+
+       format->fmt.pix = chan->format;
+
+       return 0;
+}
+
+static void
+__tegra_channel_try_format(struct tegra_channel *chan, struct v4l2_pix_format 
*pix,
+                          const struct tegra_video_format **fmtinfo)
+{
+       const struct tegra_video_format *info;
+       unsigned int min_width;
+       unsigned int max_width;
+       unsigned int min_bpl;
+       unsigned int max_bpl;
+       unsigned int width;
+       unsigned int align;
+       unsigned int bpl;
+
+       /* Retrieve format information and select the default format if the
+        * requested format isn't supported.
+        */
+       info = tegra_core_get_format_by_fourcc(pix->pixelformat);
+       if (!info)
+               info = tegra_core_get_format_by_fourcc(TEGRA_VF_DEF);
+
+       pix->pixelformat = info->fourcc;
+       /* Change this when start adding interlace format support */
+       pix->field = V4L2_FIELD_NONE;
+
+       /* The transfer alignment requirements are expressed in bytes. Compute
+        * the minimum and maximum values, clamp the requested width and convert
+        * it back to pixels.
+        */
+       align = lcm(chan->align, info->bpp);
+       min_width = roundup(TEGRA_MIN_WIDTH, align);
+       max_width = rounddown(TEGRA_MAX_WIDTH, align);
+       width = roundup(pix->width * info->bpp, align);
+
+       pix->width = clamp(width, min_width, max_width) / info->bpp;
+       pix->height = clamp(pix->height, TEGRA_MIN_HEIGHT, TEGRA_MAX_HEIGHT);
+
+       /* Clamp the requested bytes per line value. If the maximum bytes per
+        * line value is zero, the module doesn't support user configurable line
+        * sizes. Override the requested value with the minimum in that case.
+        */
+       min_bpl = pix->width * info->bpp;
+       max_bpl = rounddown(TEGRA_MAX_WIDTH, chan->align);
+       bpl = roundup(pix->bytesperline, chan->align);
+
+       pix->bytesperline = clamp(bpl, min_bpl, max_bpl);
+       pix->sizeimage = pix->bytesperline * pix->height;
+       pix->colorspace = chan->format.colorspace;
This information should be obtained by asking the subdev we're connected to.

An example of how that should be done is here:

https://patchwork.linuxtv.org/patch/30661/

Unfortunately, this depends on this patch:

https://patchwork.linuxtv.org/patch/30659/

And that can't be merged until the MC rework is done.

So just add this to your TODO list for the time being.

Great, thanks for letting know this. I also feel like this can be some generic core code helper, such as

media_link_{set|get|try}_fmt(struct media_link *link);
This will iterate each media_entity on this media_link to do fmt related operations.

Thanks,
-Bryan

-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may 
contain
confidential information.  Any unauthorized review, use, disclosure or 
distribution
is prohibited.  If you are not the intended recipient, please contact the 
sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to