brintos

brintos / linux-shallow public Read only

0
0
Text · 36.7 KiB · 1e74dd6 Raw
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