1375 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * SuperH Video Output Unit (VOU) driver4 *5 * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>6 */7 8#include <linux/dma-mapping.h>9#include <linux/delay.h>10#include <linux/errno.h>11#include <linux/fs.h>12#include <linux/i2c.h>13#include <linux/init.h>14#include <linux/interrupt.h>15#include <linux/kernel.h>16#include <linux/platform_device.h>17#include <linux/pm_runtime.h>18#include <linux/slab.h>19#include <linux/videodev2.h>20#include <linux/module.h>21 22#include <media/drv-intf/sh_vou.h>23#include <media/v4l2-common.h>24#include <media/v4l2-device.h>25#include <media/v4l2-ioctl.h>26#include <media/v4l2-mediabus.h>27#include <media/videobuf2-v4l2.h>28#include <media/videobuf2-dma-contig.h>29 30/* Mirror addresses are not available for all registers */31#define VOUER 032#define VOUCR 433#define VOUSTR 834#define VOUVCR 0xc35#define VOUISR 0x1036#define VOUBCR 0x1437#define VOUDPR 0x1838#define VOUDSR 0x1c39#define VOUVPR 0x2040#define VOUIR 0x2441#define VOUSRR 0x2842#define VOUMSR 0x2c43#define VOUHIR 0x3044#define VOUDFR 0x3445#define VOUAD1R 0x3846#define VOUAD2R 0x3c47#define VOUAIR 0x4048#define VOUSWR 0x4449#define VOURCR 0x4850#define VOURPR 0x5051 52enum sh_vou_status {53 SH_VOU_IDLE,54 SH_VOU_INITIALISING,55 SH_VOU_RUNNING,56};57 58#define VOU_MIN_IMAGE_WIDTH 1659#define VOU_MAX_IMAGE_WIDTH 72060#define VOU_MIN_IMAGE_HEIGHT 1661 62struct sh_vou_buffer {63 struct vb2_v4l2_buffer vb;64 struct list_head list;65};66 67static inline struct68sh_vou_buffer *to_sh_vou_buffer(struct vb2_v4l2_buffer *vb2)69{70 return container_of(vb2, struct sh_vou_buffer, vb);71}72 73struct sh_vou_device {74 struct v4l2_device v4l2_dev;75 struct video_device vdev;76 struct sh_vou_pdata *pdata;77 spinlock_t lock;78 void __iomem *base;79 /* State information */80 struct v4l2_pix_format pix;81 struct v4l2_rect rect;82 struct list_head buf_list;83 v4l2_std_id std;84 int pix_idx;85 struct vb2_queue queue;86 struct sh_vou_buffer *active;87 enum sh_vou_status status;88 unsigned sequence;89 struct mutex fop_lock;90};91 92/* Register access routines for sides A, B and mirror addresses */93static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg,94 u32 value)95{96 __raw_writel(value, vou_dev->base + reg);97}98 99static void sh_vou_reg_ab_write(struct sh_vou_device *vou_dev, unsigned int reg,100 u32 value)101{102 __raw_writel(value, vou_dev->base + reg);103 __raw_writel(value, vou_dev->base + reg + 0x1000);104}105 106static void sh_vou_reg_m_write(struct sh_vou_device *vou_dev, unsigned int reg,107 u32 value)108{109 __raw_writel(value, vou_dev->base + reg + 0x2000);110}111 112static u32 sh_vou_reg_a_read(struct sh_vou_device *vou_dev, unsigned int reg)113{114 return __raw_readl(vou_dev->base + reg);115}116 117static void sh_vou_reg_a_set(struct sh_vou_device *vou_dev, unsigned int reg,118 u32 value, u32 mask)119{120 u32 old = __raw_readl(vou_dev->base + reg);121 122 value = (value & mask) | (old & ~mask);123 __raw_writel(value, vou_dev->base + reg);124}125 126static void sh_vou_reg_b_set(struct sh_vou_device *vou_dev, unsigned int reg,127 u32 value, u32 mask)128{129 sh_vou_reg_a_set(vou_dev, reg + 0x1000, value, mask);130}131 132static void sh_vou_reg_ab_set(struct sh_vou_device *vou_dev, unsigned int reg,133 u32 value, u32 mask)134{135 sh_vou_reg_a_set(vou_dev, reg, value, mask);136 sh_vou_reg_b_set(vou_dev, reg, value, mask);137}138 139struct sh_vou_fmt {140 u32 pfmt;141 unsigned char bpp;142 unsigned char bpl;143 unsigned char rgb;144 unsigned char yf;145 unsigned char pkf;146};147 148/* Further pixel formats can be added */149static struct sh_vou_fmt vou_fmt[] = {150 {151 .pfmt = V4L2_PIX_FMT_NV12,152 .bpp = 12,153 .bpl = 1,154 .yf = 0,155 .rgb = 0,156 },157 {158 .pfmt = V4L2_PIX_FMT_NV16,159 .bpp = 16,160 .bpl = 1,161 .yf = 1,162 .rgb = 0,163 },164 {165 .pfmt = V4L2_PIX_FMT_RGB24,166 .bpp = 24,167 .bpl = 3,168 .pkf = 2,169 .rgb = 1,170 },171 {172 .pfmt = V4L2_PIX_FMT_RGB565,173 .bpp = 16,174 .bpl = 2,175 .pkf = 3,176 .rgb = 1,177 },178 {179 .pfmt = V4L2_PIX_FMT_RGB565X,180 .bpp = 16,181 .bpl = 2,182 .pkf = 3,183 .rgb = 1,184 },185};186 187static void sh_vou_schedule_next(struct sh_vou_device *vou_dev,188 struct vb2_v4l2_buffer *vbuf)189{190 dma_addr_t addr1, addr2;191 192 addr1 = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);193 switch (vou_dev->pix.pixelformat) {194 case V4L2_PIX_FMT_NV12:195 case V4L2_PIX_FMT_NV16:196 addr2 = addr1 + vou_dev->pix.width * vou_dev->pix.height;197 break;198 default:199 addr2 = 0;200 }201 202 sh_vou_reg_m_write(vou_dev, VOUAD1R, addr1);203 sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2);204}205 206static void sh_vou_stream_config(struct sh_vou_device *vou_dev)207{208 unsigned int row_coeff;209#ifdef __LITTLE_ENDIAN210 u32 dataswap = 7;211#else212 u32 dataswap = 0;213#endif214 215 switch (vou_dev->pix.pixelformat) {216 default:217 case V4L2_PIX_FMT_NV12:218 case V4L2_PIX_FMT_NV16:219 row_coeff = 1;220 break;221 case V4L2_PIX_FMT_RGB565:222 dataswap ^= 1;223 fallthrough;224 case V4L2_PIX_FMT_RGB565X:225 row_coeff = 2;226 break;227 case V4L2_PIX_FMT_RGB24:228 row_coeff = 3;229 break;230 }231 232 sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap);233 sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff);234}235 236/* Locking: caller holds fop_lock mutex */237static int sh_vou_queue_setup(struct vb2_queue *vq,238 unsigned int *nbuffers, unsigned int *nplanes,239 unsigned int sizes[], struct device *alloc_devs[])240{241 struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);242 struct v4l2_pix_format *pix = &vou_dev->pix;243 int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;244 245 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);246 247 if (*nplanes)248 return sizes[0] < pix->height * bytes_per_line ? -EINVAL : 0;249 *nplanes = 1;250 sizes[0] = pix->height * bytes_per_line;251 return 0;252}253 254static int sh_vou_buf_prepare(struct vb2_buffer *vb)255{256 struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue);257 struct v4l2_pix_format *pix = &vou_dev->pix;258 unsigned bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;259 unsigned size = pix->height * bytes_per_line;260 261 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);262 263 if (vb2_plane_size(vb, 0) < size) {264 /* User buffer too small */265 dev_warn(vou_dev->v4l2_dev.dev, "buffer too small (%lu < %u)\n",266 vb2_plane_size(vb, 0), size);267 return -EINVAL;268 }269 270 vb2_set_plane_payload(vb, 0, size);271 return 0;272}273 274/* Locking: caller holds fop_lock mutex and vq->irqlock spinlock */275static void sh_vou_buf_queue(struct vb2_buffer *vb)276{277 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);278 struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue);279 struct sh_vou_buffer *shbuf = to_sh_vou_buffer(vbuf);280 unsigned long flags;281 282 spin_lock_irqsave(&vou_dev->lock, flags);283 list_add_tail(&shbuf->list, &vou_dev->buf_list);284 spin_unlock_irqrestore(&vou_dev->lock, flags);285}286 287static int sh_vou_start_streaming(struct vb2_queue *vq, unsigned int count)288{289 struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);290 struct sh_vou_buffer *buf, *node;291 int ret;292 293 vou_dev->sequence = 0;294 ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,295 video, s_stream, 1);296 if (ret < 0 && ret != -ENOIOCTLCMD) {297 list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) {298 vb2_buffer_done(&buf->vb.vb2_buf,299 VB2_BUF_STATE_QUEUED);300 list_del(&buf->list);301 }302 vou_dev->active = NULL;303 return ret;304 }305 306 buf = list_entry(vou_dev->buf_list.next, struct sh_vou_buffer, list);307 308 vou_dev->active = buf;309 310 /* Start from side A: we use mirror addresses, so, set B */311 sh_vou_reg_a_write(vou_dev, VOURPR, 1);312 dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n",313 __func__, sh_vou_reg_a_read(vou_dev, VOUSTR));314 sh_vou_schedule_next(vou_dev, &buf->vb);315 316 buf = list_entry(buf->list.next, struct sh_vou_buffer, list);317 318 /* Second buffer - initialise register side B */319 sh_vou_reg_a_write(vou_dev, VOURPR, 0);320 sh_vou_schedule_next(vou_dev, &buf->vb);321 322 /* Register side switching with frame VSYNC */323 sh_vou_reg_a_write(vou_dev, VOURCR, 5);324 325 sh_vou_stream_config(vou_dev);326 /* Enable End-of-Frame (VSYNC) interrupts */327 sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004);328 329 /* Two buffers on the queue - activate the hardware */330 vou_dev->status = SH_VOU_RUNNING;331 sh_vou_reg_a_write(vou_dev, VOUER, 0x107);332 return 0;333}334 335static void sh_vou_stop_streaming(struct vb2_queue *vq)336{337 struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);338 struct sh_vou_buffer *buf, *node;339 unsigned long flags;340 341 v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,342 video, s_stream, 0);343 /* disable output */344 sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);345 /* ...but the current frame will complete */346 sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);347 msleep(50);348 spin_lock_irqsave(&vou_dev->lock, flags);349 list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) {350 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);351 list_del(&buf->list);352 }353 vou_dev->active = NULL;354 spin_unlock_irqrestore(&vou_dev->lock, flags);355}356 357static const struct vb2_ops sh_vou_qops = {358 .queue_setup = sh_vou_queue_setup,359 .buf_prepare = sh_vou_buf_prepare,360 .buf_queue = sh_vou_buf_queue,361 .start_streaming = sh_vou_start_streaming,362 .stop_streaming = sh_vou_stop_streaming,363 .wait_prepare = vb2_ops_wait_prepare,364 .wait_finish = vb2_ops_wait_finish,365};366 367/* Video IOCTLs */368static int sh_vou_querycap(struct file *file, void *priv,369 struct v4l2_capability *cap)370{371 struct sh_vou_device *vou_dev = video_drvdata(file);372 373 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);374 375 strscpy(cap->card, "SuperH VOU", sizeof(cap->card));376 strscpy(cap->driver, "sh-vou", sizeof(cap->driver));377 strscpy(cap->bus_info, "platform:sh-vou", sizeof(cap->bus_info));378 return 0;379}380 381/* Enumerate formats, that the device can accept from the user */382static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv,383 struct v4l2_fmtdesc *fmt)384{385 struct sh_vou_device *vou_dev = video_drvdata(file);386 387 if (fmt->index >= ARRAY_SIZE(vou_fmt))388 return -EINVAL;389 390 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);391 392 fmt->pixelformat = vou_fmt[fmt->index].pfmt;393 394 return 0;395}396 397static int sh_vou_g_fmt_vid_out(struct file *file, void *priv,398 struct v4l2_format *fmt)399{400 struct sh_vou_device *vou_dev = video_drvdata(file);401 402 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);403 404 fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;405 fmt->fmt.pix = vou_dev->pix;406 407 return 0;408}409 410static const unsigned char vou_scale_h_num[] = {1, 9, 2, 9, 4};411static const unsigned char vou_scale_h_den[] = {1, 8, 1, 4, 1};412static const unsigned char vou_scale_h_fld[] = {0, 2, 1, 3};413static const unsigned char vou_scale_v_num[] = {1, 2, 4};414static const unsigned char vou_scale_v_den[] = {1, 1, 1};415static const unsigned char vou_scale_v_fld[] = {0, 1};416 417static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev,418 int pix_idx, int w_idx, int h_idx)419{420 struct sh_vou_fmt *fmt = vou_fmt + pix_idx;421 unsigned int black_left, black_top, width_max,422 frame_in_height, frame_out_height, frame_out_top;423 struct v4l2_rect *rect = &vou_dev->rect;424 struct v4l2_pix_format *pix = &vou_dev->pix;425 u32 vouvcr = 0, dsr_h, dsr_v;426 427 if (vou_dev->std & V4L2_STD_525_60) {428 width_max = 858;429 /* height_max = 262; */430 } else {431 width_max = 864;432 /* height_max = 312; */433 }434 435 frame_in_height = pix->height / 2;436 frame_out_height = rect->height / 2;437 frame_out_top = rect->top / 2;438 439 /*440 * Cropping scheme: max useful image is 720x480, and the total video441 * area is 858x525 (NTSC) or 864x625 (PAL). AK8813 / 8814 starts442 * sampling data beginning with fixed 276th (NTSC) / 288th (PAL) clock,443 * of which the first 33 / 25 clocks HSYNC must be held active. This444 * has to be configured in CR[HW]. 1 pixel equals 2 clock periods.445 * This gives CR[HW] = 16 / 12, VPR[HVP] = 138 / 144, which gives446 * exactly 858 - 138 = 864 - 144 = 720! We call the out-of-display area,447 * beyond DSR, specified on the left and top by the VPR register "black448 * pixels" and out-of-image area (DPR) "background pixels." We fix VPR449 * at 138 / 144 : 20, because that's the HSYNC timing, that our first450 * client requires, and that's exactly what leaves us 720 pixels for the451 * image; we leave VPR[VVP] at default 20 for now, because the client452 * doesn't seem to have any special requirements for it. Otherwise we453 * could also set it to max - 240 = 22 / 72. Thus VPR depends only on454 * the selected standard, and DPR and DSR are selected according to455 * cropping. Q: how does the client detect the first valid line? Does456 * HSYNC stay inactive during invalid (black) lines?457 */458 black_left = width_max - VOU_MAX_IMAGE_WIDTH;459 black_top = 20;460 461 dsr_h = rect->width + rect->left;462 dsr_v = frame_out_height + frame_out_top;463 464 dev_dbg(vou_dev->v4l2_dev.dev,465 "image %ux%u, black %u:%u, offset %u:%u, display %ux%u\n",466 pix->width, frame_in_height, black_left, black_top,467 rect->left, frame_out_top, dsr_h, dsr_v);468 469 /* VOUISR height - half of a frame height in frame mode */470 sh_vou_reg_ab_write(vou_dev, VOUISR, (pix->width << 16) | frame_in_height);471 sh_vou_reg_ab_write(vou_dev, VOUVPR, (black_left << 16) | black_top);472 sh_vou_reg_ab_write(vou_dev, VOUDPR, (rect->left << 16) | frame_out_top);473 sh_vou_reg_ab_write(vou_dev, VOUDSR, (dsr_h << 16) | dsr_v);474 475 /*476 * if necessary, we could set VOUHIR to477 * max(black_left + dsr_h, width_max) here478 */479 480 if (w_idx)481 vouvcr |= (1 << 15) | (vou_scale_h_fld[w_idx - 1] << 4);482 if (h_idx)483 vouvcr |= (1 << 14) | vou_scale_v_fld[h_idx - 1];484 485 dev_dbg(vou_dev->v4l2_dev.dev, "0x%08x: scaling 0x%x\n",486 fmt->pfmt, vouvcr);487 488 /* To produce a colour bar for testing set bit 23 of VOUVCR */489 sh_vou_reg_ab_write(vou_dev, VOUVCR, vouvcr);490 sh_vou_reg_ab_write(vou_dev, VOUDFR,491 fmt->pkf | (fmt->yf << 8) | (fmt->rgb << 16));492}493 494struct sh_vou_geometry {495 struct v4l2_rect output;496 unsigned int in_width;497 unsigned int in_height;498 int scale_idx_h;499 int scale_idx_v;500};501 502/*503 * Find input geometry, that we can use to produce output, closest to the504 * requested rectangle, using VOU scaling505 */506static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std)507{508 /* The compiler cannot know, that best and idx will indeed be set */509 unsigned int best_err = UINT_MAX, best = 0, img_height_max;510 int i, idx = 0;511 512 if (std & V4L2_STD_525_60)513 img_height_max = 480;514 else515 img_height_max = 576;516 517 /* Image width must be a multiple of 4 */518 v4l_bound_align_image(&geo->in_width,519 VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 2,520 &geo->in_height,521 VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0);522 523 /* Select scales to come as close as possible to the output image */524 for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) {525 unsigned int err;526 unsigned int found = geo->output.width * vou_scale_h_den[i] /527 vou_scale_h_num[i];528 529 if (found > VOU_MAX_IMAGE_WIDTH)530 /* scales increase */531 break;532 533 err = abs(found - geo->in_width);534 if (err < best_err) {535 best_err = err;536 idx = i;537 best = found;538 }539 if (!err)540 break;541 }542 543 geo->in_width = best;544 geo->scale_idx_h = idx;545 546 best_err = UINT_MAX;547 548 /* This loop can be replaced with one division */549 for (i = ARRAY_SIZE(vou_scale_v_num) - 1; i >= 0; i--) {550 unsigned int err;551 unsigned int found = geo->output.height * vou_scale_v_den[i] /552 vou_scale_v_num[i];553 554 if (found > img_height_max)555 /* scales increase */556 break;557 558 err = abs(found - geo->in_height);559 if (err < best_err) {560 best_err = err;561 idx = i;562 best = found;563 }564 if (!err)565 break;566 }567 568 geo->in_height = best;569 geo->scale_idx_v = idx;570}571 572/*573 * Find output geometry, that we can produce, using VOU scaling, closest to574 * the requested rectangle575 */576static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)577{578 unsigned int best_err = UINT_MAX, best = geo->in_width,579 width_max, height_max, img_height_max;580 int i, idx_h = 0, idx_v = 0;581 582 if (std & V4L2_STD_525_60) {583 width_max = 858;584 height_max = 262 * 2;585 img_height_max = 480;586 } else {587 width_max = 864;588 height_max = 312 * 2;589 img_height_max = 576;590 }591 592 /* Select scales to come as close as possible to the output image */593 for (i = 0; i < ARRAY_SIZE(vou_scale_h_num); i++) {594 unsigned int err;595 unsigned int found = geo->in_width * vou_scale_h_num[i] /596 vou_scale_h_den[i];597 598 if (found > VOU_MAX_IMAGE_WIDTH)599 /* scales increase */600 break;601 602 err = abs(found - geo->output.width);603 if (err < best_err) {604 best_err = err;605 idx_h = i;606 best = found;607 }608 if (!err)609 break;610 }611 612 geo->output.width = best;613 geo->scale_idx_h = idx_h;614 if (geo->output.left + best > width_max)615 geo->output.left = width_max - best;616 617 pr_debug("%s(): W %u * %u/%u = %u\n", __func__, geo->in_width,618 vou_scale_h_num[idx_h], vou_scale_h_den[idx_h], best);619 620 best_err = UINT_MAX;621 622 /* This loop can be replaced with one division */623 for (i = 0; i < ARRAY_SIZE(vou_scale_v_num); i++) {624 unsigned int err;625 unsigned int found = geo->in_height * vou_scale_v_num[i] /626 vou_scale_v_den[i];627 628 if (found > img_height_max)629 /* scales increase */630 break;631 632 err = abs(found - geo->output.height);633 if (err < best_err) {634 best_err = err;635 idx_v = i;636 best = found;637 }638 if (!err)639 break;640 }641 642 geo->output.height = best;643 geo->scale_idx_v = idx_v;644 if (geo->output.top + best > height_max)645 geo->output.top = height_max - best;646 647 pr_debug("%s(): H %u * %u/%u = %u\n", __func__, geo->in_height,648 vou_scale_v_num[idx_v], vou_scale_v_den[idx_v], best);649}650 651static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,652 struct v4l2_format *fmt)653{654 struct sh_vou_device *vou_dev = video_drvdata(file);655 struct v4l2_pix_format *pix = &fmt->fmt.pix;656 unsigned int img_height_max;657 int pix_idx;658 659 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);660 661 pix->field = V4L2_FIELD_INTERLACED;662 pix->colorspace = V4L2_COLORSPACE_SMPTE170M;663 pix->ycbcr_enc = pix->quantization = 0;664 665 for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++)666 if (vou_fmt[pix_idx].pfmt == pix->pixelformat)667 break;668 669 if (pix_idx == ARRAY_SIZE(vou_fmt))670 return -EINVAL;671 672 if (vou_dev->std & V4L2_STD_525_60)673 img_height_max = 480;674 else675 img_height_max = 576;676 677 v4l_bound_align_image(&pix->width,678 VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 2,679 &pix->height,680 VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0);681 pix->bytesperline = pix->width * vou_fmt[pix_idx].bpl;682 pix->sizeimage = pix->height * ((pix->width * vou_fmt[pix_idx].bpp) >> 3);683 684 return 0;685}686 687static int sh_vou_set_fmt_vid_out(struct sh_vou_device *vou_dev,688 struct v4l2_pix_format *pix)689{690 unsigned int img_height_max;691 struct sh_vou_geometry geo;692 struct v4l2_subdev_format format = {693 .which = V4L2_SUBDEV_FORMAT_ACTIVE,694 /* Revisit: is this the correct code? */695 .format.code = MEDIA_BUS_FMT_YUYV8_2X8,696 .format.field = V4L2_FIELD_INTERLACED,697 .format.colorspace = V4L2_COLORSPACE_SMPTE170M,698 };699 struct v4l2_mbus_framefmt *mbfmt = &format.format;700 int pix_idx;701 int ret;702 703 if (vb2_is_busy(&vou_dev->queue))704 return -EBUSY;705 706 for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++)707 if (vou_fmt[pix_idx].pfmt == pix->pixelformat)708 break;709 710 geo.in_width = pix->width;711 geo.in_height = pix->height;712 geo.output = vou_dev->rect;713 714 vou_adjust_output(&geo, vou_dev->std);715 716 mbfmt->width = geo.output.width;717 mbfmt->height = geo.output.height;718 ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad,719 set_fmt, NULL, &format);720 /* Must be implemented, so, don't check for -ENOIOCTLCMD */721 if (ret < 0)722 return ret;723 724 dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,725 geo.output.width, geo.output.height, mbfmt->width, mbfmt->height);726 727 if (vou_dev->std & V4L2_STD_525_60)728 img_height_max = 480;729 else730 img_height_max = 576;731 732 /* Sanity checks */733 if ((unsigned)mbfmt->width > VOU_MAX_IMAGE_WIDTH ||734 (unsigned)mbfmt->height > img_height_max ||735 mbfmt->code != MEDIA_BUS_FMT_YUYV8_2X8)736 return -EIO;737 738 if (mbfmt->width != geo.output.width ||739 mbfmt->height != geo.output.height) {740 geo.output.width = mbfmt->width;741 geo.output.height = mbfmt->height;742 743 vou_adjust_input(&geo, vou_dev->std);744 }745 746 /* We tried to preserve output rectangle, but it could have changed */747 vou_dev->rect = geo.output;748 pix->width = geo.in_width;749 pix->height = geo.in_height;750 751 dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u\n", __func__,752 pix->width, pix->height);753 754 vou_dev->pix_idx = pix_idx;755 756 vou_dev->pix = *pix;757 758 sh_vou_configure_geometry(vou_dev, pix_idx,759 geo.scale_idx_h, geo.scale_idx_v);760 761 return 0;762}763 764static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,765 struct v4l2_format *fmt)766{767 struct sh_vou_device *vou_dev = video_drvdata(file);768 int ret = sh_vou_try_fmt_vid_out(file, priv, fmt);769 770 if (ret)771 return ret;772 return sh_vou_set_fmt_vid_out(vou_dev, &fmt->fmt.pix);773}774 775static int sh_vou_enum_output(struct file *file, void *fh,776 struct v4l2_output *a)777{778 struct sh_vou_device *vou_dev = video_drvdata(file);779 780 if (a->index)781 return -EINVAL;782 strscpy(a->name, "Video Out", sizeof(a->name));783 a->type = V4L2_OUTPUT_TYPE_ANALOG;784 a->std = vou_dev->vdev.tvnorms;785 return 0;786}787 788static int sh_vou_g_output(struct file *file, void *fh, unsigned int *i)789{790 *i = 0;791 return 0;792}793 794static int sh_vou_s_output(struct file *file, void *fh, unsigned int i)795{796 return i ? -EINVAL : 0;797}798 799static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt)800{801 switch (bus_fmt) {802 default:803 pr_warn("%s(): Invalid bus-format code %d, using default 8-bit\n",804 __func__, bus_fmt);805 fallthrough;806 case SH_VOU_BUS_8BIT:807 return 1;808 case SH_VOU_BUS_16BIT:809 return 0;810 case SH_VOU_BUS_BT656:811 return 3;812 }813}814 815static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id)816{817 struct sh_vou_device *vou_dev = video_drvdata(file);818 int ret;819 820 dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id);821 822 if (std_id == vou_dev->std)823 return 0;824 825 if (vb2_is_busy(&vou_dev->queue))826 return -EBUSY;827 828 ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,829 s_std_output, std_id);830 /* Shall we continue, if the subdev doesn't support .s_std_output()? */831 if (ret < 0 && ret != -ENOIOCTLCMD)832 return ret;833 834 vou_dev->rect.top = vou_dev->rect.left = 0;835 vou_dev->rect.width = VOU_MAX_IMAGE_WIDTH;836 if (std_id & V4L2_STD_525_60) {837 sh_vou_reg_ab_set(vou_dev, VOUCR,838 sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29);839 vou_dev->rect.height = 480;840 } else {841 sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29);842 vou_dev->rect.height = 576;843 }844 845 vou_dev->pix.width = vou_dev->rect.width;846 vou_dev->pix.height = vou_dev->rect.height;847 vou_dev->pix.bytesperline =848 vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpl;849 vou_dev->pix.sizeimage = vou_dev->pix.height *850 ((vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpp) >> 3);851 vou_dev->std = std_id;852 sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix);853 854 return 0;855}856 857static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std)858{859 struct sh_vou_device *vou_dev = video_drvdata(file);860 861 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);862 863 *std = vou_dev->std;864 865 return 0;866}867 868static int sh_vou_log_status(struct file *file, void *priv)869{870 struct sh_vou_device *vou_dev = video_drvdata(file);871 872 pr_info("VOUER: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUER));873 pr_info("VOUCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUCR));874 pr_info("VOUSTR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSTR));875 pr_info("VOUVCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVCR));876 pr_info("VOUISR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUISR));877 pr_info("VOUBCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUBCR));878 pr_info("VOUDPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDPR));879 pr_info("VOUDSR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDSR));880 pr_info("VOUVPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVPR));881 pr_info("VOUIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUIR));882 pr_info("VOUSRR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSRR));883 pr_info("VOUMSR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUMSR));884 pr_info("VOUHIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUHIR));885 pr_info("VOUDFR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDFR));886 pr_info("VOUAD1R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD1R));887 pr_info("VOUAD2R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD2R));888 pr_info("VOUAIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAIR));889 pr_info("VOUSWR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSWR));890 pr_info("VOURCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURCR));891 pr_info("VOURPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURPR));892 return 0;893}894 895static int sh_vou_g_selection(struct file *file, void *fh,896 struct v4l2_selection *sel)897{898 struct sh_vou_device *vou_dev = video_drvdata(file);899 900 if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)901 return -EINVAL;902 switch (sel->target) {903 case V4L2_SEL_TGT_COMPOSE:904 sel->r = vou_dev->rect;905 break;906 case V4L2_SEL_TGT_COMPOSE_DEFAULT:907 case V4L2_SEL_TGT_COMPOSE_BOUNDS:908 sel->r.left = 0;909 sel->r.top = 0;910 sel->r.width = VOU_MAX_IMAGE_WIDTH;911 if (vou_dev->std & V4L2_STD_525_60)912 sel->r.height = 480;913 else914 sel->r.height = 576;915 break;916 default:917 return -EINVAL;918 }919 return 0;920}921 922/* Assume a dull encoder, do all the work ourselves. */923static int sh_vou_s_selection(struct file *file, void *fh,924 struct v4l2_selection *sel)925{926 struct v4l2_rect *rect = &sel->r;927 struct sh_vou_device *vou_dev = video_drvdata(file);928 struct v4l2_subdev_selection sd_sel = {929 .which = V4L2_SUBDEV_FORMAT_ACTIVE,930 .target = V4L2_SEL_TGT_COMPOSE,931 };932 struct v4l2_pix_format *pix = &vou_dev->pix;933 struct sh_vou_geometry geo;934 struct v4l2_subdev_format format = {935 .which = V4L2_SUBDEV_FORMAT_ACTIVE,936 /* Revisit: is this the correct code? */937 .format.code = MEDIA_BUS_FMT_YUYV8_2X8,938 .format.field = V4L2_FIELD_INTERLACED,939 .format.colorspace = V4L2_COLORSPACE_SMPTE170M,940 };941 unsigned int img_height_max;942 int ret;943 944 if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||945 sel->target != V4L2_SEL_TGT_COMPOSE)946 return -EINVAL;947 948 if (vb2_is_busy(&vou_dev->queue))949 return -EBUSY;950 951 if (vou_dev->std & V4L2_STD_525_60)952 img_height_max = 480;953 else954 img_height_max = 576;955 956 v4l_bound_align_image(&rect->width,957 VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 1,958 &rect->height,959 VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0);960 961 if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH)962 rect->left = VOU_MAX_IMAGE_WIDTH - rect->width;963 964 if (rect->height + rect->top > img_height_max)965 rect->top = img_height_max - rect->height;966 967 geo.output = *rect;968 geo.in_width = pix->width;969 geo.in_height = pix->height;970 971 /* Configure the encoder one-to-one, position at 0, ignore errors */972 sd_sel.r.width = geo.output.width;973 sd_sel.r.height = geo.output.height;974 /*975 * We first issue a S_SELECTION, so that the subsequent S_FMT delivers the976 * final encoder configuration.977 */978 v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad,979 set_selection, NULL, &sd_sel);980 format.format.width = geo.output.width;981 format.format.height = geo.output.height;982 ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad,983 set_fmt, NULL, &format);984 /* Must be implemented, so, don't check for -ENOIOCTLCMD */985 if (ret < 0)986 return ret;987 988 /* Sanity checks */989 if ((unsigned)format.format.width > VOU_MAX_IMAGE_WIDTH ||990 (unsigned)format.format.height > img_height_max ||991 format.format.code != MEDIA_BUS_FMT_YUYV8_2X8)992 return -EIO;993 994 geo.output.width = format.format.width;995 geo.output.height = format.format.height;996 997 /*998 * No down-scaling. According to the API, current call has precedence:999 * https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/crop.html#cropping-structures1000 */1001 vou_adjust_input(&geo, vou_dev->std);1002 1003 /* We tried to preserve output rectangle, but it could have changed */1004 vou_dev->rect = geo.output;1005 pix->width = geo.in_width;1006 pix->height = geo.in_height;1007 1008 sh_vou_configure_geometry(vou_dev, vou_dev->pix_idx,1009 geo.scale_idx_h, geo.scale_idx_v);1010 1011 return 0;1012}1013 1014static irqreturn_t sh_vou_isr(int irq, void *dev_id)1015{1016 struct sh_vou_device *vou_dev = dev_id;1017 static unsigned long j;1018 struct sh_vou_buffer *vb;1019 static int cnt;1020 u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked;1021 u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR);1022 1023 if (!(irq_status & 0x300)) {1024 if (printk_timed_ratelimit(&j, 500))1025 dev_warn(vou_dev->v4l2_dev.dev, "IRQ status 0x%x!\n",1026 irq_status);1027 return IRQ_NONE;1028 }1029 1030 spin_lock(&vou_dev->lock);1031 if (!vou_dev->active || list_empty(&vou_dev->buf_list)) {1032 if (printk_timed_ratelimit(&j, 500))1033 dev_warn(vou_dev->v4l2_dev.dev,1034 "IRQ without active buffer: %x!\n", irq_status);1035 /* Just ack: buf_release will disable further interrupts */1036 sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x300);1037 spin_unlock(&vou_dev->lock);1038 return IRQ_HANDLED;1039 }1040 1041 masked = ~(0x300 & irq_status) & irq_status & 0x30304;1042 dev_dbg(vou_dev->v4l2_dev.dev,1043 "IRQ status 0x%x -> 0x%x, VOU status 0x%x, cnt %d\n",1044 irq_status, masked, vou_status, cnt);1045 1046 cnt++;1047 /* side = vou_status & 0x10000; */1048 1049 /* Clear only set interrupts */1050 sh_vou_reg_a_write(vou_dev, VOUIR, masked);1051 1052 vb = vou_dev->active;1053 if (list_is_singular(&vb->list)) {1054 /* Keep cycling while no next buffer is available */1055 sh_vou_schedule_next(vou_dev, &vb->vb);1056 spin_unlock(&vou_dev->lock);1057 return IRQ_HANDLED;1058 }1059 1060 list_del(&vb->list);1061 1062 vb->vb.vb2_buf.timestamp = ktime_get_ns();1063 vb->vb.sequence = vou_dev->sequence++;1064 vb->vb.field = V4L2_FIELD_INTERLACED;1065 vb2_buffer_done(&vb->vb.vb2_buf, VB2_BUF_STATE_DONE);1066 1067 vou_dev->active = list_entry(vou_dev->buf_list.next,1068 struct sh_vou_buffer, list);1069 1070 if (list_is_singular(&vou_dev->buf_list)) {1071 /* Keep cycling while no next buffer is available */1072 sh_vou_schedule_next(vou_dev, &vou_dev->active->vb);1073 } else {1074 struct sh_vou_buffer *new = list_entry(vou_dev->active->list.next,1075 struct sh_vou_buffer, list);1076 sh_vou_schedule_next(vou_dev, &new->vb);1077 }1078 1079 spin_unlock(&vou_dev->lock);1080 1081 return IRQ_HANDLED;1082}1083 1084static int sh_vou_hw_init(struct sh_vou_device *vou_dev)1085{1086 struct sh_vou_pdata *pdata = vou_dev->pdata;1087 u32 voucr = sh_vou_ntsc_mode(pdata->bus_fmt) << 29;1088 int i = 100;1089 1090 /* Disable all IRQs */1091 sh_vou_reg_a_write(vou_dev, VOUIR, 0);1092 1093 /* Reset VOU interfaces - registers unaffected */1094 sh_vou_reg_a_write(vou_dev, VOUSRR, 0x101);1095 while (--i && (sh_vou_reg_a_read(vou_dev, VOUSRR) & 0x101))1096 udelay(1);1097 1098 if (!i)1099 return -ETIMEDOUT;1100 1101 dev_dbg(vou_dev->v4l2_dev.dev, "Reset took %dus\n", 100 - i);1102 1103 if (pdata->flags & SH_VOU_PCLK_FALLING)1104 voucr |= 1 << 28;1105 if (pdata->flags & SH_VOU_HSYNC_LOW)1106 voucr |= 1 << 27;1107 if (pdata->flags & SH_VOU_VSYNC_LOW)1108 voucr |= 1 << 26;1109 sh_vou_reg_ab_set(vou_dev, VOUCR, voucr, 0xfc000000);1110 1111 /* Manual register side switching at first */1112 sh_vou_reg_a_write(vou_dev, VOURCR, 4);1113 /* Default - fixed HSYNC length, can be made configurable is required */1114 sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000);1115 1116 sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix);1117 1118 return 0;1119}1120 1121/* File operations */1122static int sh_vou_open(struct file *file)1123{1124 struct sh_vou_device *vou_dev = video_drvdata(file);1125 int err;1126 1127 if (mutex_lock_interruptible(&vou_dev->fop_lock))1128 return -ERESTARTSYS;1129 1130 err = v4l2_fh_open(file);1131 if (err)1132 goto done_open;1133 if (v4l2_fh_is_singular_file(file) &&1134 vou_dev->status == SH_VOU_INITIALISING) {1135 /* First open */1136 err = pm_runtime_resume_and_get(vou_dev->v4l2_dev.dev);1137 if (err < 0) {1138 v4l2_fh_release(file);1139 goto done_open;1140 }1141 err = sh_vou_hw_init(vou_dev);1142 if (err < 0) {1143 pm_runtime_put(vou_dev->v4l2_dev.dev);1144 v4l2_fh_release(file);1145 } else {1146 vou_dev->status = SH_VOU_IDLE;1147 }1148 }1149done_open:1150 mutex_unlock(&vou_dev->fop_lock);1151 return err;1152}1153 1154static int sh_vou_release(struct file *file)1155{1156 struct sh_vou_device *vou_dev = video_drvdata(file);1157 bool is_last;1158 1159 mutex_lock(&vou_dev->fop_lock);1160 is_last = v4l2_fh_is_singular_file(file);1161 _vb2_fop_release(file, NULL);1162 if (is_last) {1163 /* Last close */1164 vou_dev->status = SH_VOU_INITIALISING;1165 sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101);1166 pm_runtime_put(vou_dev->v4l2_dev.dev);1167 }1168 mutex_unlock(&vou_dev->fop_lock);1169 return 0;1170}1171 1172/* sh_vou display ioctl operations */1173static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {1174 .vidioc_querycap = sh_vou_querycap,1175 .vidioc_enum_fmt_vid_out = sh_vou_enum_fmt_vid_out,1176 .vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out,1177 .vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out,1178 .vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out,1179 .vidioc_reqbufs = vb2_ioctl_reqbufs,1180 .vidioc_create_bufs = vb2_ioctl_create_bufs,1181 .vidioc_querybuf = vb2_ioctl_querybuf,1182 .vidioc_qbuf = vb2_ioctl_qbuf,1183 .vidioc_dqbuf = vb2_ioctl_dqbuf,1184 .vidioc_prepare_buf = vb2_ioctl_prepare_buf,1185 .vidioc_streamon = vb2_ioctl_streamon,1186 .vidioc_streamoff = vb2_ioctl_streamoff,1187 .vidioc_expbuf = vb2_ioctl_expbuf,1188 .vidioc_g_output = sh_vou_g_output,1189 .vidioc_s_output = sh_vou_s_output,1190 .vidioc_enum_output = sh_vou_enum_output,1191 .vidioc_s_std = sh_vou_s_std,1192 .vidioc_g_std = sh_vou_g_std,1193 .vidioc_g_selection = sh_vou_g_selection,1194 .vidioc_s_selection = sh_vou_s_selection,1195 .vidioc_log_status = sh_vou_log_status,1196};1197 1198static const struct v4l2_file_operations sh_vou_fops = {1199 .owner = THIS_MODULE,1200 .open = sh_vou_open,1201 .release = sh_vou_release,1202 .unlocked_ioctl = video_ioctl2,1203 .mmap = vb2_fop_mmap,1204 .poll = vb2_fop_poll,1205 .write = vb2_fop_write,1206};1207 1208static const struct video_device sh_vou_video_template = {1209 .name = "sh_vou",1210 .fops = &sh_vou_fops,1211 .ioctl_ops = &sh_vou_ioctl_ops,1212 .tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */1213 .vfl_dir = VFL_DIR_TX,1214 .device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE |1215 V4L2_CAP_STREAMING,1216};1217 1218static int sh_vou_probe(struct platform_device *pdev)1219{1220 struct sh_vou_pdata *vou_pdata = pdev->dev.platform_data;1221 struct v4l2_rect *rect;1222 struct v4l2_pix_format *pix;1223 struct i2c_adapter *i2c_adap;1224 struct video_device *vdev;1225 struct sh_vou_device *vou_dev;1226 struct v4l2_subdev *subdev;1227 struct vb2_queue *q;1228 int irq, ret;1229 1230 if (!vou_pdata) {1231 dev_err(&pdev->dev, "Insufficient VOU platform information.\n");1232 return -ENODEV;1233 }1234 1235 irq = platform_get_irq(pdev, 0);1236 if (irq < 0)1237 return irq;1238 1239 vou_dev = devm_kzalloc(&pdev->dev, sizeof(*vou_dev), GFP_KERNEL);1240 if (!vou_dev)1241 return -ENOMEM;1242 1243 INIT_LIST_HEAD(&vou_dev->buf_list);1244 spin_lock_init(&vou_dev->lock);1245 mutex_init(&vou_dev->fop_lock);1246 vou_dev->pdata = vou_pdata;1247 vou_dev->status = SH_VOU_INITIALISING;1248 vou_dev->pix_idx = 1;1249 1250 rect = &vou_dev->rect;1251 pix = &vou_dev->pix;1252 1253 /* Fill in defaults */1254 vou_dev->std = V4L2_STD_NTSC_M;1255 rect->left = 0;1256 rect->top = 0;1257 rect->width = VOU_MAX_IMAGE_WIDTH;1258 rect->height = 480;1259 pix->width = VOU_MAX_IMAGE_WIDTH;1260 pix->height = 480;1261 pix->pixelformat = V4L2_PIX_FMT_NV16;1262 pix->field = V4L2_FIELD_INTERLACED;1263 pix->bytesperline = VOU_MAX_IMAGE_WIDTH;1264 pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480;1265 pix->colorspace = V4L2_COLORSPACE_SMPTE170M;1266 1267 vou_dev->base = devm_platform_ioremap_resource(pdev, 0);1268 if (IS_ERR(vou_dev->base))1269 return PTR_ERR(vou_dev->base);1270 1271 ret = devm_request_irq(&pdev->dev, irq, sh_vou_isr, 0, "vou", vou_dev);1272 if (ret < 0)1273 return ret;1274 1275 ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev);1276 if (ret < 0) {1277 dev_err(&pdev->dev, "Error registering v4l2 device\n");1278 return ret;1279 }1280 1281 vdev = &vou_dev->vdev;1282 *vdev = sh_vou_video_template;1283 if (vou_pdata->bus_fmt == SH_VOU_BUS_8BIT)1284 vdev->tvnorms |= V4L2_STD_PAL;1285 vdev->v4l2_dev = &vou_dev->v4l2_dev;1286 vdev->release = video_device_release_empty;1287 vdev->lock = &vou_dev->fop_lock;1288 1289 video_set_drvdata(vdev, vou_dev);1290 1291 /* Initialize the vb2 queue */1292 q = &vou_dev->queue;1293 q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;1294 q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE;1295 q->drv_priv = vou_dev;1296 q->buf_struct_size = sizeof(struct sh_vou_buffer);1297 q->ops = &sh_vou_qops;1298 q->mem_ops = &vb2_dma_contig_memops;1299 q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;1300 q->min_queued_buffers = 2;1301 q->lock = &vou_dev->fop_lock;1302 q->dev = &pdev->dev;1303 ret = vb2_queue_init(q);1304 if (ret)1305 goto ei2cgadap;1306 1307 vdev->queue = q;1308 INIT_LIST_HEAD(&vou_dev->buf_list);1309 1310 pm_runtime_enable(&pdev->dev);1311 pm_runtime_resume(&pdev->dev);1312 1313 i2c_adap = i2c_get_adapter(vou_pdata->i2c_adap);1314 if (!i2c_adap) {1315 ret = -ENODEV;1316 goto ei2cgadap;1317 }1318 1319 ret = sh_vou_hw_init(vou_dev);1320 if (ret < 0)1321 goto ereset;1322 1323 subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap,1324 vou_pdata->board_info, NULL);1325 if (!subdev) {1326 ret = -ENOMEM;1327 goto ei2cnd;1328 }1329 1330 ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);1331 if (ret < 0)1332 goto evregdev;1333 1334 return 0;1335 1336evregdev:1337ei2cnd:1338ereset:1339 i2c_put_adapter(i2c_adap);1340ei2cgadap:1341 pm_runtime_disable(&pdev->dev);1342 v4l2_device_unregister(&vou_dev->v4l2_dev);1343 return ret;1344}1345 1346static void sh_vou_remove(struct platform_device *pdev)1347{1348 struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);1349 struct sh_vou_device *vou_dev = container_of(v4l2_dev,1350 struct sh_vou_device, v4l2_dev);1351 struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next,1352 struct v4l2_subdev, list);1353 struct i2c_client *client = v4l2_get_subdevdata(sd);1354 1355 pm_runtime_disable(&pdev->dev);1356 video_unregister_device(&vou_dev->vdev);1357 i2c_put_adapter(client->adapter);1358 v4l2_device_unregister(&vou_dev->v4l2_dev);1359}1360 1361static struct platform_driver sh_vou = {1362 .remove_new = sh_vou_remove,1363 .driver = {1364 .name = "sh-vou",1365 },1366};1367 1368module_platform_driver_probe(sh_vou, sh_vou_probe);1369 1370MODULE_DESCRIPTION("SuperH VOU driver");1371MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");1372MODULE_LICENSE("GPL v2");1373MODULE_VERSION("0.1.0");1374MODULE_ALIAS("platform:sh-vou");1375