162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  cx18 Vertical Blank Interval support functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Derived from ivtv-vbi.c
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "cx18-driver.h"
1162306a36Sopenharmony_ci#include "cx18-vbi.h"
1262306a36Sopenharmony_ci#include "cx18-ioctl.h"
1362306a36Sopenharmony_ci#include "cx18-queue.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * Raster Reference/Protection (RP) bytes, used in Start/End Active
1762306a36Sopenharmony_ci * Video codes emitted from the digitzer in VIP 1.x mode, that flag the start
1862306a36Sopenharmony_ci * of VBI sample or VBI ancillary data regions in the digital ratser line.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_cistatic const u8 raw_vbi_sav_rp[2] = { 0x20, 0x60 };    /* __V_, _FV_ */
2362306a36Sopenharmony_cistatic const u8 sliced_vbi_eav_rp[2] = { 0xb0, 0xf0 }; /* T_VH, TFVH */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	int line = 0;
2862306a36Sopenharmony_ci	int i;
2962306a36Sopenharmony_ci	u32 linemask[2] = { 0, 0 };
3062306a36Sopenharmony_ci	unsigned short size;
3162306a36Sopenharmony_ci	static const u8 mpeg_hdr_data[] = {
3262306a36Sopenharmony_ci		/* MPEG-2 Program Pack */
3362306a36Sopenharmony_ci		0x00, 0x00, 0x01, 0xba,		    /* Prog Pack start code */
3462306a36Sopenharmony_ci		0x44, 0x00, 0x0c, 0x66, 0x24, 0x01, /* SCR, SCR Ext, markers */
3562306a36Sopenharmony_ci		0x01, 0xd1, 0xd3,		    /* Mux Rate, markers */
3662306a36Sopenharmony_ci		0xfa, 0xff, 0xff,		    /* Res, Suff cnt, Stuff */
3762306a36Sopenharmony_ci		/* MPEG-2 Private Stream 1 PES Packet */
3862306a36Sopenharmony_ci		0x00, 0x00, 0x01, 0xbd,		    /* Priv Stream 1 start */
3962306a36Sopenharmony_ci		0x00, 0x1a,			    /* length */
4062306a36Sopenharmony_ci		0x84, 0x80, 0x07,		    /* flags, hdr data len */
4162306a36Sopenharmony_ci		0x21, 0x00, 0x5d, 0x63, 0xa7,	    /* PTS, markers */
4262306a36Sopenharmony_ci		0xff, 0xff			    /* stuffing */
4362306a36Sopenharmony_ci	};
4462306a36Sopenharmony_ci	const int sd = sizeof(mpeg_hdr_data);	/* start of vbi data */
4562306a36Sopenharmony_ci	int idx = cx->vbi.frame % CX18_VBI_FRAMES;
4662306a36Sopenharmony_ci	u8 *dst = &cx->vbi.sliced_mpeg_data[idx][0];
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	for (i = 0; i < lines; i++) {
4962306a36Sopenharmony_ci		struct v4l2_sliced_vbi_data *sdata = cx->vbi.sliced_data + i;
5062306a36Sopenharmony_ci		int f, l;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci		if (sdata->id == 0)
5362306a36Sopenharmony_ci			continue;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		l = sdata->line - 6;
5662306a36Sopenharmony_ci		f = sdata->field;
5762306a36Sopenharmony_ci		if (f)
5862306a36Sopenharmony_ci			l += 18;
5962306a36Sopenharmony_ci		if (l < 32)
6062306a36Sopenharmony_ci			linemask[0] |= (1 << l);
6162306a36Sopenharmony_ci		else
6262306a36Sopenharmony_ci			linemask[1] |= (1 << (l - 32));
6362306a36Sopenharmony_ci		dst[sd + 12 + line * 43] = cx18_service2vbi(sdata->id);
6462306a36Sopenharmony_ci		memcpy(dst + sd + 12 + line * 43 + 1, sdata->data, 42);
6562306a36Sopenharmony_ci		line++;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci	memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
6862306a36Sopenharmony_ci	if (line == 36) {
6962306a36Sopenharmony_ci		/* All lines are used, so there is no space for the linemask
7062306a36Sopenharmony_ci		   (the max size of the VBI data is 36 * 43 + 4 bytes).
7162306a36Sopenharmony_ci		   So in this case we use the magic number 'ITV0'. */
7262306a36Sopenharmony_ci		memcpy(dst + sd, "ITV0", 4);
7362306a36Sopenharmony_ci		memmove(dst + sd + 4, dst + sd + 12, line * 43);
7462306a36Sopenharmony_ci		size = 4 + ((43 * line + 3) & ~3);
7562306a36Sopenharmony_ci	} else {
7662306a36Sopenharmony_ci		memcpy(dst + sd, "itv0", 4);
7762306a36Sopenharmony_ci		cpu_to_le32s(&linemask[0]);
7862306a36Sopenharmony_ci		cpu_to_le32s(&linemask[1]);
7962306a36Sopenharmony_ci		memcpy(dst + sd + 4, &linemask[0], 8);
8062306a36Sopenharmony_ci		size = 12 + ((43 * line + 3) & ~3);
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci	dst[4+16] = (size + 10) >> 8;
8362306a36Sopenharmony_ci	dst[5+16] = (size + 10) & 0xff;
8462306a36Sopenharmony_ci	dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
8562306a36Sopenharmony_ci	dst[10+16] = (pts_stamp >> 22) & 0xff;
8662306a36Sopenharmony_ci	dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
8762306a36Sopenharmony_ci	dst[12+16] = (pts_stamp >> 7) & 0xff;
8862306a36Sopenharmony_ci	dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
8962306a36Sopenharmony_ci	cx->vbi.sliced_mpeg_size[idx] = sd + size;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* Compress raw VBI format, removes leading SAV codes and surplus space
9362306a36Sopenharmony_ci   after the frame.  Returns new compressed size. */
9462306a36Sopenharmony_ci/* FIXME - this function ignores the input size. */
9562306a36Sopenharmony_cistatic u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	u32 line_size = VBI_ACTIVE_SAMPLES;
9862306a36Sopenharmony_ci	u32 lines = cx->vbi.count * 2;
9962306a36Sopenharmony_ci	u8 *q = buf;
10062306a36Sopenharmony_ci	u8 *p;
10162306a36Sopenharmony_ci	int i;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Skip the header */
10462306a36Sopenharmony_ci	buf += hdr_size;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	for (i = 0; i < lines; i++) {
10762306a36Sopenharmony_ci		p = buf + i * line_size;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		/* Look for SAV code */
11062306a36Sopenharmony_ci		if (p[0] != 0xff || p[1] || p[2] ||
11162306a36Sopenharmony_ci		    (p[3] != raw_vbi_sav_rp[0] &&
11262306a36Sopenharmony_ci		     p[3] != raw_vbi_sav_rp[1]))
11362306a36Sopenharmony_ci			break;
11462306a36Sopenharmony_ci		if (i == lines - 1) {
11562306a36Sopenharmony_ci			/* last line is hdr_size bytes short - extrapolate it */
11662306a36Sopenharmony_ci			memcpy(q, p + 4, line_size - 4 - hdr_size);
11762306a36Sopenharmony_ci			q += line_size - 4 - hdr_size;
11862306a36Sopenharmony_ci			p += line_size - hdr_size - 1;
11962306a36Sopenharmony_ci			memset(q, (int) *p, hdr_size);
12062306a36Sopenharmony_ci		} else {
12162306a36Sopenharmony_ci			memcpy(q, p + 4, line_size - 4);
12262306a36Sopenharmony_ci			q += line_size - 4;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	return lines * (line_size - 4);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
12962306a36Sopenharmony_ci			       const u32 hdr_size)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct v4l2_decode_vbi_line vbi;
13262306a36Sopenharmony_ci	int i;
13362306a36Sopenharmony_ci	u32 line = 0;
13462306a36Sopenharmony_ci	u32 line_size = cx->is_60hz ? VBI_HBLANK_SAMPLES_60HZ
13562306a36Sopenharmony_ci				    : VBI_HBLANK_SAMPLES_50HZ;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* find the first valid line */
13862306a36Sopenharmony_ci	for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) {
13962306a36Sopenharmony_ci		if (buf[0] == 0xff && !buf[1] && !buf[2] &&
14062306a36Sopenharmony_ci		    (buf[3] == sliced_vbi_eav_rp[0] ||
14162306a36Sopenharmony_ci		     buf[3] == sliced_vbi_eav_rp[1]))
14262306a36Sopenharmony_ci			break;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/*
14662306a36Sopenharmony_ci	 * The last line is short by hdr_size bytes, but for the remaining
14762306a36Sopenharmony_ci	 * checks against size, we pretend that it is not, by counting the
14862306a36Sopenharmony_ci	 * header bytes we knowingly skipped
14962306a36Sopenharmony_ci	 */
15062306a36Sopenharmony_ci	size -= (i - hdr_size);
15162306a36Sopenharmony_ci	if (size < line_size)
15262306a36Sopenharmony_ci		return line;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	for (i = 0; i < size / line_size; i++) {
15562306a36Sopenharmony_ci		u8 *p = buf + i * line_size;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		/* Look for EAV code  */
15862306a36Sopenharmony_ci		if (p[0] != 0xff || p[1] || p[2] ||
15962306a36Sopenharmony_ci		    (p[3] != sliced_vbi_eav_rp[0] &&
16062306a36Sopenharmony_ci		     p[3] != sliced_vbi_eav_rp[1]))
16162306a36Sopenharmony_ci			continue;
16262306a36Sopenharmony_ci		vbi.p = p + 4;
16362306a36Sopenharmony_ci		v4l2_subdev_call(cx->sd_av, vbi, decode_vbi_line, &vbi);
16462306a36Sopenharmony_ci		if (vbi.type) {
16562306a36Sopenharmony_ci			cx->vbi.sliced_data[line].id = vbi.type;
16662306a36Sopenharmony_ci			cx->vbi.sliced_data[line].field = vbi.is_second_field;
16762306a36Sopenharmony_ci			cx->vbi.sliced_data[line].line = vbi.line;
16862306a36Sopenharmony_ci			memcpy(cx->vbi.sliced_data[line].data, vbi.p, 42);
16962306a36Sopenharmony_ci			line++;
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci	return line;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	/*
17862306a36Sopenharmony_ci	 * The CX23418 provides a 12 byte header in its raw VBI buffers to us:
17962306a36Sopenharmony_ci	 * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp]
18062306a36Sopenharmony_ci	 */
18162306a36Sopenharmony_ci	struct vbi_data_hdr {
18262306a36Sopenharmony_ci		__be32 magic;
18362306a36Sopenharmony_ci		__be32 unknown;
18462306a36Sopenharmony_ci		__be32 pts;
18562306a36Sopenharmony_ci	} *hdr = (struct vbi_data_hdr *) buf->buf;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	u8 *p = (u8 *) buf->buf;
18862306a36Sopenharmony_ci	u32 size = buf->bytesused;
18962306a36Sopenharmony_ci	u32 pts;
19062306a36Sopenharmony_ci	int lines;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/*
19362306a36Sopenharmony_ci	 * The CX23418 sends us data that is 32 bit little-endian swapped,
19462306a36Sopenharmony_ci	 * but we want the raw VBI bytes in the order they were in the raster
19562306a36Sopenharmony_ci	 * line.  This has a side effect of making the header big endian
19662306a36Sopenharmony_ci	 */
19762306a36Sopenharmony_ci	cx18_buf_swap(buf);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* Raw VBI data */
20062306a36Sopenharmony_ci	if (cx18_raw_vbi(cx)) {
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		size = buf->bytesused =
20362306a36Sopenharmony_ci		     compress_raw_buf(cx, p, size, sizeof(struct vbi_data_hdr));
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		/*
20662306a36Sopenharmony_ci		 * Hack needed for compatibility with old VBI software.
20762306a36Sopenharmony_ci		 * Write the frame # at the last 4 bytes of the frame
20862306a36Sopenharmony_ci		 */
20962306a36Sopenharmony_ci		p += size - 4;
21062306a36Sopenharmony_ci		memcpy(p, &cx->vbi.frame, 4);
21162306a36Sopenharmony_ci		cx->vbi.frame++;
21262306a36Sopenharmony_ci		return;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Sliced VBI data with data insertion */
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	pts = (be32_to_cpu(hdr->magic) == 0x3fffffff) ? be32_to_cpu(hdr->pts)
21862306a36Sopenharmony_ci						      : 0;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	lines = compress_sliced_buf(cx, p, size, sizeof(struct vbi_data_hdr));
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* always return at least one empty line */
22362306a36Sopenharmony_ci	if (lines == 0) {
22462306a36Sopenharmony_ci		cx->vbi.sliced_data[0].id = 0;
22562306a36Sopenharmony_ci		cx->vbi.sliced_data[0].line = 0;
22662306a36Sopenharmony_ci		cx->vbi.sliced_data[0].field = 0;
22762306a36Sopenharmony_ci		lines = 1;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci	buf->bytesused = size = lines * sizeof(cx->vbi.sliced_data[0]);
23062306a36Sopenharmony_ci	memcpy(p, &cx->vbi.sliced_data[0], size);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (cx->vbi.insert_mpeg)
23362306a36Sopenharmony_ci		copy_vbi_data(cx, lines, pts);
23462306a36Sopenharmony_ci	cx->vbi.frame++;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_civoid cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
23862306a36Sopenharmony_ci			   int streamtype)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct cx18_buffer *buf;
24162306a36Sopenharmony_ci	u32 orig_used;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
24462306a36Sopenharmony_ci		return;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/*
24762306a36Sopenharmony_ci	 * Big assumption here:
24862306a36Sopenharmony_ci	 * Every buffer hooked to the MDL's buf_list is a complete VBI frame
24962306a36Sopenharmony_ci	 * that ends at the end of the buffer.
25062306a36Sopenharmony_ci	 *
25162306a36Sopenharmony_ci	 * To assume anything else would make the code in this file
25262306a36Sopenharmony_ci	 * more complex, or require extra memcpy()'s to make the
25362306a36Sopenharmony_ci	 * buffers satisfy the above assumption.  It's just simpler to set
25462306a36Sopenharmony_ci	 * up the encoder buffer transfers to make the assumption true.
25562306a36Sopenharmony_ci	 */
25662306a36Sopenharmony_ci	list_for_each_entry(buf, &mdl->buf_list, list) {
25762306a36Sopenharmony_ci		orig_used = buf->bytesused;
25862306a36Sopenharmony_ci		if (orig_used == 0)
25962306a36Sopenharmony_ci			break;
26062306a36Sopenharmony_ci		_cx18_process_vbi_data(cx, buf);
26162306a36Sopenharmony_ci		mdl->bytesused -= (orig_used - buf->bytesused);
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci}
264