18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  tw68 functions to handle video data
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Much of this code is derived from the cx88 and sa7134 drivers, which
68c2ecf20Sopenharmony_ci *  were in turn derived from the bt87x driver.  The original work was by
78c2ecf20Sopenharmony_ci *  Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
88c2ecf20Sopenharmony_ci *  Hans Verkuil, Andy Walls and many others.  Their work is gratefully
98c2ecf20Sopenharmony_ci *  acknowledged.  Full credit goes to them - any problems within this code
108c2ecf20Sopenharmony_ci *  are mine.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *  Copyright (C) 2009  William M. Brack
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *  Refactored and updated to the latest v4l core frameworks:
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci *  Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl>
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
218c2ecf20Sopenharmony_ci#include <media/v4l2-event.h>
228c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-sg.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "tw68.h"
258c2ecf20Sopenharmony_ci#include "tw68-reg.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
288c2ecf20Sopenharmony_ci/* data structs for video                                             */
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * FIXME -
318c2ecf20Sopenharmony_ci * Note that the saa7134 has formats, e.g. YUV420, which are classified
328c2ecf20Sopenharmony_ci * as "planar".  These affect overlay mode, and are flagged with a field
338c2ecf20Sopenharmony_ci * ".planar" in the format.  Do we need to implement this in this driver?
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_cistatic const struct tw68_format formats[] = {
368c2ecf20Sopenharmony_ci	{
378c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_RGB555,
388c2ecf20Sopenharmony_ci		.depth		= 16,
398c2ecf20Sopenharmony_ci		.twformat	= ColorFormatRGB15,
408c2ecf20Sopenharmony_ci	}, {
418c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_RGB555X,
428c2ecf20Sopenharmony_ci		.depth		= 16,
438c2ecf20Sopenharmony_ci		.twformat	= ColorFormatRGB15 | ColorFormatBSWAP,
448c2ecf20Sopenharmony_ci	}, {
458c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_RGB565,
468c2ecf20Sopenharmony_ci		.depth		= 16,
478c2ecf20Sopenharmony_ci		.twformat	= ColorFormatRGB16,
488c2ecf20Sopenharmony_ci	}, {
498c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_RGB565X,
508c2ecf20Sopenharmony_ci		.depth		= 16,
518c2ecf20Sopenharmony_ci		.twformat	= ColorFormatRGB16 | ColorFormatBSWAP,
528c2ecf20Sopenharmony_ci	}, {
538c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_BGR24,
548c2ecf20Sopenharmony_ci		.depth		= 24,
558c2ecf20Sopenharmony_ci		.twformat	= ColorFormatRGB24,
568c2ecf20Sopenharmony_ci	}, {
578c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_RGB24,
588c2ecf20Sopenharmony_ci		.depth		= 24,
598c2ecf20Sopenharmony_ci		.twformat	= ColorFormatRGB24 | ColorFormatBSWAP,
608c2ecf20Sopenharmony_ci	}, {
618c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_BGR32,
628c2ecf20Sopenharmony_ci		.depth		= 32,
638c2ecf20Sopenharmony_ci		.twformat	= ColorFormatRGB32,
648c2ecf20Sopenharmony_ci	}, {
658c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_RGB32,
668c2ecf20Sopenharmony_ci		.depth		= 32,
678c2ecf20Sopenharmony_ci		.twformat	= ColorFormatRGB32 | ColorFormatBSWAP |
688c2ecf20Sopenharmony_ci				  ColorFormatWSWAP,
698c2ecf20Sopenharmony_ci	}, {
708c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_YUYV,
718c2ecf20Sopenharmony_ci		.depth		= 16,
728c2ecf20Sopenharmony_ci		.twformat	= ColorFormatYUY2,
738c2ecf20Sopenharmony_ci	}, {
748c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_UYVY,
758c2ecf20Sopenharmony_ci		.depth		= 16,
768c2ecf20Sopenharmony_ci		.twformat	= ColorFormatYUY2 | ColorFormatBSWAP,
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci#define FORMATS ARRAY_SIZE(formats)
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#define NORM_625_50			\
828c2ecf20Sopenharmony_ci		.h_delay	= 3,	\
838c2ecf20Sopenharmony_ci		.h_delay0	= 133,	\
848c2ecf20Sopenharmony_ci		.h_start	= 0,	\
858c2ecf20Sopenharmony_ci		.h_stop		= 719,	\
868c2ecf20Sopenharmony_ci		.v_delay	= 24,	\
878c2ecf20Sopenharmony_ci		.vbi_v_start_0	= 7,	\
888c2ecf20Sopenharmony_ci		.vbi_v_stop_0	= 22,	\
898c2ecf20Sopenharmony_ci		.video_v_start	= 24,	\
908c2ecf20Sopenharmony_ci		.video_v_stop	= 311,	\
918c2ecf20Sopenharmony_ci		.vbi_v_start_1	= 319
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define NORM_525_60			\
948c2ecf20Sopenharmony_ci		.h_delay	= 8,	\
958c2ecf20Sopenharmony_ci		.h_delay0	= 138,	\
968c2ecf20Sopenharmony_ci		.h_start	= 0,	\
978c2ecf20Sopenharmony_ci		.h_stop		= 719,	\
988c2ecf20Sopenharmony_ci		.v_delay	= 22,	\
998c2ecf20Sopenharmony_ci		.vbi_v_start_0	= 10,	\
1008c2ecf20Sopenharmony_ci		.vbi_v_stop_0	= 21,	\
1018c2ecf20Sopenharmony_ci		.video_v_start	= 22,	\
1028c2ecf20Sopenharmony_ci		.video_v_stop	= 262,	\
1038c2ecf20Sopenharmony_ci		.vbi_v_start_1	= 273
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/*
1068c2ecf20Sopenharmony_ci * The following table is searched by tw68_s_std, first for a specific
1078c2ecf20Sopenharmony_ci * match, then for an entry which contains the desired id.  The table
1088c2ecf20Sopenharmony_ci * entries should therefore be ordered in ascending order of specificity.
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_cistatic const struct tw68_tvnorm tvnorms[] = {
1118c2ecf20Sopenharmony_ci	{
1128c2ecf20Sopenharmony_ci		.name		= "PAL", /* autodetect */
1138c2ecf20Sopenharmony_ci		.id		= V4L2_STD_PAL,
1148c2ecf20Sopenharmony_ci		NORM_625_50,
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		.sync_control	= 0x18,
1178c2ecf20Sopenharmony_ci		.luma_control	= 0x40,
1188c2ecf20Sopenharmony_ci		.chroma_ctrl1	= 0x81,
1198c2ecf20Sopenharmony_ci		.chroma_gain	= 0x2a,
1208c2ecf20Sopenharmony_ci		.chroma_ctrl2	= 0x06,
1218c2ecf20Sopenharmony_ci		.vgate_misc	= 0x1c,
1228c2ecf20Sopenharmony_ci		.format		= VideoFormatPALBDGHI,
1238c2ecf20Sopenharmony_ci	}, {
1248c2ecf20Sopenharmony_ci		.name		= "NTSC",
1258c2ecf20Sopenharmony_ci		.id		= V4L2_STD_NTSC,
1268c2ecf20Sopenharmony_ci		NORM_525_60,
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		.sync_control	= 0x59,
1298c2ecf20Sopenharmony_ci		.luma_control	= 0x40,
1308c2ecf20Sopenharmony_ci		.chroma_ctrl1	= 0x89,
1318c2ecf20Sopenharmony_ci		.chroma_gain	= 0x2a,
1328c2ecf20Sopenharmony_ci		.chroma_ctrl2	= 0x0e,
1338c2ecf20Sopenharmony_ci		.vgate_misc	= 0x18,
1348c2ecf20Sopenharmony_ci		.format		= VideoFormatNTSC,
1358c2ecf20Sopenharmony_ci	}, {
1368c2ecf20Sopenharmony_ci		.name		= "SECAM",
1378c2ecf20Sopenharmony_ci		.id		= V4L2_STD_SECAM,
1388c2ecf20Sopenharmony_ci		NORM_625_50,
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci		.sync_control	= 0x18,
1418c2ecf20Sopenharmony_ci		.luma_control	= 0x1b,
1428c2ecf20Sopenharmony_ci		.chroma_ctrl1	= 0xd1,
1438c2ecf20Sopenharmony_ci		.chroma_gain	= 0x80,
1448c2ecf20Sopenharmony_ci		.chroma_ctrl2	= 0x00,
1458c2ecf20Sopenharmony_ci		.vgate_misc	= 0x1c,
1468c2ecf20Sopenharmony_ci		.format		= VideoFormatSECAM,
1478c2ecf20Sopenharmony_ci	}, {
1488c2ecf20Sopenharmony_ci		.name		= "PAL-M",
1498c2ecf20Sopenharmony_ci		.id		= V4L2_STD_PAL_M,
1508c2ecf20Sopenharmony_ci		NORM_525_60,
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		.sync_control	= 0x59,
1538c2ecf20Sopenharmony_ci		.luma_control	= 0x40,
1548c2ecf20Sopenharmony_ci		.chroma_ctrl1	= 0xb9,
1558c2ecf20Sopenharmony_ci		.chroma_gain	= 0x2a,
1568c2ecf20Sopenharmony_ci		.chroma_ctrl2	= 0x0e,
1578c2ecf20Sopenharmony_ci		.vgate_misc	= 0x18,
1588c2ecf20Sopenharmony_ci		.format		= VideoFormatPALM,
1598c2ecf20Sopenharmony_ci	}, {
1608c2ecf20Sopenharmony_ci		.name		= "PAL-Nc",
1618c2ecf20Sopenharmony_ci		.id		= V4L2_STD_PAL_Nc,
1628c2ecf20Sopenharmony_ci		NORM_625_50,
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		.sync_control	= 0x18,
1658c2ecf20Sopenharmony_ci		.luma_control	= 0x40,
1668c2ecf20Sopenharmony_ci		.chroma_ctrl1	= 0xa1,
1678c2ecf20Sopenharmony_ci		.chroma_gain	= 0x2a,
1688c2ecf20Sopenharmony_ci		.chroma_ctrl2	= 0x06,
1698c2ecf20Sopenharmony_ci		.vgate_misc	= 0x1c,
1708c2ecf20Sopenharmony_ci		.format		= VideoFormatPALNC,
1718c2ecf20Sopenharmony_ci	}, {
1728c2ecf20Sopenharmony_ci		.name		= "PAL-60",
1738c2ecf20Sopenharmony_ci		.id		= V4L2_STD_PAL_60,
1748c2ecf20Sopenharmony_ci		.h_delay	= 186,
1758c2ecf20Sopenharmony_ci		.h_start	= 0,
1768c2ecf20Sopenharmony_ci		.h_stop		= 719,
1778c2ecf20Sopenharmony_ci		.v_delay	= 26,
1788c2ecf20Sopenharmony_ci		.video_v_start	= 23,
1798c2ecf20Sopenharmony_ci		.video_v_stop	= 262,
1808c2ecf20Sopenharmony_ci		.vbi_v_start_0	= 10,
1818c2ecf20Sopenharmony_ci		.vbi_v_stop_0	= 21,
1828c2ecf20Sopenharmony_ci		.vbi_v_start_1	= 273,
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		.sync_control	= 0x18,
1858c2ecf20Sopenharmony_ci		.luma_control	= 0x40,
1868c2ecf20Sopenharmony_ci		.chroma_ctrl1	= 0x81,
1878c2ecf20Sopenharmony_ci		.chroma_gain	= 0x2a,
1888c2ecf20Sopenharmony_ci		.chroma_ctrl2	= 0x06,
1898c2ecf20Sopenharmony_ci		.vgate_misc	= 0x1c,
1908c2ecf20Sopenharmony_ci		.format		= VideoFormatPAL60,
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci};
1938c2ecf20Sopenharmony_ci#define TVNORMS ARRAY_SIZE(tvnorms)
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic const struct tw68_format *format_by_fourcc(unsigned int fourcc)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	unsigned int i;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	for (i = 0; i < FORMATS; i++)
2008c2ecf20Sopenharmony_ci		if (formats[i].fourcc == fourcc)
2018c2ecf20Sopenharmony_ci			return formats+i;
2028c2ecf20Sopenharmony_ci	return NULL;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
2078c2ecf20Sopenharmony_ci/*
2088c2ecf20Sopenharmony_ci * Note that the cropping rectangles are described in terms of a single
2098c2ecf20Sopenharmony_ci * frame, i.e. line positions are only 1/2 the interlaced equivalent
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_cistatic void set_tvnorm(struct tw68_dev *dev, const struct tw68_tvnorm *norm)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	if (norm != dev->tvnorm) {
2148c2ecf20Sopenharmony_ci		dev->width = 720;
2158c2ecf20Sopenharmony_ci		dev->height = (norm->id & V4L2_STD_525_60) ? 480 : 576;
2168c2ecf20Sopenharmony_ci		dev->tvnorm = norm;
2178c2ecf20Sopenharmony_ci		tw68_set_tvnorm_hw(dev);
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/*
2228c2ecf20Sopenharmony_ci * tw68_set_scale
2238c2ecf20Sopenharmony_ci *
2248c2ecf20Sopenharmony_ci * Scaling and Cropping for video decoding
2258c2ecf20Sopenharmony_ci *
2268c2ecf20Sopenharmony_ci * We are working with 3 values for horizontal and vertical - scale,
2278c2ecf20Sopenharmony_ci * delay and active.
2288c2ecf20Sopenharmony_ci *
2298c2ecf20Sopenharmony_ci * HACTIVE represent the actual number of pixels in the "usable" image,
2308c2ecf20Sopenharmony_ci * before scaling.  HDELAY represents the number of pixels skipped
2318c2ecf20Sopenharmony_ci * between the start of the horizontal sync and the start of the image.
2328c2ecf20Sopenharmony_ci * HSCALE is calculated using the formula
2338c2ecf20Sopenharmony_ci *	HSCALE = (HACTIVE / (#pixels desired)) * 256
2348c2ecf20Sopenharmony_ci *
2358c2ecf20Sopenharmony_ci * The vertical registers are similar, except based upon the total number
2368c2ecf20Sopenharmony_ci * of lines in the image, and the first line of the image (i.e. ignoring
2378c2ecf20Sopenharmony_ci * vertical sync and VBI).
2388c2ecf20Sopenharmony_ci *
2398c2ecf20Sopenharmony_ci * Note that the number of bytes reaching the FIFO (and hence needing
2408c2ecf20Sopenharmony_ci * to be processed by the DMAP program) is completely dependent upon
2418c2ecf20Sopenharmony_ci * these values, especially HSCALE.
2428c2ecf20Sopenharmony_ci *
2438c2ecf20Sopenharmony_ci * Parameters:
2448c2ecf20Sopenharmony_ci *	@dev		pointer to the device structure, needed for
2458c2ecf20Sopenharmony_ci *			getting current norm (as well as debug print)
2468c2ecf20Sopenharmony_ci *	@width		actual image width (from user buffer)
2478c2ecf20Sopenharmony_ci *	@height		actual image height
2488c2ecf20Sopenharmony_ci *	@field		indicates Top, Bottom or Interlaced
2498c2ecf20Sopenharmony_ci */
2508c2ecf20Sopenharmony_cistatic int tw68_set_scale(struct tw68_dev *dev, unsigned int width,
2518c2ecf20Sopenharmony_ci			  unsigned int height, enum v4l2_field field)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	const struct tw68_tvnorm *norm = dev->tvnorm;
2548c2ecf20Sopenharmony_ci	/* set individually for debugging clarity */
2558c2ecf20Sopenharmony_ci	int hactive, hdelay, hscale;
2568c2ecf20Sopenharmony_ci	int vactive, vdelay, vscale;
2578c2ecf20Sopenharmony_ci	int comb;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (V4L2_FIELD_HAS_BOTH(field))	/* if field is interlaced */
2608c2ecf20Sopenharmony_ci		height /= 2;		/* we must set for 1-frame */
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	pr_debug("%s: width=%d, height=%d, both=%d\n"
2638c2ecf20Sopenharmony_ci		 "  tvnorm h_delay=%d, h_start=%d, h_stop=%d, v_delay=%d, v_start=%d, v_stop=%d\n",
2648c2ecf20Sopenharmony_ci		__func__, width, height, V4L2_FIELD_HAS_BOTH(field),
2658c2ecf20Sopenharmony_ci		norm->h_delay, norm->h_start, norm->h_stop,
2668c2ecf20Sopenharmony_ci		norm->v_delay, norm->video_v_start,
2678c2ecf20Sopenharmony_ci		norm->video_v_stop);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	switch (dev->vdecoder) {
2708c2ecf20Sopenharmony_ci	case TW6800:
2718c2ecf20Sopenharmony_ci		hdelay = norm->h_delay0;
2728c2ecf20Sopenharmony_ci		break;
2738c2ecf20Sopenharmony_ci	default:
2748c2ecf20Sopenharmony_ci		hdelay = norm->h_delay;
2758c2ecf20Sopenharmony_ci		break;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	hdelay += norm->h_start;
2798c2ecf20Sopenharmony_ci	hactive = norm->h_stop - norm->h_start + 1;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	hscale = (hactive * 256) / (width);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	vdelay = norm->v_delay;
2848c2ecf20Sopenharmony_ci	vactive = ((norm->id & V4L2_STD_525_60) ? 524 : 624) / 2 - norm->video_v_start;
2858c2ecf20Sopenharmony_ci	vscale = (vactive * 256) / height;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	pr_debug("%s: %dx%d [%s%s,%s]\n", __func__,
2888c2ecf20Sopenharmony_ci		width, height,
2898c2ecf20Sopenharmony_ci		V4L2_FIELD_HAS_TOP(field)    ? "T" : "",
2908c2ecf20Sopenharmony_ci		V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
2918c2ecf20Sopenharmony_ci		v4l2_norm_to_name(dev->tvnorm->id));
2928c2ecf20Sopenharmony_ci	pr_debug("%s: hactive=%d, hdelay=%d, hscale=%d; vactive=%d, vdelay=%d, vscale=%d\n",
2938c2ecf20Sopenharmony_ci		 __func__,
2948c2ecf20Sopenharmony_ci		hactive, hdelay, hscale, vactive, vdelay, vscale);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	comb =	((vdelay & 0x300)  >> 2) |
2978c2ecf20Sopenharmony_ci		((vactive & 0x300) >> 4) |
2988c2ecf20Sopenharmony_ci		((hdelay & 0x300)  >> 6) |
2998c2ecf20Sopenharmony_ci		((hactive & 0x300) >> 8);
3008c2ecf20Sopenharmony_ci	pr_debug("%s: setting CROP_HI=%02x, VDELAY_LO=%02x, VACTIVE_LO=%02x, HDELAY_LO=%02x, HACTIVE_LO=%02x\n",
3018c2ecf20Sopenharmony_ci		__func__, comb, vdelay, vactive, hdelay, hactive);
3028c2ecf20Sopenharmony_ci	tw_writeb(TW68_CROP_HI, comb);
3038c2ecf20Sopenharmony_ci	tw_writeb(TW68_VDELAY_LO, vdelay & 0xff);
3048c2ecf20Sopenharmony_ci	tw_writeb(TW68_VACTIVE_LO, vactive & 0xff);
3058c2ecf20Sopenharmony_ci	tw_writeb(TW68_HDELAY_LO, hdelay & 0xff);
3068c2ecf20Sopenharmony_ci	tw_writeb(TW68_HACTIVE_LO, hactive & 0xff);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	comb = ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8);
3098c2ecf20Sopenharmony_ci	pr_debug("%s: setting SCALE_HI=%02x, VSCALE_LO=%02x, HSCALE_LO=%02x\n",
3108c2ecf20Sopenharmony_ci		 __func__, comb, vscale, hscale);
3118c2ecf20Sopenharmony_ci	tw_writeb(TW68_SCALE_HI, comb);
3128c2ecf20Sopenharmony_ci	tw_writeb(TW68_VSCALE_LO, vscale);
3138c2ecf20Sopenharmony_ci	tw_writeb(TW68_HSCALE_LO, hscale);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ciint tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	/* Set cropping and scaling */
3238c2ecf20Sopenharmony_ci	tw68_set_scale(dev, dev->width, dev->height, dev->field);
3248c2ecf20Sopenharmony_ci	/*
3258c2ecf20Sopenharmony_ci	 *  Set start address for RISC program.  Note that if the DMAP
3268c2ecf20Sopenharmony_ci	 *  processor is currently running, it must be stopped before
3278c2ecf20Sopenharmony_ci	 *  a new address can be set.
3288c2ecf20Sopenharmony_ci	 */
3298c2ecf20Sopenharmony_ci	tw_clearl(TW68_DMAC, TW68_DMAP_EN);
3308c2ecf20Sopenharmony_ci	tw_writel(TW68_DMAP_SA, buf->dma);
3318c2ecf20Sopenharmony_ci	/* Clear any pending interrupts */
3328c2ecf20Sopenharmony_ci	tw_writel(TW68_INTSTAT, dev->board_virqmask);
3338c2ecf20Sopenharmony_ci	/* Enable the risc engine and the fifo */
3348c2ecf20Sopenharmony_ci	tw_andorl(TW68_DMAC, 0xff, dev->fmt->twformat |
3358c2ecf20Sopenharmony_ci		ColorFormatGamma | TW68_DMAP_EN | TW68_FIFO_EN);
3368c2ecf20Sopenharmony_ci	dev->pci_irqmask |= dev->board_virqmask;
3378c2ecf20Sopenharmony_ci	tw_setl(TW68_INTMASK, dev->pci_irqmask);
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci/* calc max # of buffers from size (must not exceed the 4MB virtual
3448c2ecf20Sopenharmony_ci * address space per DMA channel) */
3458c2ecf20Sopenharmony_cistatic int tw68_buffer_count(unsigned int size, unsigned int count)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	unsigned int maxcount;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	maxcount = (4 * 1024 * 1024) / roundup(size, PAGE_SIZE);
3508c2ecf20Sopenharmony_ci	if (count > maxcount)
3518c2ecf20Sopenharmony_ci		count = maxcount;
3528c2ecf20Sopenharmony_ci	return count;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/* ------------------------------------------------------------- */
3568c2ecf20Sopenharmony_ci/* vb2 queue operations                                          */
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int tw68_queue_setup(struct vb2_queue *q,
3598c2ecf20Sopenharmony_ci			   unsigned int *num_buffers, unsigned int *num_planes,
3608c2ecf20Sopenharmony_ci			   unsigned int sizes[], struct device *alloc_devs[])
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct tw68_dev *dev = vb2_get_drv_priv(q);
3638c2ecf20Sopenharmony_ci	unsigned tot_bufs = q->num_buffers + *num_buffers;
3648c2ecf20Sopenharmony_ci	unsigned size = (dev->fmt->depth * dev->width * dev->height) >> 3;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (tot_bufs < 2)
3678c2ecf20Sopenharmony_ci		tot_bufs = 2;
3688c2ecf20Sopenharmony_ci	tot_bufs = tw68_buffer_count(size, tot_bufs);
3698c2ecf20Sopenharmony_ci	*num_buffers = tot_bufs - q->num_buffers;
3708c2ecf20Sopenharmony_ci	/*
3718c2ecf20Sopenharmony_ci	 * We allow create_bufs, but only if the sizeimage is >= as the
3728c2ecf20Sopenharmony_ci	 * current sizeimage. The tw68_buffer_count calculation becomes quite
3738c2ecf20Sopenharmony_ci	 * difficult otherwise.
3748c2ecf20Sopenharmony_ci	 */
3758c2ecf20Sopenharmony_ci	if (*num_planes)
3768c2ecf20Sopenharmony_ci		return sizes[0] < size ? -EINVAL : 0;
3778c2ecf20Sopenharmony_ci	*num_planes = 1;
3788c2ecf20Sopenharmony_ci	sizes[0] = size;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return 0;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/*
3848c2ecf20Sopenharmony_ci * The risc program for each buffers works as follows: it starts with a simple
3858c2ecf20Sopenharmony_ci * 'JUMP to addr + 8', which is effectively a NOP. Then the program to DMA the
3868c2ecf20Sopenharmony_ci * buffer follows and at the end we have a JUMP back to the start + 8 (skipping
3878c2ecf20Sopenharmony_ci * the initial JUMP).
3888c2ecf20Sopenharmony_ci *
3898c2ecf20Sopenharmony_ci * This is the program of the first buffer to be queued if the active list is
3908c2ecf20Sopenharmony_ci * empty and it just keeps DMAing this buffer without generating any interrupts.
3918c2ecf20Sopenharmony_ci *
3928c2ecf20Sopenharmony_ci * If a new buffer is added then the initial JUMP in the program generates an
3938c2ecf20Sopenharmony_ci * interrupt as well which signals that the previous buffer has been DMAed
3948c2ecf20Sopenharmony_ci * successfully and that it can be returned to userspace.
3958c2ecf20Sopenharmony_ci *
3968c2ecf20Sopenharmony_ci * It also sets the final jump of the previous buffer to the start of the new
3978c2ecf20Sopenharmony_ci * buffer, thus chaining the new buffer into the DMA chain. This is a single
3988c2ecf20Sopenharmony_ci * atomic u32 write, so there is no race condition.
3998c2ecf20Sopenharmony_ci *
4008c2ecf20Sopenharmony_ci * The end-result of all this that you only get an interrupt when a buffer
4018c2ecf20Sopenharmony_ci * is ready, so the control flow is very easy.
4028c2ecf20Sopenharmony_ci */
4038c2ecf20Sopenharmony_cistatic void tw68_buf_queue(struct vb2_buffer *vb)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
4068c2ecf20Sopenharmony_ci	struct vb2_queue *vq = vb->vb2_queue;
4078c2ecf20Sopenharmony_ci	struct tw68_dev *dev = vb2_get_drv_priv(vq);
4088c2ecf20Sopenharmony_ci	struct tw68_buf *buf = container_of(vbuf, struct tw68_buf, vb);
4098c2ecf20Sopenharmony_ci	struct tw68_buf *prev;
4108c2ecf20Sopenharmony_ci	unsigned long flags;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* append a 'JUMP to start of buffer' to the buffer risc program */
4158c2ecf20Sopenharmony_ci	buf->jmp[0] = cpu_to_le32(RISC_JUMP);
4168c2ecf20Sopenharmony_ci	buf->jmp[1] = cpu_to_le32(buf->dma + 8);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (!list_empty(&dev->active)) {
4198c2ecf20Sopenharmony_ci		prev = list_entry(dev->active.prev, struct tw68_buf, list);
4208c2ecf20Sopenharmony_ci		buf->cpu[0] |= cpu_to_le32(RISC_INT_BIT);
4218c2ecf20Sopenharmony_ci		prev->jmp[1] = cpu_to_le32(buf->dma);
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci	list_add_tail(&buf->list, &dev->active);
4248c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci/*
4288c2ecf20Sopenharmony_ci * buffer_prepare
4298c2ecf20Sopenharmony_ci *
4308c2ecf20Sopenharmony_ci * Set the ancillary information into the buffer structure.  This
4318c2ecf20Sopenharmony_ci * includes generating the necessary risc program if it hasn't already
4328c2ecf20Sopenharmony_ci * been done for the current buffer format.
4338c2ecf20Sopenharmony_ci * The structure fh contains the details of the format requested by the
4348c2ecf20Sopenharmony_ci * user - type, width, height and #fields.  This is compared with the
4358c2ecf20Sopenharmony_ci * last format set for the current buffer.  If they differ, the risc
4368c2ecf20Sopenharmony_ci * code (which controls the filling of the buffer) is (re-)generated.
4378c2ecf20Sopenharmony_ci */
4388c2ecf20Sopenharmony_cistatic int tw68_buf_prepare(struct vb2_buffer *vb)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
4418c2ecf20Sopenharmony_ci	struct vb2_queue *vq = vb->vb2_queue;
4428c2ecf20Sopenharmony_ci	struct tw68_dev *dev = vb2_get_drv_priv(vq);
4438c2ecf20Sopenharmony_ci	struct tw68_buf *buf = container_of(vbuf, struct tw68_buf, vb);
4448c2ecf20Sopenharmony_ci	struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0);
4458c2ecf20Sopenharmony_ci	unsigned size, bpl;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	size = (dev->width * dev->height * dev->fmt->depth) >> 3;
4488c2ecf20Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size)
4498c2ecf20Sopenharmony_ci		return -EINVAL;
4508c2ecf20Sopenharmony_ci	vb2_set_plane_payload(vb, 0, size);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	bpl = (dev->width * dev->fmt->depth) >> 3;
4538c2ecf20Sopenharmony_ci	switch (dev->field) {
4548c2ecf20Sopenharmony_ci	case V4L2_FIELD_TOP:
4558c2ecf20Sopenharmony_ci		tw68_risc_buffer(dev->pci, buf, dma->sgl,
4568c2ecf20Sopenharmony_ci				 0, UNSET, bpl, 0, dev->height);
4578c2ecf20Sopenharmony_ci		break;
4588c2ecf20Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
4598c2ecf20Sopenharmony_ci		tw68_risc_buffer(dev->pci, buf, dma->sgl,
4608c2ecf20Sopenharmony_ci				 UNSET, 0, bpl, 0, dev->height);
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci	case V4L2_FIELD_SEQ_TB:
4638c2ecf20Sopenharmony_ci		tw68_risc_buffer(dev->pci, buf, dma->sgl,
4648c2ecf20Sopenharmony_ci				 0, bpl * (dev->height >> 1),
4658c2ecf20Sopenharmony_ci				 bpl, 0, dev->height >> 1);
4668c2ecf20Sopenharmony_ci		break;
4678c2ecf20Sopenharmony_ci	case V4L2_FIELD_SEQ_BT:
4688c2ecf20Sopenharmony_ci		tw68_risc_buffer(dev->pci, buf, dma->sgl,
4698c2ecf20Sopenharmony_ci				 bpl * (dev->height >> 1), 0,
4708c2ecf20Sopenharmony_ci				 bpl, 0, dev->height >> 1);
4718c2ecf20Sopenharmony_ci		break;
4728c2ecf20Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
4738c2ecf20Sopenharmony_ci	default:
4748c2ecf20Sopenharmony_ci		tw68_risc_buffer(dev->pci, buf, dma->sgl,
4758c2ecf20Sopenharmony_ci				 0, bpl, bpl, bpl, dev->height >> 1);
4768c2ecf20Sopenharmony_ci		break;
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci	return 0;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic void tw68_buf_finish(struct vb2_buffer *vb)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
4848c2ecf20Sopenharmony_ci	struct vb2_queue *vq = vb->vb2_queue;
4858c2ecf20Sopenharmony_ci	struct tw68_dev *dev = vb2_get_drv_priv(vq);
4868c2ecf20Sopenharmony_ci	struct tw68_buf *buf = container_of(vbuf, struct tw68_buf, vb);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	pci_free_consistent(dev->pci, buf->size, buf->cpu, buf->dma);
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic int tw68_start_streaming(struct vb2_queue *q, unsigned int count)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct tw68_dev *dev = vb2_get_drv_priv(q);
4948c2ecf20Sopenharmony_ci	struct tw68_buf *buf =
4958c2ecf20Sopenharmony_ci		container_of(dev->active.next, struct tw68_buf, list);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	dev->seqnr = 0;
4988c2ecf20Sopenharmony_ci	tw68_video_start_dma(dev, buf);
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic void tw68_stop_streaming(struct vb2_queue *q)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct tw68_dev *dev = vb2_get_drv_priv(q);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	/* Stop risc & fifo */
5078c2ecf20Sopenharmony_ci	tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
5088c2ecf20Sopenharmony_ci	while (!list_empty(&dev->active)) {
5098c2ecf20Sopenharmony_ci		struct tw68_buf *buf =
5108c2ecf20Sopenharmony_ci			container_of(dev->active.next, struct tw68_buf, list);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		list_del(&buf->list);
5138c2ecf20Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic const struct vb2_ops tw68_video_qops = {
5188c2ecf20Sopenharmony_ci	.queue_setup	= tw68_queue_setup,
5198c2ecf20Sopenharmony_ci	.buf_queue	= tw68_buf_queue,
5208c2ecf20Sopenharmony_ci	.buf_prepare	= tw68_buf_prepare,
5218c2ecf20Sopenharmony_ci	.buf_finish	= tw68_buf_finish,
5228c2ecf20Sopenharmony_ci	.start_streaming = tw68_start_streaming,
5238c2ecf20Sopenharmony_ci	.stop_streaming = tw68_stop_streaming,
5248c2ecf20Sopenharmony_ci	.wait_prepare	= vb2_ops_wait_prepare,
5258c2ecf20Sopenharmony_ci	.wait_finish	= vb2_ops_wait_finish,
5268c2ecf20Sopenharmony_ci};
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic int tw68_s_ctrl(struct v4l2_ctrl *ctrl)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	struct tw68_dev *dev =
5338c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct tw68_dev, hdl);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	switch (ctrl->id) {
5368c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
5378c2ecf20Sopenharmony_ci		tw_writeb(TW68_BRIGHT, ctrl->val);
5388c2ecf20Sopenharmony_ci		break;
5398c2ecf20Sopenharmony_ci	case V4L2_CID_HUE:
5408c2ecf20Sopenharmony_ci		tw_writeb(TW68_HUE, ctrl->val);
5418c2ecf20Sopenharmony_ci		break;
5428c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
5438c2ecf20Sopenharmony_ci		tw_writeb(TW68_CONTRAST, ctrl->val);
5448c2ecf20Sopenharmony_ci		break;
5458c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
5468c2ecf20Sopenharmony_ci		tw_writeb(TW68_SAT_U, ctrl->val);
5478c2ecf20Sopenharmony_ci		tw_writeb(TW68_SAT_V, ctrl->val);
5488c2ecf20Sopenharmony_ci		break;
5498c2ecf20Sopenharmony_ci	case V4L2_CID_COLOR_KILLER:
5508c2ecf20Sopenharmony_ci		if (ctrl->val)
5518c2ecf20Sopenharmony_ci			tw_andorb(TW68_MISC2, 0xe0, 0xe0);
5528c2ecf20Sopenharmony_ci		else
5538c2ecf20Sopenharmony_ci			tw_andorb(TW68_MISC2, 0xe0, 0x00);
5548c2ecf20Sopenharmony_ci		break;
5558c2ecf20Sopenharmony_ci	case V4L2_CID_CHROMA_AGC:
5568c2ecf20Sopenharmony_ci		if (ctrl->val)
5578c2ecf20Sopenharmony_ci			tw_andorb(TW68_LOOP, 0x30, 0x20);
5588c2ecf20Sopenharmony_ci		else
5598c2ecf20Sopenharmony_ci			tw_andorb(TW68_LOOP, 0x30, 0x00);
5608c2ecf20Sopenharmony_ci		break;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci	return 0;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci/*
5688c2ecf20Sopenharmony_ci * Note that this routine returns what is stored in the fh structure, and
5698c2ecf20Sopenharmony_ci * does not interrogate any of the device registers.
5708c2ecf20Sopenharmony_ci */
5718c2ecf20Sopenharmony_cistatic int tw68_g_fmt_vid_cap(struct file *file, void *priv,
5728c2ecf20Sopenharmony_ci				struct v4l2_format *f)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	f->fmt.pix.width        = dev->width;
5778c2ecf20Sopenharmony_ci	f->fmt.pix.height       = dev->height;
5788c2ecf20Sopenharmony_ci	f->fmt.pix.field        = dev->field;
5798c2ecf20Sopenharmony_ci	f->fmt.pix.pixelformat  = dev->fmt->fourcc;
5808c2ecf20Sopenharmony_ci	f->fmt.pix.bytesperline =
5818c2ecf20Sopenharmony_ci		(f->fmt.pix.width * (dev->fmt->depth)) >> 3;
5828c2ecf20Sopenharmony_ci	f->fmt.pix.sizeimage =
5838c2ecf20Sopenharmony_ci		f->fmt.pix.height * f->fmt.pix.bytesperline;
5848c2ecf20Sopenharmony_ci	f->fmt.pix.colorspace	= V4L2_COLORSPACE_SMPTE170M;
5858c2ecf20Sopenharmony_ci	return 0;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic int tw68_try_fmt_vid_cap(struct file *file, void *priv,
5898c2ecf20Sopenharmony_ci						struct v4l2_format *f)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
5928c2ecf20Sopenharmony_ci	const struct tw68_format *fmt;
5938c2ecf20Sopenharmony_ci	enum v4l2_field field;
5948c2ecf20Sopenharmony_ci	unsigned int maxh;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
5978c2ecf20Sopenharmony_ci	if (NULL == fmt)
5988c2ecf20Sopenharmony_ci		return -EINVAL;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	field = f->fmt.pix.field;
6018c2ecf20Sopenharmony_ci	maxh  = (dev->tvnorm->id & V4L2_STD_525_60) ? 480 : 576;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	switch (field) {
6048c2ecf20Sopenharmony_ci	case V4L2_FIELD_TOP:
6058c2ecf20Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
6068c2ecf20Sopenharmony_ci		break;
6078c2ecf20Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
6088c2ecf20Sopenharmony_ci	case V4L2_FIELD_SEQ_BT:
6098c2ecf20Sopenharmony_ci	case V4L2_FIELD_SEQ_TB:
6108c2ecf20Sopenharmony_ci		maxh = maxh * 2;
6118c2ecf20Sopenharmony_ci		break;
6128c2ecf20Sopenharmony_ci	default:
6138c2ecf20Sopenharmony_ci		field = (f->fmt.pix.height > maxh / 2)
6148c2ecf20Sopenharmony_ci			? V4L2_FIELD_INTERLACED
6158c2ecf20Sopenharmony_ci			: V4L2_FIELD_BOTTOM;
6168c2ecf20Sopenharmony_ci		break;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	f->fmt.pix.field = field;
6208c2ecf20Sopenharmony_ci	if (f->fmt.pix.width  < 48)
6218c2ecf20Sopenharmony_ci		f->fmt.pix.width  = 48;
6228c2ecf20Sopenharmony_ci	if (f->fmt.pix.height < 32)
6238c2ecf20Sopenharmony_ci		f->fmt.pix.height = 32;
6248c2ecf20Sopenharmony_ci	if (f->fmt.pix.width > 720)
6258c2ecf20Sopenharmony_ci		f->fmt.pix.width = 720;
6268c2ecf20Sopenharmony_ci	if (f->fmt.pix.height > maxh)
6278c2ecf20Sopenharmony_ci		f->fmt.pix.height = maxh;
6288c2ecf20Sopenharmony_ci	f->fmt.pix.width &= ~0x03;
6298c2ecf20Sopenharmony_ci	f->fmt.pix.bytesperline =
6308c2ecf20Sopenharmony_ci		(f->fmt.pix.width * (fmt->depth)) >> 3;
6318c2ecf20Sopenharmony_ci	f->fmt.pix.sizeimage =
6328c2ecf20Sopenharmony_ci		f->fmt.pix.height * f->fmt.pix.bytesperline;
6338c2ecf20Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
6348c2ecf20Sopenharmony_ci	return 0;
6358c2ecf20Sopenharmony_ci}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci/*
6388c2ecf20Sopenharmony_ci * Note that tw68_s_fmt_vid_cap sets the information into the fh structure,
6398c2ecf20Sopenharmony_ci * and it will be used for all future new buffers.  However, there could be
6408c2ecf20Sopenharmony_ci * some number of buffers on the "active" chain which will be filled before
6418c2ecf20Sopenharmony_ci * the change takes place.
6428c2ecf20Sopenharmony_ci */
6438c2ecf20Sopenharmony_cistatic int tw68_s_fmt_vid_cap(struct file *file, void *priv,
6448c2ecf20Sopenharmony_ci					struct v4l2_format *f)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
6478c2ecf20Sopenharmony_ci	int err;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	err = tw68_try_fmt_vid_cap(file, priv, f);
6508c2ecf20Sopenharmony_ci	if (0 != err)
6518c2ecf20Sopenharmony_ci		return err;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
6548c2ecf20Sopenharmony_ci	dev->width = f->fmt.pix.width;
6558c2ecf20Sopenharmony_ci	dev->height = f->fmt.pix.height;
6568c2ecf20Sopenharmony_ci	dev->field = f->fmt.pix.field;
6578c2ecf20Sopenharmony_ci	return 0;
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int tw68_enum_input(struct file *file, void *priv,
6618c2ecf20Sopenharmony_ci					struct v4l2_input *i)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
6648c2ecf20Sopenharmony_ci	unsigned int n;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	n = i->index;
6678c2ecf20Sopenharmony_ci	if (n >= TW68_INPUT_MAX)
6688c2ecf20Sopenharmony_ci		return -EINVAL;
6698c2ecf20Sopenharmony_ci	i->index = n;
6708c2ecf20Sopenharmony_ci	i->type = V4L2_INPUT_TYPE_CAMERA;
6718c2ecf20Sopenharmony_ci	snprintf(i->name, sizeof(i->name), "Composite %d", n);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	/* If the query is for the current input, get live data */
6748c2ecf20Sopenharmony_ci	if (n == dev->input) {
6758c2ecf20Sopenharmony_ci		int v1 = tw_readb(TW68_STATUS1);
6768c2ecf20Sopenharmony_ci		int v2 = tw_readb(TW68_MVSN);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci		if (0 != (v1 & (1 << 7)))
6798c2ecf20Sopenharmony_ci			i->status |= V4L2_IN_ST_NO_SYNC;
6808c2ecf20Sopenharmony_ci		if (0 != (v1 & (1 << 6)))
6818c2ecf20Sopenharmony_ci			i->status |= V4L2_IN_ST_NO_H_LOCK;
6828c2ecf20Sopenharmony_ci		if (0 != (v1 & (1 << 2)))
6838c2ecf20Sopenharmony_ci			i->status |= V4L2_IN_ST_NO_SIGNAL;
6848c2ecf20Sopenharmony_ci		if (0 != (v1 & 1 << 1))
6858c2ecf20Sopenharmony_ci			i->status |= V4L2_IN_ST_NO_COLOR;
6868c2ecf20Sopenharmony_ci		if (0 != (v2 & (1 << 2)))
6878c2ecf20Sopenharmony_ci			i->status |= V4L2_IN_ST_MACROVISION;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci	i->std = video_devdata(file)->tvnorms;
6908c2ecf20Sopenharmony_ci	return 0;
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic int tw68_g_input(struct file *file, void *priv, unsigned int *i)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	*i = dev->input;
6988c2ecf20Sopenharmony_ci	return 0;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic int tw68_s_input(struct file *file, void *priv, unsigned int i)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (i >= TW68_INPUT_MAX)
7068c2ecf20Sopenharmony_ci		return -EINVAL;
7078c2ecf20Sopenharmony_ci	dev->input = i;
7088c2ecf20Sopenharmony_ci	tw_andorb(TW68_INFORM, 0x03 << 2, dev->input << 2);
7098c2ecf20Sopenharmony_ci	return 0;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic int tw68_querycap(struct file *file, void  *priv,
7138c2ecf20Sopenharmony_ci					struct v4l2_capability *cap)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	strscpy(cap->driver, "tw68", sizeof(cap->driver));
7188c2ecf20Sopenharmony_ci	strscpy(cap->card, "Techwell Capture Card",
7198c2ecf20Sopenharmony_ci		sizeof(cap->card));
7208c2ecf20Sopenharmony_ci	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
7218c2ecf20Sopenharmony_ci	return 0;
7228c2ecf20Sopenharmony_ci}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic int tw68_s_std(struct file *file, void *priv, v4l2_std_id id)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
7278c2ecf20Sopenharmony_ci	unsigned int i;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	if (vb2_is_busy(&dev->vidq))
7308c2ecf20Sopenharmony_ci		return -EBUSY;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	/* Look for match on complete norm id (may have mult bits) */
7338c2ecf20Sopenharmony_ci	for (i = 0; i < TVNORMS; i++) {
7348c2ecf20Sopenharmony_ci		if (id == tvnorms[i].id)
7358c2ecf20Sopenharmony_ci			break;
7368c2ecf20Sopenharmony_ci	}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/* If no exact match, look for norm which contains this one */
7398c2ecf20Sopenharmony_ci	if (i == TVNORMS) {
7408c2ecf20Sopenharmony_ci		for (i = 0; i < TVNORMS; i++)
7418c2ecf20Sopenharmony_ci			if (id & tvnorms[i].id)
7428c2ecf20Sopenharmony_ci				break;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci	/* If still not matched, give up */
7458c2ecf20Sopenharmony_ci	if (i == TVNORMS)
7468c2ecf20Sopenharmony_ci		return -EINVAL;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	set_tvnorm(dev, &tvnorms[i]);	/* do the actual setting */
7498c2ecf20Sopenharmony_ci	return 0;
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_cistatic int tw68_g_std(struct file *file, void *priv, v4l2_std_id *id)
7538c2ecf20Sopenharmony_ci{
7548c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	*id = dev->tvnorm->id;
7578c2ecf20Sopenharmony_ci	return 0;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic int tw68_enum_fmt_vid_cap(struct file *file, void  *priv,
7618c2ecf20Sopenharmony_ci					struct v4l2_fmtdesc *f)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	if (f->index >= FORMATS)
7648c2ecf20Sopenharmony_ci		return -EINVAL;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	f->pixelformat = formats[f->index].fourcc;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	return 0;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci/*
7728c2ecf20Sopenharmony_ci * Used strictly for internal development and debugging, this routine
7738c2ecf20Sopenharmony_ci * prints out the current register contents for the tw68xx device.
7748c2ecf20Sopenharmony_ci */
7758c2ecf20Sopenharmony_cistatic void tw68_dump_regs(struct tw68_dev *dev)
7768c2ecf20Sopenharmony_ci{
7778c2ecf20Sopenharmony_ci	unsigned char line[80];
7788c2ecf20Sopenharmony_ci	int i, j, k;
7798c2ecf20Sopenharmony_ci	unsigned char *cptr;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	pr_info("Full dump of TW68 registers:\n");
7828c2ecf20Sopenharmony_ci	/* First we do the PCI regs, 8 4-byte regs per line */
7838c2ecf20Sopenharmony_ci	for (i = 0; i < 0x100; i += 32) {
7848c2ecf20Sopenharmony_ci		cptr = line;
7858c2ecf20Sopenharmony_ci		cptr += sprintf(cptr, "%03x  ", i);
7868c2ecf20Sopenharmony_ci		/* j steps through the next 4 words */
7878c2ecf20Sopenharmony_ci		for (j = i; j < i + 16; j += 4)
7888c2ecf20Sopenharmony_ci			cptr += sprintf(cptr, "%08x ", tw_readl(j));
7898c2ecf20Sopenharmony_ci		*cptr++ = ' ';
7908c2ecf20Sopenharmony_ci		for (; j < i + 32; j += 4)
7918c2ecf20Sopenharmony_ci			cptr += sprintf(cptr, "%08x ", tw_readl(j));
7928c2ecf20Sopenharmony_ci		*cptr++ = '\n';
7938c2ecf20Sopenharmony_ci		*cptr = 0;
7948c2ecf20Sopenharmony_ci		pr_info("%s", line);
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci	/* Next the control regs, which are single-byte, address mod 4 */
7978c2ecf20Sopenharmony_ci	while (i < 0x400) {
7988c2ecf20Sopenharmony_ci		cptr = line;
7998c2ecf20Sopenharmony_ci		cptr += sprintf(cptr, "%03x ", i);
8008c2ecf20Sopenharmony_ci		/* Print out 4 groups of 4 bytes */
8018c2ecf20Sopenharmony_ci		for (j = 0; j < 4; j++) {
8028c2ecf20Sopenharmony_ci			for (k = 0; k < 4; k++) {
8038c2ecf20Sopenharmony_ci				cptr += sprintf(cptr, "%02x ",
8048c2ecf20Sopenharmony_ci					tw_readb(i));
8058c2ecf20Sopenharmony_ci				i += 4;
8068c2ecf20Sopenharmony_ci			}
8078c2ecf20Sopenharmony_ci			*cptr++ = ' ';
8088c2ecf20Sopenharmony_ci		}
8098c2ecf20Sopenharmony_ci		*cptr++ = '\n';
8108c2ecf20Sopenharmony_ci		*cptr = 0;
8118c2ecf20Sopenharmony_ci		pr_info("%s", line);
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic int vidioc_log_status(struct file *file, void *priv)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	tw68_dump_regs(dev);
8208c2ecf20Sopenharmony_ci	return v4l2_ctrl_log_status(file, priv);
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
8248c2ecf20Sopenharmony_cistatic int vidioc_g_register(struct file *file, void *priv,
8258c2ecf20Sopenharmony_ci			      struct v4l2_dbg_register *reg)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	if (reg->size == 1)
8308c2ecf20Sopenharmony_ci		reg->val = tw_readb(reg->reg);
8318c2ecf20Sopenharmony_ci	else
8328c2ecf20Sopenharmony_ci		reg->val = tw_readl(reg->reg);
8338c2ecf20Sopenharmony_ci	return 0;
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cistatic int vidioc_s_register(struct file *file, void *priv,
8378c2ecf20Sopenharmony_ci				const struct v4l2_dbg_register *reg)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	struct tw68_dev *dev = video_drvdata(file);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	if (reg->size == 1)
8428c2ecf20Sopenharmony_ci		tw_writeb(reg->reg, reg->val);
8438c2ecf20Sopenharmony_ci	else
8448c2ecf20Sopenharmony_ci		tw_writel(reg->reg & 0xffff, reg->val);
8458c2ecf20Sopenharmony_ci	return 0;
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci#endif
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops tw68_ctrl_ops = {
8508c2ecf20Sopenharmony_ci	.s_ctrl = tw68_s_ctrl,
8518c2ecf20Sopenharmony_ci};
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations video_fops = {
8548c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
8558c2ecf20Sopenharmony_ci	.open			= v4l2_fh_open,
8568c2ecf20Sopenharmony_ci	.release		= vb2_fop_release,
8578c2ecf20Sopenharmony_ci	.read			= vb2_fop_read,
8588c2ecf20Sopenharmony_ci	.poll			= vb2_fop_poll,
8598c2ecf20Sopenharmony_ci	.mmap			= vb2_fop_mmap,
8608c2ecf20Sopenharmony_ci	.unlocked_ioctl		= video_ioctl2,
8618c2ecf20Sopenharmony_ci};
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops video_ioctl_ops = {
8648c2ecf20Sopenharmony_ci	.vidioc_querycap		= tw68_querycap,
8658c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= tw68_enum_fmt_vid_cap,
8668c2ecf20Sopenharmony_ci	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
8678c2ecf20Sopenharmony_ci	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
8688c2ecf20Sopenharmony_ci	.vidioc_querybuf		= vb2_ioctl_querybuf,
8698c2ecf20Sopenharmony_ci	.vidioc_qbuf			= vb2_ioctl_qbuf,
8708c2ecf20Sopenharmony_ci	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
8718c2ecf20Sopenharmony_ci	.vidioc_s_std			= tw68_s_std,
8728c2ecf20Sopenharmony_ci	.vidioc_g_std			= tw68_g_std,
8738c2ecf20Sopenharmony_ci	.vidioc_enum_input		= tw68_enum_input,
8748c2ecf20Sopenharmony_ci	.vidioc_g_input			= tw68_g_input,
8758c2ecf20Sopenharmony_ci	.vidioc_s_input			= tw68_s_input,
8768c2ecf20Sopenharmony_ci	.vidioc_streamon		= vb2_ioctl_streamon,
8778c2ecf20Sopenharmony_ci	.vidioc_streamoff		= vb2_ioctl_streamoff,
8788c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap		= tw68_g_fmt_vid_cap,
8798c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap		= tw68_try_fmt_vid_cap,
8808c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap		= tw68_s_fmt_vid_cap,
8818c2ecf20Sopenharmony_ci	.vidioc_log_status		= vidioc_log_status,
8828c2ecf20Sopenharmony_ci	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
8838c2ecf20Sopenharmony_ci	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
8848c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
8858c2ecf20Sopenharmony_ci	.vidioc_g_register              = vidioc_g_register,
8868c2ecf20Sopenharmony_ci	.vidioc_s_register              = vidioc_s_register,
8878c2ecf20Sopenharmony_ci#endif
8888c2ecf20Sopenharmony_ci};
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_cistatic const struct video_device tw68_video_template = {
8918c2ecf20Sopenharmony_ci	.name			= "tw68_video",
8928c2ecf20Sopenharmony_ci	.fops			= &video_fops,
8938c2ecf20Sopenharmony_ci	.ioctl_ops		= &video_ioctl_ops,
8948c2ecf20Sopenharmony_ci	.release		= video_device_release_empty,
8958c2ecf20Sopenharmony_ci	.tvnorms		= TW68_NORMS,
8968c2ecf20Sopenharmony_ci	.device_caps		= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
8978c2ecf20Sopenharmony_ci				  V4L2_CAP_STREAMING,
8988c2ecf20Sopenharmony_ci};
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
9018c2ecf20Sopenharmony_ci/* exported stuff                                                     */
9028c2ecf20Sopenharmony_civoid tw68_set_tvnorm_hw(struct tw68_dev *dev)
9038c2ecf20Sopenharmony_ci{
9048c2ecf20Sopenharmony_ci	tw_andorb(TW68_SDT, 0x07, dev->tvnorm->format);
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ciint tw68_video_init1(struct tw68_dev *dev)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler *hdl = &dev->hdl;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(hdl, 6);
9128c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
9138c2ecf20Sopenharmony_ci			V4L2_CID_BRIGHTNESS, -128, 127, 1, 20);
9148c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
9158c2ecf20Sopenharmony_ci			V4L2_CID_CONTRAST, 0, 255, 1, 100);
9168c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
9178c2ecf20Sopenharmony_ci			V4L2_CID_SATURATION, 0, 255, 1, 128);
9188c2ecf20Sopenharmony_ci	/* NTSC only */
9198c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
9208c2ecf20Sopenharmony_ci			V4L2_CID_HUE, -128, 127, 1, 0);
9218c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
9228c2ecf20Sopenharmony_ci			V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
9238c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
9248c2ecf20Sopenharmony_ci			V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
9258c2ecf20Sopenharmony_ci	if (hdl->error) {
9268c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_free(hdl);
9278c2ecf20Sopenharmony_ci		return hdl->error;
9288c2ecf20Sopenharmony_ci	}
9298c2ecf20Sopenharmony_ci	dev->v4l2_dev.ctrl_handler = hdl;
9308c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_setup(hdl);
9318c2ecf20Sopenharmony_ci	return 0;
9328c2ecf20Sopenharmony_ci}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ciint tw68_video_init2(struct tw68_dev *dev, int video_nr)
9358c2ecf20Sopenharmony_ci{
9368c2ecf20Sopenharmony_ci	int ret;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	set_tvnorm(dev, &tvnorms[0]);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	dev->fmt      = format_by_fourcc(V4L2_PIX_FMT_BGR24);
9418c2ecf20Sopenharmony_ci	dev->width    = 720;
9428c2ecf20Sopenharmony_ci	dev->height   = 576;
9438c2ecf20Sopenharmony_ci	dev->field    = V4L2_FIELD_INTERLACED;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dev->active);
9468c2ecf20Sopenharmony_ci	dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
9478c2ecf20Sopenharmony_ci	dev->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
9488c2ecf20Sopenharmony_ci	dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | VB2_DMABUF;
9498c2ecf20Sopenharmony_ci	dev->vidq.ops = &tw68_video_qops;
9508c2ecf20Sopenharmony_ci	dev->vidq.mem_ops = &vb2_dma_sg_memops;
9518c2ecf20Sopenharmony_ci	dev->vidq.drv_priv = dev;
9528c2ecf20Sopenharmony_ci	dev->vidq.gfp_flags = __GFP_DMA32 | __GFP_KSWAPD_RECLAIM;
9538c2ecf20Sopenharmony_ci	dev->vidq.buf_struct_size = sizeof(struct tw68_buf);
9548c2ecf20Sopenharmony_ci	dev->vidq.lock = &dev->lock;
9558c2ecf20Sopenharmony_ci	dev->vidq.min_buffers_needed = 2;
9568c2ecf20Sopenharmony_ci	dev->vidq.dev = &dev->pci->dev;
9578c2ecf20Sopenharmony_ci	ret = vb2_queue_init(&dev->vidq);
9588c2ecf20Sopenharmony_ci	if (ret)
9598c2ecf20Sopenharmony_ci		return ret;
9608c2ecf20Sopenharmony_ci	dev->vdev = tw68_video_template;
9618c2ecf20Sopenharmony_ci	dev->vdev.v4l2_dev = &dev->v4l2_dev;
9628c2ecf20Sopenharmony_ci	dev->vdev.lock = &dev->lock;
9638c2ecf20Sopenharmony_ci	dev->vdev.queue = &dev->vidq;
9648c2ecf20Sopenharmony_ci	video_set_drvdata(&dev->vdev, dev);
9658c2ecf20Sopenharmony_ci	return video_register_device(&dev->vdev, VFL_TYPE_VIDEO, video_nr);
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci/*
9698c2ecf20Sopenharmony_ci * tw68_irq_video_done
9708c2ecf20Sopenharmony_ci */
9718c2ecf20Sopenharmony_civoid tw68_irq_video_done(struct tw68_dev *dev, unsigned long status)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	__u32 reg;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	/* reset interrupts handled by this routine */
9768c2ecf20Sopenharmony_ci	tw_writel(TW68_INTSTAT, status);
9778c2ecf20Sopenharmony_ci	/*
9788c2ecf20Sopenharmony_ci	 * Check most likely first
9798c2ecf20Sopenharmony_ci	 *
9808c2ecf20Sopenharmony_ci	 * DMAPI shows we have reached the end of the risc code
9818c2ecf20Sopenharmony_ci	 * for the current buffer.
9828c2ecf20Sopenharmony_ci	 */
9838c2ecf20Sopenharmony_ci	if (status & TW68_DMAPI) {
9848c2ecf20Sopenharmony_ci		struct tw68_buf *buf;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci		spin_lock(&dev->slock);
9878c2ecf20Sopenharmony_ci		buf = list_entry(dev->active.next, struct tw68_buf, list);
9888c2ecf20Sopenharmony_ci		list_del(&buf->list);
9898c2ecf20Sopenharmony_ci		spin_unlock(&dev->slock);
9908c2ecf20Sopenharmony_ci		buf->vb.vb2_buf.timestamp = ktime_get_ns();
9918c2ecf20Sopenharmony_ci		buf->vb.field = dev->field;
9928c2ecf20Sopenharmony_ci		buf->vb.sequence = dev->seqnr++;
9938c2ecf20Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
9948c2ecf20Sopenharmony_ci		status &= ~(TW68_DMAPI);
9958c2ecf20Sopenharmony_ci		if (0 == status)
9968c2ecf20Sopenharmony_ci			return;
9978c2ecf20Sopenharmony_ci	}
9988c2ecf20Sopenharmony_ci	if (status & (TW68_VLOCK | TW68_HLOCK))
9998c2ecf20Sopenharmony_ci		dev_dbg(&dev->pci->dev, "Lost sync\n");
10008c2ecf20Sopenharmony_ci	if (status & TW68_PABORT)
10018c2ecf20Sopenharmony_ci		dev_err(&dev->pci->dev, "PABORT interrupt\n");
10028c2ecf20Sopenharmony_ci	if (status & TW68_DMAPERR)
10038c2ecf20Sopenharmony_ci		dev_err(&dev->pci->dev, "DMAPERR interrupt\n");
10048c2ecf20Sopenharmony_ci	/*
10058c2ecf20Sopenharmony_ci	 * On TW6800, FDMIS is apparently generated if video input is switched
10068c2ecf20Sopenharmony_ci	 * during operation.  Therefore, it is not enabled for that chip.
10078c2ecf20Sopenharmony_ci	 */
10088c2ecf20Sopenharmony_ci	if (status & TW68_FDMIS)
10098c2ecf20Sopenharmony_ci		dev_dbg(&dev->pci->dev, "FDMIS interrupt\n");
10108c2ecf20Sopenharmony_ci	if (status & TW68_FFOF) {
10118c2ecf20Sopenharmony_ci		/* probably a logic error */
10128c2ecf20Sopenharmony_ci		reg = tw_readl(TW68_DMAC) & TW68_FIFO_EN;
10138c2ecf20Sopenharmony_ci		tw_clearl(TW68_DMAC, TW68_FIFO_EN);
10148c2ecf20Sopenharmony_ci		dev_dbg(&dev->pci->dev, "FFOF interrupt\n");
10158c2ecf20Sopenharmony_ci		tw_setl(TW68_DMAC, reg);
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci	if (status & TW68_FFERR)
10188c2ecf20Sopenharmony_ci		dev_dbg(&dev->pci->dev, "FFERR interrupt\n");
10198c2ecf20Sopenharmony_ci}
1020