Commit a81fb9b223508f5773d8d7e226b1574d1b068642

Authored by Guennadi Liakhovetski
Committed by Mauro Carvalho Chehab
1 parent f35f1bb8fc

V4L/DVB: V4L: SuperH Video Output Unit (VOU) driver

A number of SuperH Mobile SoCs, including sh7724, include a Video Output Unit.
This patch adds a video (V4L2) output driver for it. The driver uses v4l2-subdev
and mediabus APIs to interface to TV encoders.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Acked-by: Paul Mundt <lethal@linux-sh.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

Showing 4 changed files with 1521 additions and 0 deletions Side-by-side Diff

drivers/media/video/Kconfig
... ... @@ -520,6 +520,13 @@
520 520 To compile this driver as a module, choose M here: the
521 521 module will be called vpif_display.
522 522  
  523 +config VIDEO_SH_VOU
  524 + tristate "SuperH VOU video output driver"
  525 + depends on VIDEO_DEV && ARCH_SHMOBILE
  526 + select VIDEOBUF_DMA_CONTIG
  527 + help
  528 + Support for the Video Output Unit (VOU) on SuperH SoCs.
  529 +
523 530 config CAPTURE_DAVINCI_DM646X_EVM
524 531 tristate "DM646x EVM Video Capture"
525 532 depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
drivers/media/video/Makefile
... ... @@ -160,6 +160,10 @@
160 160 obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
161 161 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
162 162  
  163 +obj-$(CONFIG_ARCH_DAVINCI) += davinci/
  164 +
  165 +obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
  166 +
163 167 obj-$(CONFIG_VIDEO_AU0828) += au0828/
164 168  
165 169 obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/
drivers/media/video/sh_vou.c
Changes suppressed. Click to show
  1 +/*
  2 + * SuperH Video Output Unit (VOU) driver
  3 + *
  4 + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License version 2 as
  8 + * published by the Free Software Foundation.
  9 + */
  10 +
  11 +#include <linux/dma-mapping.h>
  12 +#include <linux/delay.h>
  13 +#include <linux/errno.h>
  14 +#include <linux/fs.h>
  15 +#include <linux/i2c.h>
  16 +#include <linux/init.h>
  17 +#include <linux/interrupt.h>
  18 +#include <linux/kernel.h>
  19 +#include <linux/platform_device.h>
  20 +#include <linux/pm_runtime.h>
  21 +#include <linux/version.h>
  22 +#include <linux/videodev2.h>
  23 +
  24 +#include <media/sh_vou.h>
  25 +#include <media/v4l2-common.h>
  26 +#include <media/v4l2-device.h>
  27 +#include <media/v4l2-ioctl.h>
  28 +#include <media/v4l2-mediabus.h>
  29 +#include <media/videobuf-dma-contig.h>
  30 +
  31 +/* Mirror addresses are not available for all registers */
  32 +#define VOUER 0
  33 +#define VOUCR 4
  34 +#define VOUSTR 8
  35 +#define VOUVCR 0xc
  36 +#define VOUISR 0x10
  37 +#define VOUBCR 0x14
  38 +#define VOUDPR 0x18
  39 +#define VOUDSR 0x1c
  40 +#define VOUVPR 0x20
  41 +#define VOUIR 0x24
  42 +#define VOUSRR 0x28
  43 +#define VOUMSR 0x2c
  44 +#define VOUHIR 0x30
  45 +#define VOUDFR 0x34
  46 +#define VOUAD1R 0x38
  47 +#define VOUAD2R 0x3c
  48 +#define VOUAIR 0x40
  49 +#define VOUSWR 0x44
  50 +#define VOURCR 0x48
  51 +#define VOURPR 0x50
  52 +
  53 +enum sh_vou_status {
  54 + SH_VOU_IDLE,
  55 + SH_VOU_INITIALISING,
  56 + SH_VOU_RUNNING,
  57 +};
  58 +
  59 +#define VOU_MAX_IMAGE_WIDTH 720
  60 +#define VOU_MAX_IMAGE_HEIGHT 480
  61 +
  62 +struct sh_vou_device {
  63 + struct v4l2_device v4l2_dev;
  64 + struct video_device *vdev;
  65 + atomic_t use_count;
  66 + struct sh_vou_pdata *pdata;
  67 + spinlock_t lock;
  68 + void __iomem *base;
  69 + /* State information */
  70 + struct v4l2_pix_format pix;
  71 + struct v4l2_rect rect;
  72 + struct list_head queue;
  73 + v4l2_std_id std;
  74 + int pix_idx;
  75 + struct videobuf_buffer *active;
  76 + enum sh_vou_status status;
  77 +};
  78 +
  79 +struct sh_vou_file {
  80 + struct videobuf_queue vbq;
  81 +};
  82 +
  83 +/* Register access routines for sides A, B and mirror addresses */
  84 +static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg,
  85 + u32 value)
  86 +{
  87 + __raw_writel(value, vou_dev->base + reg);
  88 +}
  89 +
  90 +static void sh_vou_reg_ab_write(struct sh_vou_device *vou_dev, unsigned int reg,
  91 + u32 value)
  92 +{
  93 + __raw_writel(value, vou_dev->base + reg);
  94 + __raw_writel(value, vou_dev->base + reg + 0x1000);
  95 +}
  96 +
  97 +static void sh_vou_reg_m_write(struct sh_vou_device *vou_dev, unsigned int reg,
  98 + u32 value)
  99 +{
  100 + __raw_writel(value, vou_dev->base + reg + 0x2000);
  101 +}
  102 +
  103 +static u32 sh_vou_reg_a_read(struct sh_vou_device *vou_dev, unsigned int reg)
  104 +{
  105 + return __raw_readl(vou_dev->base + reg);
  106 +}
  107 +
  108 +static void sh_vou_reg_a_set(struct sh_vou_device *vou_dev, unsigned int reg,
  109 + u32 value, u32 mask)
  110 +{
  111 + u32 old = __raw_readl(vou_dev->base + reg);
  112 +
  113 + value = (value & mask) | (old & ~mask);
  114 + __raw_writel(value, vou_dev->base + reg);
  115 +}
  116 +
  117 +static void sh_vou_reg_b_set(struct sh_vou_device *vou_dev, unsigned int reg,
  118 + u32 value, u32 mask)
  119 +{
  120 + sh_vou_reg_a_set(vou_dev, reg + 0x1000, value, mask);
  121 +}
  122 +
  123 +static void sh_vou_reg_ab_set(struct sh_vou_device *vou_dev, unsigned int reg,
  124 + u32 value, u32 mask)
  125 +{
  126 + sh_vou_reg_a_set(vou_dev, reg, value, mask);
  127 + sh_vou_reg_b_set(vou_dev, reg, value, mask);
  128 +}
  129 +
  130 +struct sh_vou_fmt {
  131 + u32 pfmt;
  132 + char *desc;
  133 + unsigned char bpp;
  134 + unsigned char rgb;
  135 + unsigned char yf;
  136 + unsigned char pkf;
  137 +};
  138 +
  139 +/* Further pixel formats can be added */
  140 +static struct sh_vou_fmt vou_fmt[] = {
  141 + {
  142 + .pfmt = V4L2_PIX_FMT_NV12,
  143 + .bpp = 12,
  144 + .desc = "YVU420 planar",
  145 + .yf = 0,
  146 + .rgb = 0,
  147 + },
  148 + {
  149 + .pfmt = V4L2_PIX_FMT_NV16,
  150 + .bpp = 16,
  151 + .desc = "YVYU planar",
  152 + .yf = 1,
  153 + .rgb = 0,
  154 + },
  155 + {
  156 + .pfmt = V4L2_PIX_FMT_RGB24,
  157 + .bpp = 24,
  158 + .desc = "RGB24",
  159 + .pkf = 2,
  160 + .rgb = 1,
  161 + },
  162 + {
  163 + .pfmt = V4L2_PIX_FMT_RGB565,
  164 + .bpp = 16,
  165 + .desc = "RGB565",
  166 + .pkf = 3,
  167 + .rgb = 1,
  168 + },
  169 + {
  170 + .pfmt = V4L2_PIX_FMT_RGB565X,
  171 + .bpp = 16,
  172 + .desc = "RGB565 byteswapped",
  173 + .pkf = 3,
  174 + .rgb = 1,
  175 + },
  176 +};
  177 +
  178 +static void sh_vou_schedule_next(struct sh_vou_device *vou_dev,
  179 + struct videobuf_buffer *vb)
  180 +{
  181 + dma_addr_t addr1, addr2;
  182 +
  183 + addr1 = videobuf_to_dma_contig(vb);
  184 + switch (vou_dev->pix.pixelformat) {
  185 + case V4L2_PIX_FMT_NV12:
  186 + case V4L2_PIX_FMT_NV16:
  187 + addr2 = addr1 + vou_dev->pix.width * vou_dev->pix.height;
  188 + break;
  189 + default:
  190 + addr2 = 0;
  191 + }
  192 +
  193 + sh_vou_reg_m_write(vou_dev, VOUAD1R, addr1);
  194 + sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2);
  195 +}
  196 +
  197 +static void sh_vou_stream_start(struct sh_vou_device *vou_dev,
  198 + struct videobuf_buffer *vb)
  199 +{
  200 + unsigned int row_coeff;
  201 +#ifdef __LITTLE_ENDIAN
  202 + u32 dataswap = 7;
  203 +#else
  204 + u32 dataswap = 0;
  205 +#endif
  206 +
  207 + switch (vou_dev->pix.pixelformat) {
  208 + case V4L2_PIX_FMT_NV12:
  209 + case V4L2_PIX_FMT_NV16:
  210 + row_coeff = 1;
  211 + break;
  212 + case V4L2_PIX_FMT_RGB565:
  213 + dataswap ^= 1;
  214 + case V4L2_PIX_FMT_RGB565X:
  215 + row_coeff = 2;
  216 + break;
  217 + case V4L2_PIX_FMT_RGB24:
  218 + row_coeff = 3;
  219 + break;
  220 + }
  221 +
  222 + sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap);
  223 + sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff);
  224 + sh_vou_schedule_next(vou_dev, vb);
  225 +}
  226 +
  227 +static void free_buffer(struct videobuf_queue *vq, struct videobuf_buffer *vb)
  228 +{
  229 + BUG_ON(in_interrupt());
  230 +
  231 + /* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */
  232 + videobuf_waiton(vb, 0, 0);
  233 + videobuf_dma_contig_free(vq, vb);
  234 + vb->state = VIDEOBUF_NEEDS_INIT;
  235 +}
  236 +
  237 +/* Locking: caller holds vq->vb_lock mutex */
  238 +static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count,
  239 + unsigned int *size)
  240 +{
  241 + struct video_device *vdev = vq->priv_data;
  242 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  243 +
  244 + *size = vou_fmt[vou_dev->pix_idx].bpp * vou_dev->pix.width *
  245 + vou_dev->pix.height / 8;
  246 +
  247 + if (*count < 2)
  248 + *count = 2;
  249 +
  250 + /* Taking into account maximum frame size, *count will stay >= 2 */
  251 + if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024)
  252 + *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size);
  253 +
  254 + dev_dbg(vq->dev, "%s(): count=%d, size=%d\n", __func__, *count, *size);
  255 +
  256 + return 0;
  257 +}
  258 +
  259 +/* Locking: caller holds vq->vb_lock mutex */
  260 +static int sh_vou_buf_prepare(struct videobuf_queue *vq,
  261 + struct videobuf_buffer *vb,
  262 + enum v4l2_field field)
  263 +{
  264 + struct video_device *vdev = vq->priv_data;
  265 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  266 + struct v4l2_pix_format *pix = &vou_dev->pix;
  267 + int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
  268 + int ret;
  269 +
  270 + dev_dbg(vq->dev, "%s()\n", __func__);
  271 +
  272 + if (vb->width != pix->width ||
  273 + vb->height != pix->height ||
  274 + vb->field != pix->field) {
  275 + vb->width = pix->width;
  276 + vb->height = pix->height;
  277 + vb->field = field;
  278 + if (vb->state != VIDEOBUF_NEEDS_INIT)
  279 + free_buffer(vq, vb);
  280 + }
  281 +
  282 + vb->size = vb->height * bytes_per_line;
  283 + if (vb->baddr && vb->bsize < vb->size) {
  284 + /* User buffer too small */
  285 + dev_warn(vq->dev, "User buffer too small: [%u] @ %lx\n",
  286 + vb->bsize, vb->baddr);
  287 + return -EINVAL;
  288 + }
  289 +
  290 + if (vb->state == VIDEOBUF_NEEDS_INIT) {
  291 + ret = videobuf_iolock(vq, vb, NULL);
  292 + if (ret < 0) {
  293 + dev_warn(vq->dev, "IOLOCK buf-type %d: %d\n",
  294 + vb->memory, ret);
  295 + return ret;
  296 + }
  297 + vb->state = VIDEOBUF_PREPARED;
  298 + }
  299 +
  300 + dev_dbg(vq->dev,
  301 + "%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n",
  302 + __func__, vou_dev->pix_idx, bytes_per_line,
  303 + videobuf_to_dma_contig(vb), vb->memory, vb->state);
  304 +
  305 + return 0;
  306 +}
  307 +
  308 +/* Locking: caller holds vq->vb_lock mutex and vq->irqlock spinlock */
  309 +static void sh_vou_buf_queue(struct videobuf_queue *vq,
  310 + struct videobuf_buffer *vb)
  311 +{
  312 + struct video_device *vdev = vq->priv_data;
  313 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  314 +
  315 + dev_dbg(vq->dev, "%s()\n", __func__);
  316 +
  317 + vb->state = VIDEOBUF_QUEUED;
  318 + list_add_tail(&vb->queue, &vou_dev->queue);
  319 +
  320 + if (vou_dev->status == SH_VOU_RUNNING) {
  321 + return;
  322 + } else if (!vou_dev->active) {
  323 + vou_dev->active = vb;
  324 + /* Start from side A: we use mirror addresses, so, set B */
  325 + sh_vou_reg_a_write(vou_dev, VOURPR, 1);
  326 + dev_dbg(vq->dev, "%s: first buffer status 0x%x\n", __func__,
  327 + sh_vou_reg_a_read(vou_dev, VOUSTR));
  328 + sh_vou_schedule_next(vou_dev, vb);
  329 + /* Only activate VOU after the second buffer */
  330 + } else if (vou_dev->active->queue.next == &vb->queue) {
  331 + /* Second buffer - initialise register side B */
  332 + sh_vou_reg_a_write(vou_dev, VOURPR, 0);
  333 + sh_vou_stream_start(vou_dev, vb);
  334 +
  335 + /* Register side switching with frame VSYNC */
  336 + sh_vou_reg_a_write(vou_dev, VOURCR, 5);
  337 + dev_dbg(vq->dev, "%s: second buffer status 0x%x\n", __func__,
  338 + sh_vou_reg_a_read(vou_dev, VOUSTR));
  339 +
  340 + /* Enable End-of-Frame (VSYNC) interrupts */
  341 + sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004);
  342 + /* Two buffers on the queue - activate the hardware */
  343 +
  344 + vou_dev->status = SH_VOU_RUNNING;
  345 + sh_vou_reg_a_write(vou_dev, VOUER, 0x107);
  346 + }
  347 +}
  348 +
  349 +static void sh_vou_buf_release(struct videobuf_queue *vq,
  350 + struct videobuf_buffer *vb)
  351 +{
  352 + struct video_device *vdev = vq->priv_data;
  353 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  354 + unsigned long flags;
  355 +
  356 + dev_dbg(vq->dev, "%s()\n", __func__);
  357 +
  358 + spin_lock_irqsave(&vou_dev->lock, flags);
  359 +
  360 + if (vou_dev->active == vb) {
  361 + /* disable output */
  362 + sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
  363 + /* ...but the current frame will complete */
  364 + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
  365 + vou_dev->active = NULL;
  366 + }
  367 +
  368 + if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) {
  369 + vb->state = VIDEOBUF_ERROR;
  370 + list_del(&vb->queue);
  371 + }
  372 +
  373 + spin_unlock_irqrestore(&vou_dev->lock, flags);
  374 +
  375 + free_buffer(vq, vb);
  376 +}
  377 +
  378 +static struct videobuf_queue_ops sh_vou_video_qops = {
  379 + .buf_setup = sh_vou_buf_setup,
  380 + .buf_prepare = sh_vou_buf_prepare,
  381 + .buf_queue = sh_vou_buf_queue,
  382 + .buf_release = sh_vou_buf_release,
  383 +};
  384 +
  385 +/* Video IOCTLs */
  386 +static int sh_vou_querycap(struct file *file, void *priv,
  387 + struct v4l2_capability *cap)
  388 +{
  389 + struct sh_vou_file *vou_file = priv;
  390 +
  391 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  392 +
  393 + strlcpy(cap->card, "SuperH VOU", sizeof(cap->card));
  394 + cap->version = KERNEL_VERSION(0, 1, 0);
  395 + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
  396 + return 0;
  397 +}
  398 +
  399 +/* Enumerate formats, that the device can accept from the user */
  400 +static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv,
  401 + struct v4l2_fmtdesc *fmt)
  402 +{
  403 + struct sh_vou_file *vou_file = priv;
  404 +
  405 + if (fmt->index >= ARRAY_SIZE(vou_fmt))
  406 + return -EINVAL;
  407 +
  408 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  409 +
  410 + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
  411 + strlcpy(fmt->description, vou_fmt[fmt->index].desc,
  412 + sizeof(fmt->description));
  413 + fmt->pixelformat = vou_fmt[fmt->index].pfmt;
  414 +
  415 + return 0;
  416 +}
  417 +
  418 +static int sh_vou_g_fmt_vid_out(struct file *file, void *priv,
  419 + struct v4l2_format *fmt)
  420 +{
  421 + struct video_device *vdev = video_devdata(file);
  422 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  423 +
  424 + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
  425 +
  426 + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
  427 + fmt->fmt.pix = vou_dev->pix;
  428 +
  429 + return 0;
  430 +}
  431 +
  432 +static const unsigned char vou_scale_h_num[] = {1, 9, 2, 9, 4};
  433 +static const unsigned char vou_scale_h_den[] = {1, 8, 1, 4, 1};
  434 +static const unsigned char vou_scale_h_fld[] = {0, 2, 1, 3};
  435 +static const unsigned char vou_scale_v_num[] = {1, 2, 4};
  436 +static const unsigned char vou_scale_v_den[] = {1, 1, 1};
  437 +static const unsigned char vou_scale_v_fld[] = {0, 1};
  438 +
  439 +static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev,
  440 + int pix_idx, int w_idx, int h_idx)
  441 +{
  442 + struct sh_vou_fmt *fmt = vou_fmt + pix_idx;
  443 + unsigned int black_left, black_top, width_max, height_max,
  444 + frame_in_height, frame_out_height, frame_out_top;
  445 + struct v4l2_rect *rect = &vou_dev->rect;
  446 + struct v4l2_pix_format *pix = &vou_dev->pix;
  447 + u32 vouvcr = 0, dsr_h, dsr_v;
  448 +
  449 + if (vou_dev->std & V4L2_STD_525_60) {
  450 + width_max = 858;
  451 + height_max = 262;
  452 + } else {
  453 + width_max = 864;
  454 + height_max = 312;
  455 + }
  456 +
  457 + frame_in_height = pix->height / 2;
  458 + frame_out_height = rect->height / 2;
  459 + frame_out_top = rect->top / 2;
  460 +
  461 + /*
  462 + * Cropping scheme: max useful image is 720x480, and the total video
  463 + * area is 858x525 (NTSC) or 864x625 (PAL). AK8813 / 8814 starts
  464 + * sampling data beginning with fixed 276th (NTSC) / 288th (PAL) clock,
  465 + * of which the first 33 / 25 clocks HSYNC must be held active. This
  466 + * has to be configured in CR[HW]. 1 pixel equals 2 clock periods.
  467 + * This gives CR[HW] = 16 / 12, VPR[HVP] = 138 / 144, which gives
  468 + * exactly 858 - 138 = 864 - 144 = 720! We call the out-of-display area,
  469 + * beyond DSR, specified on the left and top by the VPR register "black
  470 + * pixels" and out-of-image area (DPR) "background pixels." We fix VPR
  471 + * at 138 / 144 : 20, because that's the HSYNC timing, that our first
  472 + * client requires, and that's exactly what leaves us 720 pixels for the
  473 + * image; we leave VPR[VVP] at default 20 for now, because the client
  474 + * doesn't seem to have any special requirements for it. Otherwise we
  475 + * could also set it to max - 240 = 22 / 72. Thus VPR depends only on
  476 + * the selected standard, and DPR and DSR are selected according to
  477 + * cropping. Q: how does the client detect the first valid line? Does
  478 + * HSYNC stay inactive during invalid (black) lines?
  479 + */
  480 + black_left = width_max - VOU_MAX_IMAGE_WIDTH;
  481 + black_top = 20;
  482 +
  483 + dsr_h = rect->width + rect->left;
  484 + dsr_v = frame_out_height + frame_out_top;
  485 +
  486 + dev_dbg(vou_dev->v4l2_dev.dev,
  487 + "image %ux%u, black %u:%u, offset %u:%u, display %ux%u\n",
  488 + pix->width, frame_in_height, black_left, black_top,
  489 + rect->left, frame_out_top, dsr_h, dsr_v);
  490 +
  491 + /* VOUISR height - half of a frame height in frame mode */
  492 + sh_vou_reg_ab_write(vou_dev, VOUISR, (pix->width << 16) | frame_in_height);
  493 + sh_vou_reg_ab_write(vou_dev, VOUVPR, (black_left << 16) | black_top);
  494 + sh_vou_reg_ab_write(vou_dev, VOUDPR, (rect->left << 16) | frame_out_top);
  495 + sh_vou_reg_ab_write(vou_dev, VOUDSR, (dsr_h << 16) | dsr_v);
  496 +
  497 + /*
  498 + * if necessary, we could set VOUHIR to
  499 + * max(black_left + dsr_h, width_max) here
  500 + */
  501 +
  502 + if (w_idx)
  503 + vouvcr |= (1 << 15) | (vou_scale_h_fld[w_idx - 1] << 4);
  504 + if (h_idx)
  505 + vouvcr |= (1 << 14) | vou_scale_v_fld[h_idx - 1];
  506 +
  507 + dev_dbg(vou_dev->v4l2_dev.dev, "%s: scaling 0x%x\n", fmt->desc, vouvcr);
  508 +
  509 + /* To produce a colour bar for testing set bit 23 of VOUVCR */
  510 + sh_vou_reg_ab_write(vou_dev, VOUVCR, vouvcr);
  511 + sh_vou_reg_ab_write(vou_dev, VOUDFR,
  512 + fmt->pkf | (fmt->yf << 8) | (fmt->rgb << 16));
  513 +}
  514 +
  515 +struct sh_vou_geometry {
  516 + struct v4l2_rect output;
  517 + unsigned int in_width;
  518 + unsigned int in_height;
  519 + int scale_idx_h;
  520 + int scale_idx_v;
  521 +};
  522 +
  523 +/*
  524 + * Find input geometry, that we can use to produce output, closest to the
  525 + * requested rectangle, using VOU scaling
  526 + */
  527 +static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std)
  528 +{
  529 + /* The compiler cannot know, that best and idx will indeed be set */
  530 + unsigned int best_err = UINT_MAX, best = 0, width_max, height_max;
  531 + int i, idx = 0;
  532 +
  533 + if (std & V4L2_STD_525_60) {
  534 + width_max = 858;
  535 + height_max = 262;
  536 + } else {
  537 + width_max = 864;
  538 + height_max = 312;
  539 + }
  540 +
  541 + /* Image width must be a multiple of 4 */
  542 + v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2,
  543 + &geo->in_height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
  544 +
  545 + /* Select scales to come as close as possible to the output image */
  546 + for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) {
  547 + unsigned int err;
  548 + unsigned int found = geo->output.width * vou_scale_h_den[i] /
  549 + vou_scale_h_num[i];
  550 +
  551 + if (found > VOU_MAX_IMAGE_WIDTH)
  552 + /* scales increase */
  553 + break;
  554 +
  555 + err = abs(found - geo->in_width);
  556 + if (err < best_err) {
  557 + best_err = err;
  558 + idx = i;
  559 + best = found;
  560 + }
  561 + if (!err)
  562 + break;
  563 + }
  564 +
  565 + geo->in_width = best;
  566 + geo->scale_idx_h = idx;
  567 +
  568 + best_err = UINT_MAX;
  569 +
  570 + /* This loop can be replaced with one division */
  571 + for (i = ARRAY_SIZE(vou_scale_v_num) - 1; i >= 0; i--) {
  572 + unsigned int err;
  573 + unsigned int found = geo->output.height * vou_scale_v_den[i] /
  574 + vou_scale_v_num[i];
  575 +
  576 + if (found > VOU_MAX_IMAGE_HEIGHT)
  577 + /* scales increase */
  578 + break;
  579 +
  580 + err = abs(found - geo->in_height);
  581 + if (err < best_err) {
  582 + best_err = err;
  583 + idx = i;
  584 + best = found;
  585 + }
  586 + if (!err)
  587 + break;
  588 + }
  589 +
  590 + geo->in_height = best;
  591 + geo->scale_idx_v = idx;
  592 +}
  593 +
  594 +/*
  595 + * Find output geometry, that we can produce, using VOU scaling, closest to
  596 + * the requested rectangle
  597 + */
  598 +static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
  599 +{
  600 + unsigned int best_err = UINT_MAX, best, width_max, height_max;
  601 + int i, idx;
  602 +
  603 + if (std & V4L2_STD_525_60) {
  604 + width_max = 858;
  605 + height_max = 262 * 2;
  606 + } else {
  607 + width_max = 864;
  608 + height_max = 312 * 2;
  609 + }
  610 +
  611 + /* Select scales to come as close as possible to the output image */
  612 + for (i = 0; i < ARRAY_SIZE(vou_scale_h_num); i++) {
  613 + unsigned int err;
  614 + unsigned int found = geo->in_width * vou_scale_h_num[i] /
  615 + vou_scale_h_den[i];
  616 +
  617 + if (found > VOU_MAX_IMAGE_WIDTH)
  618 + /* scales increase */
  619 + break;
  620 +
  621 + err = abs(found - geo->output.width);
  622 + if (err < best_err) {
  623 + best_err = err;
  624 + idx = i;
  625 + best = found;
  626 + }
  627 + if (!err)
  628 + break;
  629 + }
  630 +
  631 + geo->output.width = best;
  632 + geo->scale_idx_h = idx;
  633 + if (geo->output.left + best > width_max)
  634 + geo->output.left = width_max - best;
  635 +
  636 + pr_debug("%s(): W %u * %u/%u = %u\n", __func__, geo->in_width,
  637 + vou_scale_h_num[idx], vou_scale_h_den[idx], best);
  638 +
  639 + best_err = UINT_MAX;
  640 +
  641 + /* This loop can be replaced with one division */
  642 + for (i = 0; i < ARRAY_SIZE(vou_scale_v_num); i++) {
  643 + unsigned int err;
  644 + unsigned int found = geo->in_height * vou_scale_v_num[i] /
  645 + vou_scale_v_den[i];
  646 +
  647 + if (found > VOU_MAX_IMAGE_HEIGHT)
  648 + /* scales increase */
  649 + break;
  650 +
  651 + err = abs(found - geo->output.height);
  652 + if (err < best_err) {
  653 + best_err = err;
  654 + idx = i;
  655 + best = found;
  656 + }
  657 + if (!err)
  658 + break;
  659 + }
  660 +
  661 + geo->output.height = best;
  662 + geo->scale_idx_v = idx;
  663 + if (geo->output.top + best > height_max)
  664 + geo->output.top = height_max - best;
  665 +
  666 + pr_debug("%s(): H %u * %u/%u = %u\n", __func__, geo->in_height,
  667 + vou_scale_v_num[idx], vou_scale_v_den[idx], best);
  668 +}
  669 +
  670 +static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
  671 + struct v4l2_format *fmt)
  672 +{
  673 + struct video_device *vdev = video_devdata(file);
  674 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  675 + struct v4l2_pix_format *pix = &fmt->fmt.pix;
  676 + int pix_idx;
  677 + struct sh_vou_geometry geo;
  678 + struct v4l2_mbus_framefmt mbfmt = {
  679 + /* Revisit: is this the correct code? */
  680 + .code = V4L2_MBUS_FMT_YUYV8_2X8_LE,
  681 + .field = V4L2_FIELD_INTERLACED,
  682 + .colorspace = V4L2_COLORSPACE_SMPTE170M,
  683 + };
  684 + int ret;
  685 +
  686 + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
  687 + vou_dev->rect.width, vou_dev->rect.height,
  688 + pix->width, pix->height);
  689 +
  690 + if (pix->field == V4L2_FIELD_ANY)
  691 + pix->field = V4L2_FIELD_NONE;
  692 +
  693 + if (fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
  694 + pix->field != V4L2_FIELD_NONE)
  695 + return -EINVAL;
  696 +
  697 + for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++)
  698 + if (vou_fmt[pix_idx].pfmt == pix->pixelformat)
  699 + break;
  700 +
  701 + if (pix_idx == ARRAY_SIZE(vou_fmt))
  702 + return -EINVAL;
  703 +
  704 + /* Image width must be a multiple of 4 */
  705 + v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2,
  706 + &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
  707 +
  708 + geo.in_width = pix->width;
  709 + geo.in_height = pix->height;
  710 + geo.output = vou_dev->rect;
  711 +
  712 + vou_adjust_output(&geo, vou_dev->std);
  713 +
  714 + mbfmt.width = geo.output.width;
  715 + mbfmt.height = geo.output.height;
  716 + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
  717 + s_mbus_fmt, &mbfmt);
  718 + /* Must be implemented, so, don't check for -ENOIOCTLCMD */
  719 + if (ret < 0)
  720 + return ret;
  721 +
  722 + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
  723 + geo.output.width, geo.output.height, mbfmt.width, mbfmt.height);
  724 +
  725 + /* Sanity checks */
  726 + if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
  727 + (unsigned)mbfmt.height > VOU_MAX_IMAGE_HEIGHT ||
  728 + mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8_LE)
  729 + return -EIO;
  730 +
  731 + if (mbfmt.width != geo.output.width ||
  732 + mbfmt.height != geo.output.height) {
  733 + geo.output.width = mbfmt.width;
  734 + geo.output.height = mbfmt.height;
  735 +
  736 + vou_adjust_input(&geo, vou_dev->std);
  737 + }
  738 +
  739 + /* We tried to preserve output rectangle, but it could have changed */
  740 + vou_dev->rect = geo.output;
  741 + pix->width = geo.in_width;
  742 + pix->height = geo.in_height;
  743 +
  744 + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u\n", __func__,
  745 + pix->width, pix->height);
  746 +
  747 + vou_dev->pix_idx = pix_idx;
  748 +
  749 + vou_dev->pix = *pix;
  750 +
  751 + sh_vou_configure_geometry(vou_dev, pix_idx,
  752 + geo.scale_idx_h, geo.scale_idx_v);
  753 +
  754 + return 0;
  755 +}
  756 +
  757 +static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
  758 + struct v4l2_format *fmt)
  759 +{
  760 + struct sh_vou_file *vou_file = priv;
  761 + struct v4l2_pix_format *pix = &fmt->fmt.pix;
  762 + int i;
  763 +
  764 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  765 +
  766 + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
  767 + pix->field = V4L2_FIELD_NONE;
  768 +
  769 + v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
  770 + &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
  771 +
  772 + for (i = 0; ARRAY_SIZE(vou_fmt); i++)
  773 + if (vou_fmt[i].pfmt == pix->pixelformat)
  774 + return 0;
  775 +
  776 + pix->pixelformat = vou_fmt[0].pfmt;
  777 +
  778 + return 0;
  779 +}
  780 +
  781 +static int sh_vou_reqbufs(struct file *file, void *priv,
  782 + struct v4l2_requestbuffers *req)
  783 +{
  784 + struct sh_vou_file *vou_file = priv;
  785 +
  786 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  787 +
  788 + if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
  789 + return -EINVAL;
  790 +
  791 + return videobuf_reqbufs(&vou_file->vbq, req);
  792 +}
  793 +
  794 +static int sh_vou_querybuf(struct file *file, void *priv,
  795 + struct v4l2_buffer *b)
  796 +{
  797 + struct sh_vou_file *vou_file = priv;
  798 +
  799 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  800 +
  801 + return videobuf_querybuf(&vou_file->vbq, b);
  802 +}
  803 +
  804 +static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
  805 +{
  806 + struct sh_vou_file *vou_file = priv;
  807 +
  808 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  809 +
  810 + return videobuf_qbuf(&vou_file->vbq, b);
  811 +}
  812 +
  813 +static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
  814 +{
  815 + struct sh_vou_file *vou_file = priv;
  816 +
  817 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  818 +
  819 + return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK);
  820 +}
  821 +
  822 +static int sh_vou_streamon(struct file *file, void *priv,
  823 + enum v4l2_buf_type buftype)
  824 +{
  825 + struct video_device *vdev = video_devdata(file);
  826 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  827 + struct sh_vou_file *vou_file = priv;
  828 + int ret;
  829 +
  830 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  831 +
  832 + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
  833 + video, s_stream, 1);
  834 + if (ret < 0 && ret != -ENOIOCTLCMD)
  835 + return ret;
  836 +
  837 + /* This calls our .buf_queue() (== sh_vou_buf_queue) */
  838 + return videobuf_streamon(&vou_file->vbq);
  839 +}
  840 +
  841 +static int sh_vou_streamoff(struct file *file, void *priv,
  842 + enum v4l2_buf_type buftype)
  843 +{
  844 + struct video_device *vdev = video_devdata(file);
  845 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  846 + struct sh_vou_file *vou_file = priv;
  847 +
  848 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  849 +
  850 + /*
  851 + * This calls buf_release from host driver's videobuf_queue_ops for all
  852 + * remaining buffers. When the last buffer is freed, stop streaming
  853 + */
  854 + videobuf_streamoff(&vou_file->vbq);
  855 + v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 0);
  856 +
  857 + return 0;
  858 +}
  859 +
  860 +static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt)
  861 +{
  862 + switch (bus_fmt) {
  863 + default:
  864 + pr_warning("%s(): Invalid bus-format code %d, using default 8-bit\n",
  865 + __func__, bus_fmt);
  866 + case SH_VOU_BUS_8BIT:
  867 + return 1;
  868 + case SH_VOU_BUS_16BIT:
  869 + return 0;
  870 + case SH_VOU_BUS_BT656:
  871 + return 3;
  872 + }
  873 +}
  874 +
  875 +static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
  876 +{
  877 + struct video_device *vdev = video_devdata(file);
  878 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  879 + int ret;
  880 +
  881 + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id);
  882 +
  883 + if (*std_id & ~vdev->tvnorms)
  884 + return -EINVAL;
  885 +
  886 + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
  887 + s_std_output, *std_id);
  888 + /* Shall we continue, if the subdev doesn't support .s_std_output()? */
  889 + if (ret < 0 && ret != -ENOIOCTLCMD)
  890 + return ret;
  891 +
  892 + if (*std_id & V4L2_STD_525_60)
  893 + sh_vou_reg_ab_set(vou_dev, VOUCR,
  894 + sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29);
  895 + else
  896 + sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29);
  897 +
  898 + vou_dev->std = *std_id;
  899 +
  900 + return 0;
  901 +}
  902 +
  903 +static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std)
  904 +{
  905 + struct video_device *vdev = video_devdata(file);
  906 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  907 +
  908 + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
  909 +
  910 + *std = vou_dev->std;
  911 +
  912 + return 0;
  913 +}
  914 +
  915 +static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
  916 +{
  917 + struct video_device *vdev = video_devdata(file);
  918 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  919 +
  920 + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
  921 +
  922 + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
  923 + a->c = vou_dev->rect;
  924 +
  925 + return 0;
  926 +}
  927 +
  928 +/* Assume a dull encoder, do all the work ourselves. */
  929 +static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a)
  930 +{
  931 + struct video_device *vdev = video_devdata(file);
  932 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  933 + struct v4l2_rect *rect = &a->c;
  934 + struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT};
  935 + struct v4l2_pix_format *pix = &vou_dev->pix;
  936 + struct sh_vou_geometry geo;
  937 + struct v4l2_mbus_framefmt mbfmt = {
  938 + /* Revisit: is this the correct code? */
  939 + .code = V4L2_MBUS_FMT_YUYV8_2X8_LE,
  940 + .field = V4L2_FIELD_INTERLACED,
  941 + .colorspace = V4L2_COLORSPACE_SMPTE170M,
  942 + };
  943 + int ret;
  944 +
  945 + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__,
  946 + rect->width, rect->height, rect->left, rect->top);
  947 +
  948 + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
  949 + return -EINVAL;
  950 +
  951 + v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
  952 + &rect->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
  953 +
  954 + if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH)
  955 + rect->left = VOU_MAX_IMAGE_WIDTH - rect->width;
  956 +
  957 + if (rect->height + rect->top > VOU_MAX_IMAGE_HEIGHT)
  958 + rect->top = VOU_MAX_IMAGE_HEIGHT - rect->height;
  959 +
  960 + geo.output = *rect;
  961 + geo.in_width = pix->width;
  962 + geo.in_height = pix->height;
  963 +
  964 + /* Configure the encoder one-to-one, position at 0, ignore errors */
  965 + sd_crop.c.width = geo.output.width;
  966 + sd_crop.c.height = geo.output.height;
  967 + /*
  968 + * We first issue a S_CROP, so that the subsequent S_FMT delivers the
  969 + * final encoder configuration.
  970 + */
  971 + v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
  972 + s_crop, &sd_crop);
  973 + mbfmt.width = geo.output.width;
  974 + mbfmt.height = geo.output.height;
  975 + ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
  976 + s_mbus_fmt, &mbfmt);
  977 + /* Must be implemented, so, don't check for -ENOIOCTLCMD */
  978 + if (ret < 0)
  979 + return ret;
  980 +
  981 + /* Sanity checks */
  982 + if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
  983 + (unsigned)mbfmt.height > VOU_MAX_IMAGE_HEIGHT ||
  984 + mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8_LE)
  985 + return -EIO;
  986 +
  987 + geo.output.width = mbfmt.width;
  988 + geo.output.height = mbfmt.height;
  989 +
  990 + /*
  991 + * No down-scaling. According to the API, current call has precedence:
  992 + * http://v4l2spec.bytesex.org/spec/x1904.htm#AEN1954 paragraph two.
  993 + */
  994 + vou_adjust_input(&geo, vou_dev->std);
  995 +
  996 + /* We tried to preserve output rectangle, but it could have changed */
  997 + vou_dev->rect = geo.output;
  998 + pix->width = geo.in_width;
  999 + pix->height = geo.in_height;
  1000 +
  1001 + sh_vou_configure_geometry(vou_dev, vou_dev->pix_idx,
  1002 + geo.scale_idx_h, geo.scale_idx_v);
  1003 +
  1004 + return 0;
  1005 +}
  1006 +
  1007 +/*
  1008 + * Total field: NTSC 858 x 2 * 262/263, PAL 864 x 2 * 312/313, default rectangle
  1009 + * is the initial register values, height takes the interlaced format into
  1010 + * account. The actual image can only go up to 720 x 2 * 240, So, VOUVPR can
  1011 + * actually only meaningfully contain values <= 720 and <= 240 respectively, and
  1012 + * not <= 864 and <= 312.
  1013 + */
  1014 +static int sh_vou_cropcap(struct file *file, void *priv,
  1015 + struct v4l2_cropcap *a)
  1016 +{
  1017 + struct sh_vou_file *vou_file = priv;
  1018 +
  1019 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  1020 +
  1021 + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
  1022 + a->bounds.left = 0;
  1023 + a->bounds.top = 0;
  1024 + a->bounds.width = VOU_MAX_IMAGE_WIDTH;
  1025 + a->bounds.height = VOU_MAX_IMAGE_HEIGHT;
  1026 + /* Default = max, set VOUDPR = 0, which is not hardware default */
  1027 + a->defrect.left = 0;
  1028 + a->defrect.top = 0;
  1029 + a->defrect.width = VOU_MAX_IMAGE_WIDTH;
  1030 + a->defrect.height = VOU_MAX_IMAGE_HEIGHT;
  1031 + a->pixelaspect.numerator = 1;
  1032 + a->pixelaspect.denominator = 1;
  1033 +
  1034 + return 0;
  1035 +}
  1036 +
  1037 +static irqreturn_t sh_vou_isr(int irq, void *dev_id)
  1038 +{
  1039 + struct sh_vou_device *vou_dev = dev_id;
  1040 + static unsigned long j;
  1041 + struct videobuf_buffer *vb;
  1042 + static int cnt;
  1043 + static int side;
  1044 + u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked;
  1045 + u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR);
  1046 +
  1047 + if (!(irq_status & 0x300)) {
  1048 + if (printk_timed_ratelimit(&j, 500))
  1049 + dev_warn(vou_dev->v4l2_dev.dev, "IRQ status 0x%x!\n",
  1050 + irq_status);
  1051 + return IRQ_NONE;
  1052 + }
  1053 +
  1054 + spin_lock(&vou_dev->lock);
  1055 + if (!vou_dev->active || list_empty(&vou_dev->queue)) {
  1056 + if (printk_timed_ratelimit(&j, 500))
  1057 + dev_warn(vou_dev->v4l2_dev.dev,
  1058 + "IRQ without active buffer: %x!\n", irq_status);
  1059 + /* Just ack: buf_release will disable further interrupts */
  1060 + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x300);
  1061 + spin_unlock(&vou_dev->lock);
  1062 + return IRQ_HANDLED;
  1063 + }
  1064 +
  1065 + masked = ~(0x300 & irq_status) & irq_status & 0x30304;
  1066 + dev_dbg(vou_dev->v4l2_dev.dev,
  1067 + "IRQ status 0x%x -> 0x%x, VOU status 0x%x, cnt %d\n",
  1068 + irq_status, masked, vou_status, cnt);
  1069 +
  1070 + cnt++;
  1071 + side = vou_status & 0x10000;
  1072 +
  1073 + /* Clear only set interrupts */
  1074 + sh_vou_reg_a_write(vou_dev, VOUIR, masked);
  1075 +
  1076 + vb = vou_dev->active;
  1077 + list_del(&vb->queue);
  1078 +
  1079 + vb->state = VIDEOBUF_DONE;
  1080 + do_gettimeofday(&vb->ts);
  1081 + vb->field_count++;
  1082 + wake_up(&vb->done);
  1083 +
  1084 + if (list_empty(&vou_dev->queue)) {
  1085 + /* Stop VOU */
  1086 + dev_dbg(vou_dev->v4l2_dev.dev, "%s: queue empty after %d\n",
  1087 + __func__, cnt);
  1088 + sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
  1089 + vou_dev->active = NULL;
  1090 + vou_dev->status = SH_VOU_INITIALISING;
  1091 + /* Disable End-of-Frame (VSYNC) interrupts */
  1092 + sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
  1093 + spin_unlock(&vou_dev->lock);
  1094 + return IRQ_HANDLED;
  1095 + }
  1096 +
  1097 + vou_dev->active = list_entry(vou_dev->queue.next,
  1098 + struct videobuf_buffer, queue);
  1099 +
  1100 + if (vou_dev->active->queue.next != &vou_dev->queue) {
  1101 + struct videobuf_buffer *new = list_entry(vou_dev->active->queue.next,
  1102 + struct videobuf_buffer, queue);
  1103 + sh_vou_schedule_next(vou_dev, new);
  1104 + }
  1105 +
  1106 + spin_unlock(&vou_dev->lock);
  1107 +
  1108 + return IRQ_HANDLED;
  1109 +}
  1110 +
  1111 +static int sh_vou_hw_init(struct sh_vou_device *vou_dev)
  1112 +{
  1113 + struct sh_vou_pdata *pdata = vou_dev->pdata;
  1114 + u32 voucr = sh_vou_ntsc_mode(pdata->bus_fmt) << 29;
  1115 + int i = 100;
  1116 +
  1117 + /* Disable all IRQs */
  1118 + sh_vou_reg_a_write(vou_dev, VOUIR, 0);
  1119 +
  1120 + /* Reset VOU interfaces - registers unaffected */
  1121 + sh_vou_reg_a_write(vou_dev, VOUSRR, 0x101);
  1122 + while (--i && (sh_vou_reg_a_read(vou_dev, VOUSRR) & 0x101))
  1123 + udelay(1);
  1124 +
  1125 + if (!i)
  1126 + return -ETIMEDOUT;
  1127 +
  1128 + dev_dbg(vou_dev->v4l2_dev.dev, "Reset took %dus\n", 100 - i);
  1129 +
  1130 + if (pdata->flags & SH_VOU_PCLK_FALLING)
  1131 + voucr |= 1 << 28;
  1132 + if (pdata->flags & SH_VOU_HSYNC_LOW)
  1133 + voucr |= 1 << 27;
  1134 + if (pdata->flags & SH_VOU_VSYNC_LOW)
  1135 + voucr |= 1 << 26;
  1136 + sh_vou_reg_ab_set(vou_dev, VOUCR, voucr, 0xfc000000);
  1137 +
  1138 + /* Manual register side switching at first */
  1139 + sh_vou_reg_a_write(vou_dev, VOURCR, 4);
  1140 + /* Default - fixed HSYNC length, can be made configurable is required */
  1141 + sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000);
  1142 +
  1143 + return 0;
  1144 +}
  1145 +
  1146 +/* File operations */
  1147 +static int sh_vou_open(struct file *file)
  1148 +{
  1149 + struct video_device *vdev = video_devdata(file);
  1150 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  1151 + struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file),
  1152 + GFP_KERNEL);
  1153 +
  1154 + if (!vou_file)
  1155 + return -ENOMEM;
  1156 +
  1157 + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
  1158 +
  1159 + file->private_data = vou_file;
  1160 +
  1161 + if (atomic_inc_return(&vou_dev->use_count) == 1) {
  1162 + int ret;
  1163 + /* First open */
  1164 + vou_dev->status = SH_VOU_INITIALISING;
  1165 + pm_runtime_get_sync(vdev->v4l2_dev->dev);
  1166 + ret = sh_vou_hw_init(vou_dev);
  1167 + if (ret < 0) {
  1168 + atomic_dec(&vou_dev->use_count);
  1169 + pm_runtime_put(vdev->v4l2_dev->dev);
  1170 + vou_dev->status = SH_VOU_IDLE;
  1171 + return ret;
  1172 + }
  1173 + }
  1174 +
  1175 + videobuf_queue_dma_contig_init(&vou_file->vbq, &sh_vou_video_qops,
  1176 + vou_dev->v4l2_dev.dev, &vou_dev->lock,
  1177 + V4L2_BUF_TYPE_VIDEO_OUTPUT,
  1178 + V4L2_FIELD_NONE,
  1179 + sizeof(struct videobuf_buffer), vdev);
  1180 +
  1181 + return 0;
  1182 +}
  1183 +
  1184 +static int sh_vou_release(struct file *file)
  1185 +{
  1186 + struct video_device *vdev = video_devdata(file);
  1187 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  1188 + struct sh_vou_file *vou_file = file->private_data;
  1189 +
  1190 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  1191 +
  1192 + if (!atomic_dec_return(&vou_dev->use_count)) {
  1193 + /* Last close */
  1194 + vou_dev->status = SH_VOU_IDLE;
  1195 + sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101);
  1196 + pm_runtime_put(vdev->v4l2_dev->dev);
  1197 + }
  1198 +
  1199 + file->private_data = NULL;
  1200 + kfree(vou_file);
  1201 +
  1202 + return 0;
  1203 +}
  1204 +
  1205 +static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma)
  1206 +{
  1207 + struct sh_vou_file *vou_file = file->private_data;
  1208 +
  1209 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  1210 +
  1211 + return videobuf_mmap_mapper(&vou_file->vbq, vma);
  1212 +}
  1213 +
  1214 +static unsigned int sh_vou_poll(struct file *file, poll_table *wait)
  1215 +{
  1216 + struct sh_vou_file *vou_file = file->private_data;
  1217 +
  1218 + dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
  1219 +
  1220 + return videobuf_poll_stream(file, &vou_file->vbq, wait);
  1221 +}
  1222 +
  1223 +static int sh_vou_g_chip_ident(struct file *file, void *fh,
  1224 + struct v4l2_dbg_chip_ident *id)
  1225 +{
  1226 + struct video_device *vdev = video_devdata(file);
  1227 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  1228 +
  1229 + return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id);
  1230 +}
  1231 +
  1232 +#ifdef CONFIG_VIDEO_ADV_DEBUG
  1233 +static int sh_vou_g_register(struct file *file, void *fh,
  1234 + struct v4l2_dbg_register *reg)
  1235 +{
  1236 + struct video_device *vdev = video_devdata(file);
  1237 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  1238 +
  1239 + return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg);
  1240 +}
  1241 +
  1242 +static int sh_vou_s_register(struct file *file, void *fh,
  1243 + struct v4l2_dbg_register *reg)
  1244 +{
  1245 + struct video_device *vdev = video_devdata(file);
  1246 + struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
  1247 +
  1248 + return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg);
  1249 +}
  1250 +#endif
  1251 +
  1252 +/* sh_vou display ioctl operations */
  1253 +static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {
  1254 + .vidioc_querycap = sh_vou_querycap,
  1255 + .vidioc_enum_fmt_vid_out = sh_vou_enum_fmt_vid_out,
  1256 + .vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out,
  1257 + .vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out,
  1258 + .vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out,
  1259 + .vidioc_reqbufs = sh_vou_reqbufs,
  1260 + .vidioc_querybuf = sh_vou_querybuf,
  1261 + .vidioc_qbuf = sh_vou_qbuf,
  1262 + .vidioc_dqbuf = sh_vou_dqbuf,
  1263 + .vidioc_streamon = sh_vou_streamon,
  1264 + .vidioc_streamoff = sh_vou_streamoff,
  1265 + .vidioc_s_std = sh_vou_s_std,
  1266 + .vidioc_g_std = sh_vou_g_std,
  1267 + .vidioc_cropcap = sh_vou_cropcap,
  1268 + .vidioc_g_crop = sh_vou_g_crop,
  1269 + .vidioc_s_crop = sh_vou_s_crop,
  1270 + .vidioc_g_chip_ident = sh_vou_g_chip_ident,
  1271 +#ifdef CONFIG_VIDEO_ADV_DEBUG
  1272 + .vidioc_g_register = sh_vou_g_register,
  1273 + .vidioc_s_register = sh_vou_s_register,
  1274 +#endif
  1275 +};
  1276 +
  1277 +static const struct v4l2_file_operations sh_vou_fops = {
  1278 + .owner = THIS_MODULE,
  1279 + .open = sh_vou_open,
  1280 + .release = sh_vou_release,
  1281 + .ioctl = video_ioctl2,
  1282 + .mmap = sh_vou_mmap,
  1283 + .poll = sh_vou_poll,
  1284 +};
  1285 +
  1286 +static const struct video_device sh_vou_video_template = {
  1287 + .name = "sh_vou",
  1288 + .fops = &sh_vou_fops,
  1289 + .ioctl_ops = &sh_vou_ioctl_ops,
  1290 + .tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */
  1291 + .current_norm = V4L2_STD_NTSC_M,
  1292 +};
  1293 +
  1294 +static int __devinit sh_vou_probe(struct platform_device *pdev)
  1295 +{
  1296 + struct sh_vou_pdata *vou_pdata = pdev->dev.platform_data;
  1297 + struct v4l2_rect *rect;
  1298 + struct v4l2_pix_format *pix;
  1299 + struct i2c_adapter *i2c_adap;
  1300 + struct video_device *vdev;
  1301 + struct sh_vou_device *vou_dev;
  1302 + struct resource *reg_res, *region;
  1303 + struct v4l2_subdev *subdev;
  1304 + int irq, ret;
  1305 +
  1306 + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  1307 + irq = platform_get_irq(pdev, 0);
  1308 +
  1309 + if (!vou_pdata || !reg_res || irq <= 0) {
  1310 + dev_err(&pdev->dev, "Insufficient VOU platform information.\n");
  1311 + return -ENODEV;
  1312 + }
  1313 +
  1314 + vou_dev = kzalloc(sizeof(*vou_dev), GFP_KERNEL);
  1315 + if (!vou_dev)
  1316 + return -ENOMEM;
  1317 +
  1318 + INIT_LIST_HEAD(&vou_dev->queue);
  1319 + spin_lock_init(&vou_dev->lock);
  1320 + atomic_set(&vou_dev->use_count, 0);
  1321 + vou_dev->pdata = vou_pdata;
  1322 + vou_dev->status = SH_VOU_IDLE;
  1323 +
  1324 + rect = &vou_dev->rect;
  1325 + pix = &vou_dev->pix;
  1326 +
  1327 + /* Fill in defaults */
  1328 + vou_dev->std = sh_vou_video_template.current_norm;
  1329 + rect->left = 0;
  1330 + rect->top = 0;
  1331 + rect->width = VOU_MAX_IMAGE_WIDTH;
  1332 + rect->height = VOU_MAX_IMAGE_HEIGHT;
  1333 + pix->width = VOU_MAX_IMAGE_WIDTH;
  1334 + pix->height = VOU_MAX_IMAGE_HEIGHT;
  1335 + pix->pixelformat = V4L2_PIX_FMT_YVYU;
  1336 + pix->field = V4L2_FIELD_NONE;
  1337 + pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2;
  1338 + pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * VOU_MAX_IMAGE_HEIGHT;
  1339 + pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
  1340 +
  1341 + region = request_mem_region(reg_res->start, resource_size(reg_res),
  1342 + pdev->name);
  1343 + if (!region) {
  1344 + dev_err(&pdev->dev, "VOU region already claimed\n");
  1345 + ret = -EBUSY;
  1346 + goto ereqmemreg;
  1347 + }
  1348 +
  1349 + vou_dev->base = ioremap(reg_res->start, resource_size(reg_res));
  1350 + if (!vou_dev->base) {
  1351 + ret = -ENOMEM;
  1352 + goto emap;
  1353 + }
  1354 +
  1355 + ret = request_irq(irq, sh_vou_isr, 0, "vou", vou_dev);
  1356 + if (ret < 0)
  1357 + goto ereqirq;
  1358 +
  1359 + ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev);
  1360 + if (ret < 0) {
  1361 + dev_err(&pdev->dev, "Error registering v4l2 device\n");
  1362 + goto ev4l2devreg;
  1363 + }
  1364 +
  1365 + /* Allocate memory for video device */
  1366 + vdev = video_device_alloc();
  1367 + if (vdev == NULL) {
  1368 + ret = -ENOMEM;
  1369 + goto evdevalloc;
  1370 + }
  1371 +
  1372 + *vdev = sh_vou_video_template;
  1373 + if (vou_pdata->bus_fmt == SH_VOU_BUS_8BIT)
  1374 + vdev->tvnorms |= V4L2_STD_PAL;
  1375 + vdev->v4l2_dev = &vou_dev->v4l2_dev;
  1376 + vdev->release = video_device_release;
  1377 +
  1378 + vou_dev->vdev = vdev;
  1379 + video_set_drvdata(vdev, vou_dev);
  1380 +
  1381 + pm_runtime_enable(&pdev->dev);
  1382 + pm_runtime_resume(&pdev->dev);
  1383 +
  1384 + i2c_adap = i2c_get_adapter(vou_pdata->i2c_adap);
  1385 + if (!i2c_adap) {
  1386 + ret = -ENODEV;
  1387 + goto ei2cgadap;
  1388 + }
  1389 +
  1390 + ret = sh_vou_hw_init(vou_dev);
  1391 + if (ret < 0)
  1392 + goto ereset;
  1393 +
  1394 + subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap,
  1395 + vou_pdata->module_name, vou_pdata->board_info, NULL);
  1396 + if (!subdev) {
  1397 + ret = -ENOMEM;
  1398 + goto ei2cnd;
  1399 + }
  1400 +
  1401 + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
  1402 + if (ret < 0)
  1403 + goto evregdev;
  1404 +
  1405 + return 0;
  1406 +
  1407 +evregdev:
  1408 +ei2cnd:
  1409 +ereset:
  1410 + i2c_put_adapter(i2c_adap);
  1411 +ei2cgadap:
  1412 + video_device_release(vdev);
  1413 + pm_runtime_disable(&pdev->dev);
  1414 +evdevalloc:
  1415 + v4l2_device_unregister(&vou_dev->v4l2_dev);
  1416 +ev4l2devreg:
  1417 + free_irq(irq, vou_dev);
  1418 +ereqirq:
  1419 + iounmap(vou_dev->base);
  1420 +emap:
  1421 + release_mem_region(reg_res->start, resource_size(reg_res));
  1422 +ereqmemreg:
  1423 + kfree(vou_dev);
  1424 + return ret;
  1425 +}
  1426 +
  1427 +static int __devexit sh_vou_remove(struct platform_device *pdev)
  1428 +{
  1429 + int irq = platform_get_irq(pdev, 0);
  1430 + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
  1431 + struct sh_vou_device *vou_dev = container_of(v4l2_dev,
  1432 + struct sh_vou_device, v4l2_dev);
  1433 + struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next,
  1434 + struct v4l2_subdev, list);
  1435 + struct i2c_client *client = v4l2_get_subdevdata(sd);
  1436 + struct resource *reg_res;
  1437 +
  1438 + if (irq > 0)
  1439 + free_irq(irq, vou_dev);
  1440 + pm_runtime_disable(&pdev->dev);
  1441 + video_unregister_device(vou_dev->vdev);
  1442 + i2c_put_adapter(client->adapter);
  1443 + v4l2_device_unregister(&vou_dev->v4l2_dev);
  1444 + iounmap(vou_dev->base);
  1445 + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  1446 + if (reg_res)
  1447 + release_mem_region(reg_res->start, resource_size(reg_res));
  1448 + kfree(vou_dev);
  1449 + return 0;
  1450 +}
  1451 +
  1452 +static struct platform_driver __refdata sh_vou = {
  1453 + .remove = __devexit_p(sh_vou_remove),
  1454 + .driver = {
  1455 + .name = "sh-vou",
  1456 + .owner = THIS_MODULE,
  1457 + },
  1458 +};
  1459 +
  1460 +static int __init sh_vou_init(void)
  1461 +{
  1462 + return platform_driver_probe(&sh_vou, sh_vou_probe);
  1463 +}
  1464 +
  1465 +static void __exit sh_vou_exit(void)
  1466 +{
  1467 + platform_driver_unregister(&sh_vou);
  1468 +}
  1469 +
  1470 +module_init(sh_vou_init);
  1471 +module_exit(sh_vou_exit);
  1472 +
  1473 +MODULE_DESCRIPTION("SuperH VOU driver");
  1474 +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
  1475 +MODULE_LICENSE("GPL v2");
  1476 +MODULE_ALIAS("platform:sh-vou");
include/media/sh_vou.h
  1 +/*
  2 + * SuperH Video Output Unit (VOU) driver header
  3 + *
  4 + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License version 2 as
  8 + * published by the Free Software Foundation.
  9 + */
  10 +#ifndef SH_VOU_H
  11 +#define SH_VOU_H
  12 +
  13 +#include <linux/i2c.h>
  14 +
  15 +/* Bus flags */
  16 +#define SH_VOU_PCLK_FALLING (1 << 0)
  17 +#define SH_VOU_HSYNC_LOW (1 << 1)
  18 +#define SH_VOU_VSYNC_LOW (1 << 2)
  19 +
  20 +enum sh_vou_bus_fmt {
  21 + SH_VOU_BUS_8BIT,
  22 + SH_VOU_BUS_16BIT,
  23 + SH_VOU_BUS_BT656,
  24 +};
  25 +
  26 +struct sh_vou_pdata {
  27 + enum sh_vou_bus_fmt bus_fmt;
  28 + int i2c_adap;
  29 + struct i2c_board_info *board_info;
  30 + unsigned long flags;
  31 + char *module_name;
  32 +};
  33 +
  34 +#endif