18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  cx18 Vertical Blank Interval support functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Derived from ivtv-vbi.c
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "cx18-driver.h"
118c2ecf20Sopenharmony_ci#include "cx18-vbi.h"
128c2ecf20Sopenharmony_ci#include "cx18-ioctl.h"
138c2ecf20Sopenharmony_ci#include "cx18-queue.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * Raster Reference/Protection (RP) bytes, used in Start/End Active
178c2ecf20Sopenharmony_ci * Video codes emitted from the digitzer in VIP 1.x mode, that flag the start
188c2ecf20Sopenharmony_ci * of VBI sample or VBI ancillary data regions in the digital ratser line.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_cistatic const u8 raw_vbi_sav_rp[2] = { 0x20, 0x60 };    /* __V_, _FV_ */
238c2ecf20Sopenharmony_cistatic const u8 sliced_vbi_eav_rp[2] = { 0xb0, 0xf0 }; /* T_VH, TFVH */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	int line = 0;
288c2ecf20Sopenharmony_ci	int i;
298c2ecf20Sopenharmony_ci	u32 linemask[2] = { 0, 0 };
308c2ecf20Sopenharmony_ci	unsigned short size;
318c2ecf20Sopenharmony_ci	static const u8 mpeg_hdr_data[] = {
328c2ecf20Sopenharmony_ci		/* MPEG-2 Program Pack */
338c2ecf20Sopenharmony_ci		0x00, 0x00, 0x01, 0xba,		    /* Prog Pack start code */
348c2ecf20Sopenharmony_ci		0x44, 0x00, 0x0c, 0x66, 0x24, 0x01, /* SCR, SCR Ext, markers */
358c2ecf20Sopenharmony_ci		0x01, 0xd1, 0xd3,		    /* Mux Rate, markers */
368c2ecf20Sopenharmony_ci		0xfa, 0xff, 0xff,		    /* Res, Suff cnt, Stuff */
378c2ecf20Sopenharmony_ci		/* MPEG-2 Private Stream 1 PES Packet */
388c2ecf20Sopenharmony_ci		0x00, 0x00, 0x01, 0xbd,		    /* Priv Stream 1 start */
398c2ecf20Sopenharmony_ci		0x00, 0x1a,			    /* length */
408c2ecf20Sopenharmony_ci		0x84, 0x80, 0x07,		    /* flags, hdr data len */
418c2ecf20Sopenharmony_ci		0x21, 0x00, 0x5d, 0x63, 0xa7,	    /* PTS, markers */
428c2ecf20Sopenharmony_ci		0xff, 0xff			    /* stuffing */
438c2ecf20Sopenharmony_ci	};
448c2ecf20Sopenharmony_ci	const int sd = sizeof(mpeg_hdr_data);	/* start of vbi data */
458c2ecf20Sopenharmony_ci	int idx = cx->vbi.frame % CX18_VBI_FRAMES;
468c2ecf20Sopenharmony_ci	u8 *dst = &cx->vbi.sliced_mpeg_data[idx][0];
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	for (i = 0; i < lines; i++) {
498c2ecf20Sopenharmony_ci		struct v4l2_sliced_vbi_data *sdata = cx->vbi.sliced_data + i;
508c2ecf20Sopenharmony_ci		int f, l;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci		if (sdata->id == 0)
538c2ecf20Sopenharmony_ci			continue;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		l = sdata->line - 6;
568c2ecf20Sopenharmony_ci		f = sdata->field;
578c2ecf20Sopenharmony_ci		if (f)
588c2ecf20Sopenharmony_ci			l += 18;
598c2ecf20Sopenharmony_ci		if (l < 32)
608c2ecf20Sopenharmony_ci			linemask[0] |= (1 << l);
618c2ecf20Sopenharmony_ci		else
628c2ecf20Sopenharmony_ci			linemask[1] |= (1 << (l - 32));
638c2ecf20Sopenharmony_ci		dst[sd + 12 + line * 43] = cx18_service2vbi(sdata->id);
648c2ecf20Sopenharmony_ci		memcpy(dst + sd + 12 + line * 43 + 1, sdata->data, 42);
658c2ecf20Sopenharmony_ci		line++;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci	memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
688c2ecf20Sopenharmony_ci	if (line == 36) {
698c2ecf20Sopenharmony_ci		/* All lines are used, so there is no space for the linemask
708c2ecf20Sopenharmony_ci		   (the max size of the VBI data is 36 * 43 + 4 bytes).
718c2ecf20Sopenharmony_ci		   So in this case we use the magic number 'ITV0'. */
728c2ecf20Sopenharmony_ci		memcpy(dst + sd, "ITV0", 4);
738c2ecf20Sopenharmony_ci		memmove(dst + sd + 4, dst + sd + 12, line * 43);
748c2ecf20Sopenharmony_ci		size = 4 + ((43 * line + 3) & ~3);
758c2ecf20Sopenharmony_ci	} else {
768c2ecf20Sopenharmony_ci		memcpy(dst + sd, "itv0", 4);
778c2ecf20Sopenharmony_ci		cpu_to_le32s(&linemask[0]);
788c2ecf20Sopenharmony_ci		cpu_to_le32s(&linemask[1]);
798c2ecf20Sopenharmony_ci		memcpy(dst + sd + 4, &linemask[0], 8);
808c2ecf20Sopenharmony_ci		size = 12 + ((43 * line + 3) & ~3);
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci	dst[4+16] = (size + 10) >> 8;
838c2ecf20Sopenharmony_ci	dst[5+16] = (size + 10) & 0xff;
848c2ecf20Sopenharmony_ci	dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
858c2ecf20Sopenharmony_ci	dst[10+16] = (pts_stamp >> 22) & 0xff;
868c2ecf20Sopenharmony_ci	dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
878c2ecf20Sopenharmony_ci	dst[12+16] = (pts_stamp >> 7) & 0xff;
888c2ecf20Sopenharmony_ci	dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
898c2ecf20Sopenharmony_ci	cx->vbi.sliced_mpeg_size[idx] = sd + size;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* Compress raw VBI format, removes leading SAV codes and surplus space
938c2ecf20Sopenharmony_ci   after the frame.  Returns new compressed size. */
948c2ecf20Sopenharmony_ci/* FIXME - this function ignores the input size. */
958c2ecf20Sopenharmony_cistatic u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	u32 line_size = VBI_ACTIVE_SAMPLES;
988c2ecf20Sopenharmony_ci	u32 lines = cx->vbi.count * 2;
998c2ecf20Sopenharmony_ci	u8 *q = buf;
1008c2ecf20Sopenharmony_ci	u8 *p;
1018c2ecf20Sopenharmony_ci	int i;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	/* Skip the header */
1048c2ecf20Sopenharmony_ci	buf += hdr_size;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	for (i = 0; i < lines; i++) {
1078c2ecf20Sopenharmony_ci		p = buf + i * line_size;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		/* Look for SAV code */
1108c2ecf20Sopenharmony_ci		if (p[0] != 0xff || p[1] || p[2] ||
1118c2ecf20Sopenharmony_ci		    (p[3] != raw_vbi_sav_rp[0] &&
1128c2ecf20Sopenharmony_ci		     p[3] != raw_vbi_sav_rp[1]))
1138c2ecf20Sopenharmony_ci			break;
1148c2ecf20Sopenharmony_ci		if (i == lines - 1) {
1158c2ecf20Sopenharmony_ci			/* last line is hdr_size bytes short - extrapolate it */
1168c2ecf20Sopenharmony_ci			memcpy(q, p + 4, line_size - 4 - hdr_size);
1178c2ecf20Sopenharmony_ci			q += line_size - 4 - hdr_size;
1188c2ecf20Sopenharmony_ci			p += line_size - hdr_size - 1;
1198c2ecf20Sopenharmony_ci			memset(q, (int) *p, hdr_size);
1208c2ecf20Sopenharmony_ci		} else {
1218c2ecf20Sopenharmony_ci			memcpy(q, p + 4, line_size - 4);
1228c2ecf20Sopenharmony_ci			q += line_size - 4;
1238c2ecf20Sopenharmony_ci		}
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	return lines * (line_size - 4);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
1298c2ecf20Sopenharmony_ci			       const u32 hdr_size)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct v4l2_decode_vbi_line vbi;
1328c2ecf20Sopenharmony_ci	int i;
1338c2ecf20Sopenharmony_ci	u32 line = 0;
1348c2ecf20Sopenharmony_ci	u32 line_size = cx->is_60hz ? VBI_HBLANK_SAMPLES_60HZ
1358c2ecf20Sopenharmony_ci				    : VBI_HBLANK_SAMPLES_50HZ;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* find the first valid line */
1388c2ecf20Sopenharmony_ci	for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) {
1398c2ecf20Sopenharmony_ci		if (buf[0] == 0xff && !buf[1] && !buf[2] &&
1408c2ecf20Sopenharmony_ci		    (buf[3] == sliced_vbi_eav_rp[0] ||
1418c2ecf20Sopenharmony_ci		     buf[3] == sliced_vbi_eav_rp[1]))
1428c2ecf20Sopenharmony_ci			break;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/*
1468c2ecf20Sopenharmony_ci	 * The last line is short by hdr_size bytes, but for the remaining
1478c2ecf20Sopenharmony_ci	 * checks against size, we pretend that it is not, by counting the
1488c2ecf20Sopenharmony_ci	 * header bytes we knowingly skipped
1498c2ecf20Sopenharmony_ci	 */
1508c2ecf20Sopenharmony_ci	size -= (i - hdr_size);
1518c2ecf20Sopenharmony_ci	if (size < line_size)
1528c2ecf20Sopenharmony_ci		return line;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	for (i = 0; i < size / line_size; i++) {
1558c2ecf20Sopenharmony_ci		u8 *p = buf + i * line_size;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		/* Look for EAV code  */
1588c2ecf20Sopenharmony_ci		if (p[0] != 0xff || p[1] || p[2] ||
1598c2ecf20Sopenharmony_ci		    (p[3] != sliced_vbi_eav_rp[0] &&
1608c2ecf20Sopenharmony_ci		     p[3] != sliced_vbi_eav_rp[1]))
1618c2ecf20Sopenharmony_ci			continue;
1628c2ecf20Sopenharmony_ci		vbi.p = p + 4;
1638c2ecf20Sopenharmony_ci		v4l2_subdev_call(cx->sd_av, vbi, decode_vbi_line, &vbi);
1648c2ecf20Sopenharmony_ci		if (vbi.type) {
1658c2ecf20Sopenharmony_ci			cx->vbi.sliced_data[line].id = vbi.type;
1668c2ecf20Sopenharmony_ci			cx->vbi.sliced_data[line].field = vbi.is_second_field;
1678c2ecf20Sopenharmony_ci			cx->vbi.sliced_data[line].line = vbi.line;
1688c2ecf20Sopenharmony_ci			memcpy(cx->vbi.sliced_data[line].data, vbi.p, 42);
1698c2ecf20Sopenharmony_ci			line++;
1708c2ecf20Sopenharmony_ci		}
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	return line;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	/*
1788c2ecf20Sopenharmony_ci	 * The CX23418 provides a 12 byte header in its raw VBI buffers to us:
1798c2ecf20Sopenharmony_ci	 * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp]
1808c2ecf20Sopenharmony_ci	 */
1818c2ecf20Sopenharmony_ci	struct vbi_data_hdr {
1828c2ecf20Sopenharmony_ci		__be32 magic;
1838c2ecf20Sopenharmony_ci		__be32 unknown;
1848c2ecf20Sopenharmony_ci		__be32 pts;
1858c2ecf20Sopenharmony_ci	} *hdr = (struct vbi_data_hdr *) buf->buf;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	u8 *p = (u8 *) buf->buf;
1888c2ecf20Sopenharmony_ci	u32 size = buf->bytesused;
1898c2ecf20Sopenharmony_ci	u32 pts;
1908c2ecf20Sopenharmony_ci	int lines;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/*
1938c2ecf20Sopenharmony_ci	 * The CX23418 sends us data that is 32 bit little-endian swapped,
1948c2ecf20Sopenharmony_ci	 * but we want the raw VBI bytes in the order they were in the raster
1958c2ecf20Sopenharmony_ci	 * line.  This has a side effect of making the header big endian
1968c2ecf20Sopenharmony_ci	 */
1978c2ecf20Sopenharmony_ci	cx18_buf_swap(buf);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/* Raw VBI data */
2008c2ecf20Sopenharmony_ci	if (cx18_raw_vbi(cx)) {
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		size = buf->bytesused =
2038c2ecf20Sopenharmony_ci		     compress_raw_buf(cx, p, size, sizeof(struct vbi_data_hdr));
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		/*
2068c2ecf20Sopenharmony_ci		 * Hack needed for compatibility with old VBI software.
2078c2ecf20Sopenharmony_ci		 * Write the frame # at the last 4 bytes of the frame
2088c2ecf20Sopenharmony_ci		 */
2098c2ecf20Sopenharmony_ci		p += size - 4;
2108c2ecf20Sopenharmony_ci		memcpy(p, &cx->vbi.frame, 4);
2118c2ecf20Sopenharmony_ci		cx->vbi.frame++;
2128c2ecf20Sopenharmony_ci		return;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* Sliced VBI data with data insertion */
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	pts = (be32_to_cpu(hdr->magic) == 0x3fffffff) ? be32_to_cpu(hdr->pts)
2188c2ecf20Sopenharmony_ci						      : 0;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	lines = compress_sliced_buf(cx, p, size, sizeof(struct vbi_data_hdr));
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/* always return at least one empty line */
2238c2ecf20Sopenharmony_ci	if (lines == 0) {
2248c2ecf20Sopenharmony_ci		cx->vbi.sliced_data[0].id = 0;
2258c2ecf20Sopenharmony_ci		cx->vbi.sliced_data[0].line = 0;
2268c2ecf20Sopenharmony_ci		cx->vbi.sliced_data[0].field = 0;
2278c2ecf20Sopenharmony_ci		lines = 1;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci	buf->bytesused = size = lines * sizeof(cx->vbi.sliced_data[0]);
2308c2ecf20Sopenharmony_ci	memcpy(p, &cx->vbi.sliced_data[0], size);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (cx->vbi.insert_mpeg)
2338c2ecf20Sopenharmony_ci		copy_vbi_data(cx, lines, pts);
2348c2ecf20Sopenharmony_ci	cx->vbi.frame++;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_civoid cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
2388c2ecf20Sopenharmony_ci			   int streamtype)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct cx18_buffer *buf;
2418c2ecf20Sopenharmony_ci	u32 orig_used;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
2448c2ecf20Sopenharmony_ci		return;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/*
2478c2ecf20Sopenharmony_ci	 * Big assumption here:
2488c2ecf20Sopenharmony_ci	 * Every buffer hooked to the MDL's buf_list is a complete VBI frame
2498c2ecf20Sopenharmony_ci	 * that ends at the end of the buffer.
2508c2ecf20Sopenharmony_ci	 *
2518c2ecf20Sopenharmony_ci	 * To assume anything else would make the code in this file
2528c2ecf20Sopenharmony_ci	 * more complex, or require extra memcpy()'s to make the
2538c2ecf20Sopenharmony_ci	 * buffers satisfy the above assumption.  It's just simpler to set
2548c2ecf20Sopenharmony_ci	 * up the encoder buffer transfers to make the assumption true.
2558c2ecf20Sopenharmony_ci	 */
2568c2ecf20Sopenharmony_ci	list_for_each_entry(buf, &mdl->buf_list, list) {
2578c2ecf20Sopenharmony_ci		orig_used = buf->bytesused;
2588c2ecf20Sopenharmony_ci		if (orig_used == 0)
2598c2ecf20Sopenharmony_ci			break;
2608c2ecf20Sopenharmony_ci		_cx18_process_vbi_data(cx, buf);
2618c2ecf20Sopenharmony_ci		mdl->bytesused -= (orig_used - buf->bytesused);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci}
264