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 <pengw@xxxxxxxxxx> Reviewed-by: Hans Verkuil <hans.verkuil@xxxxxxxxx> --- 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 <pengw@xxxxxxxxxx> + * + * 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html