162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Vidtv serves as a reference DVB driver and helps validate the existing APIs
462306a36Sopenharmony_ci * in the media subsystem. It can also aid developers working on userspace
562306a36Sopenharmony_ci * applications.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This file contains the logic to translate the ES data for one access unit
862306a36Sopenharmony_ci * from an encoder into MPEG TS packets. It does so by first encapsulating it
962306a36Sopenharmony_ci * with a PES header and then splitting it into TS packets.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Copyright (C) 2020 Daniel W. S. Almeida
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/types.h>
1762306a36Sopenharmony_ci#include <linux/printk.h>
1862306a36Sopenharmony_ci#include <linux/ratelimit.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "vidtv_pes.h"
2162306a36Sopenharmony_ci#include "vidtv_common.h"
2262306a36Sopenharmony_ci#include "vidtv_encoder.h"
2362306a36Sopenharmony_ci#include "vidtv_ts.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define PRIVATE_STREAM_1_ID 0xbd /* private_stream_1. See SMPTE 302M-2007 p.6 */
2662306a36Sopenharmony_ci#define PES_HEADER_MAX_STUFFING_BYTES 32
2762306a36Sopenharmony_ci#define PES_TS_HEADER_MAX_STUFFING_BYTES 182
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic u32 vidtv_pes_op_get_len(bool send_pts, bool send_dts)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	u32 len = 0;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	/* the flags must always be sent */
3462306a36Sopenharmony_ci	len += sizeof(struct vidtv_pes_optional);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	/* From all optionals, we might send these for now */
3762306a36Sopenharmony_ci	if (send_pts && send_dts)
3862306a36Sopenharmony_ci		len += sizeof(struct vidtv_pes_optional_pts_dts);
3962306a36Sopenharmony_ci	else if (send_pts)
4062306a36Sopenharmony_ci		len += sizeof(struct vidtv_pes_optional_pts);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return len;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define SIZE_PCR (6 + sizeof(struct vidtv_mpeg_ts_adaption))
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic u32 vidtv_pes_h_get_len(bool send_pts, bool send_dts)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	u32 len = 0;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* PES header length notwithstanding stuffing bytes */
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	len += sizeof(struct vidtv_mpeg_pes);
5462306a36Sopenharmony_ci	len += vidtv_pes_op_get_len(send_pts, send_dts);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return len;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic u32 vidtv_pes_write_header_stuffing(struct pes_header_write_args *args)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	/*
6262306a36Sopenharmony_ci	 * This is a fixed 8-bit value equal to '0xFF' that can be inserted
6362306a36Sopenharmony_ci	 * by the encoder, for example to meet the requirements of the channel.
6462306a36Sopenharmony_ci	 * It is discarded by the decoder. No more than 32 stuffing bytes shall
6562306a36Sopenharmony_ci	 * be present in one PES packet header.
6662306a36Sopenharmony_ci	 */
6762306a36Sopenharmony_ci	if (args->n_pes_h_s_bytes > PES_HEADER_MAX_STUFFING_BYTES) {
6862306a36Sopenharmony_ci		pr_warn_ratelimited("More than %d stuffing bytes in PES packet header\n",
6962306a36Sopenharmony_ci				    PES_HEADER_MAX_STUFFING_BYTES);
7062306a36Sopenharmony_ci		args->n_pes_h_s_bytes = PES_HEADER_MAX_STUFFING_BYTES;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return vidtv_memset(args->dest_buf,
7462306a36Sopenharmony_ci			    args->dest_offset,
7562306a36Sopenharmony_ci			    args->dest_buf_sz,
7662306a36Sopenharmony_ci			    TS_FILL_BYTE,
7762306a36Sopenharmony_ci			    args->n_pes_h_s_bytes);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic u32 vidtv_pes_write_pts_dts(struct pes_header_write_args *args)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	u32 nbytes = 0;  /* the number of bytes written by this function */
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	struct vidtv_pes_optional_pts pts = {};
8562306a36Sopenharmony_ci	struct vidtv_pes_optional_pts_dts pts_dts = {};
8662306a36Sopenharmony_ci	void *op = NULL;
8762306a36Sopenharmony_ci	size_t op_sz = 0;
8862306a36Sopenharmony_ci	u64 mask1;
8962306a36Sopenharmony_ci	u64 mask2;
9062306a36Sopenharmony_ci	u64 mask3;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (!args->send_pts && args->send_dts)
9362306a36Sopenharmony_ci		return 0;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	mask1 = GENMASK_ULL(32, 30);
9662306a36Sopenharmony_ci	mask2 = GENMASK_ULL(29, 15);
9762306a36Sopenharmony_ci	mask3 = GENMASK_ULL(14, 0);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* see ISO/IEC 13818-1 : 2000 p. 32 */
10062306a36Sopenharmony_ci	if (args->send_pts && args->send_dts) {
10162306a36Sopenharmony_ci		pts_dts.pts1 = (0x3 << 4) | ((args->pts & mask1) >> 29) | 0x1;
10262306a36Sopenharmony_ci		pts_dts.pts2 = cpu_to_be16(((args->pts & mask2) >> 14) | 0x1);
10362306a36Sopenharmony_ci		pts_dts.pts3 = cpu_to_be16(((args->pts & mask3) << 1) | 0x1);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		pts_dts.dts1 = (0x1 << 4) | ((args->dts & mask1) >> 29) | 0x1;
10662306a36Sopenharmony_ci		pts_dts.dts2 = cpu_to_be16(((args->dts & mask2) >> 14) | 0x1);
10762306a36Sopenharmony_ci		pts_dts.dts3 = cpu_to_be16(((args->dts & mask3) << 1) | 0x1);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		op = &pts_dts;
11062306a36Sopenharmony_ci		op_sz = sizeof(pts_dts);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	} else if (args->send_pts) {
11362306a36Sopenharmony_ci		pts.pts1 = (0x1 << 5) | ((args->pts & mask1) >> 29) | 0x1;
11462306a36Sopenharmony_ci		pts.pts2 = cpu_to_be16(((args->pts & mask2) >> 14) | 0x1);
11562306a36Sopenharmony_ci		pts.pts3 = cpu_to_be16(((args->pts & mask3) << 1) | 0x1);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		op = &pts;
11862306a36Sopenharmony_ci		op_sz = sizeof(pts);
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* copy PTS/DTS optional */
12262306a36Sopenharmony_ci	nbytes += vidtv_memcpy(args->dest_buf,
12362306a36Sopenharmony_ci			       args->dest_offset + nbytes,
12462306a36Sopenharmony_ci			       args->dest_buf_sz,
12562306a36Sopenharmony_ci			       op,
12662306a36Sopenharmony_ci			       op_sz);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return nbytes;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic u32 vidtv_pes_write_h(struct pes_header_write_args *args)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	u32 nbytes = 0;  /* the number of bytes written by this function */
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	struct vidtv_mpeg_pes pes_header          = {};
13662306a36Sopenharmony_ci	struct vidtv_pes_optional pes_optional    = {};
13762306a36Sopenharmony_ci	struct pes_header_write_args pts_dts_args;
13862306a36Sopenharmony_ci	u32 stream_id = (args->encoder_id == S302M) ? PRIVATE_STREAM_1_ID : args->stream_id;
13962306a36Sopenharmony_ci	u16 pes_opt_bitfield = 0x01 << 15;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	pes_header.bitfield = cpu_to_be32((PES_START_CODE_PREFIX << 8) | stream_id);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	pes_header.length = cpu_to_be16(vidtv_pes_op_get_len(args->send_pts,
14462306a36Sopenharmony_ci							     args->send_dts) +
14562306a36Sopenharmony_ci							     args->access_unit_len);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (args->send_pts && args->send_dts)
14862306a36Sopenharmony_ci		pes_opt_bitfield |= (0x3 << 6);
14962306a36Sopenharmony_ci	else if (args->send_pts)
15062306a36Sopenharmony_ci		pes_opt_bitfield |= (0x1 << 7);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	pes_optional.bitfield = cpu_to_be16(pes_opt_bitfield);
15362306a36Sopenharmony_ci	pes_optional.length = vidtv_pes_op_get_len(args->send_pts, args->send_dts) +
15462306a36Sopenharmony_ci			      args->n_pes_h_s_bytes -
15562306a36Sopenharmony_ci			      sizeof(struct vidtv_pes_optional);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* copy header */
15862306a36Sopenharmony_ci	nbytes += vidtv_memcpy(args->dest_buf,
15962306a36Sopenharmony_ci			       args->dest_offset + nbytes,
16062306a36Sopenharmony_ci			       args->dest_buf_sz,
16162306a36Sopenharmony_ci			       &pes_header,
16262306a36Sopenharmony_ci			       sizeof(pes_header));
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* copy optional header bits */
16562306a36Sopenharmony_ci	nbytes += vidtv_memcpy(args->dest_buf,
16662306a36Sopenharmony_ci			       args->dest_offset + nbytes,
16762306a36Sopenharmony_ci			       args->dest_buf_sz,
16862306a36Sopenharmony_ci			       &pes_optional,
16962306a36Sopenharmony_ci			       sizeof(pes_optional));
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* copy the timing information */
17262306a36Sopenharmony_ci	pts_dts_args = *args;
17362306a36Sopenharmony_ci	pts_dts_args.dest_offset = args->dest_offset + nbytes;
17462306a36Sopenharmony_ci	nbytes += vidtv_pes_write_pts_dts(&pts_dts_args);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* write any PES header stuffing */
17762306a36Sopenharmony_ci	nbytes += vidtv_pes_write_header_stuffing(args);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return nbytes;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic u32 vidtv_pes_write_pcr_bits(u8 *to, u32 to_offset, u64 pcr)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	/* Exact same from ffmpeg. PCR is a counter driven by a 27Mhz clock */
18562306a36Sopenharmony_ci	u64 div;
18662306a36Sopenharmony_ci	u64 rem;
18762306a36Sopenharmony_ci	u8 *buf = to + to_offset;
18862306a36Sopenharmony_ci	u64 pcr_low;
18962306a36Sopenharmony_ci	u64 pcr_high;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	div = div64_u64_rem(pcr, 300, &rem);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	pcr_low = rem; /* pcr_low = pcr % 300 */
19462306a36Sopenharmony_ci	pcr_high = div; /* pcr_high = pcr / 300 */
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	*buf++ = pcr_high >> 25;
19762306a36Sopenharmony_ci	*buf++ = pcr_high >> 17;
19862306a36Sopenharmony_ci	*buf++ = pcr_high >>  9;
19962306a36Sopenharmony_ci	*buf++ = pcr_high >>  1;
20062306a36Sopenharmony_ci	*buf++ = pcr_high <<  7 | pcr_low >> 8 | 0x7e;
20162306a36Sopenharmony_ci	*buf++ = pcr_low;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return 6;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic u32 vidtv_pes_write_stuffing(struct pes_ts_header_write_args *args,
20762306a36Sopenharmony_ci				    u32 dest_offset, bool need_pcr,
20862306a36Sopenharmony_ci				    u64 *last_pcr)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct vidtv_mpeg_ts_adaption ts_adap = {};
21162306a36Sopenharmony_ci	int stuff_nbytes;
21262306a36Sopenharmony_ci	u32 nbytes = 0;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (!args->n_stuffing_bytes)
21562306a36Sopenharmony_ci		return 0;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	ts_adap.random_access = 1;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* length _immediately_ following 'adaptation_field_length' */
22062306a36Sopenharmony_ci	if (need_pcr) {
22162306a36Sopenharmony_ci		ts_adap.PCR = 1;
22262306a36Sopenharmony_ci		ts_adap.length = SIZE_PCR;
22362306a36Sopenharmony_ci	} else {
22462306a36Sopenharmony_ci		ts_adap.length = sizeof(ts_adap);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	stuff_nbytes = args->n_stuffing_bytes - ts_adap.length;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	ts_adap.length -= sizeof(ts_adap.length);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (unlikely(stuff_nbytes < 0))
23162306a36Sopenharmony_ci		stuff_nbytes = 0;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	ts_adap.length += stuff_nbytes;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* write the adap after the TS header */
23662306a36Sopenharmony_ci	nbytes += vidtv_memcpy(args->dest_buf,
23762306a36Sopenharmony_ci			       dest_offset + nbytes,
23862306a36Sopenharmony_ci			       args->dest_buf_sz,
23962306a36Sopenharmony_ci			       &ts_adap,
24062306a36Sopenharmony_ci			       sizeof(ts_adap));
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* write the optional PCR */
24362306a36Sopenharmony_ci	if (need_pcr) {
24462306a36Sopenharmony_ci		nbytes += vidtv_pes_write_pcr_bits(args->dest_buf,
24562306a36Sopenharmony_ci						   dest_offset + nbytes,
24662306a36Sopenharmony_ci						   args->pcr);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		*last_pcr = args->pcr;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* write the stuffing bytes, if are there anything left */
25262306a36Sopenharmony_ci	if (stuff_nbytes)
25362306a36Sopenharmony_ci		nbytes += vidtv_memset(args->dest_buf,
25462306a36Sopenharmony_ci				       dest_offset + nbytes,
25562306a36Sopenharmony_ci				       args->dest_buf_sz,
25662306a36Sopenharmony_ci				       TS_FILL_BYTE,
25762306a36Sopenharmony_ci				       stuff_nbytes);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/*
26062306a36Sopenharmony_ci	 * The n_stuffing_bytes contain a pre-calculated value of
26162306a36Sopenharmony_ci	 * the amount of data that this function would read, made from
26262306a36Sopenharmony_ci	 * vidtv_pes_h_get_len(). If something went wrong, print a warning
26362306a36Sopenharmony_ci	 */
26462306a36Sopenharmony_ci	if (nbytes != args->n_stuffing_bytes)
26562306a36Sopenharmony_ci		pr_warn_ratelimited("write size was %d, expected %d\n",
26662306a36Sopenharmony_ci				    nbytes, args->n_stuffing_bytes);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return nbytes;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic u32 vidtv_pes_write_ts_h(struct pes_ts_header_write_args args,
27262306a36Sopenharmony_ci				bool need_pcr, u64 *last_pcr)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	/* number of bytes written by this function */
27562306a36Sopenharmony_ci	u32 nbytes = 0;
27662306a36Sopenharmony_ci	struct vidtv_mpeg_ts ts_header = {};
27762306a36Sopenharmony_ci	u16 payload_start = !args.wrote_pes_header;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	ts_header.sync_byte        = TS_SYNC_BYTE;
28062306a36Sopenharmony_ci	ts_header.bitfield         = cpu_to_be16((payload_start << 14) | args.pid);
28162306a36Sopenharmony_ci	ts_header.scrambling       = 0;
28262306a36Sopenharmony_ci	ts_header.adaptation_field = (args.n_stuffing_bytes) > 0;
28362306a36Sopenharmony_ci	ts_header.payload          = (args.n_stuffing_bytes) < PES_TS_HEADER_MAX_STUFFING_BYTES;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	ts_header.continuity_counter = *args.continuity_counter;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	vidtv_ts_inc_cc(args.continuity_counter);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* write the TS header */
29062306a36Sopenharmony_ci	nbytes += vidtv_memcpy(args.dest_buf,
29162306a36Sopenharmony_ci			       args.dest_offset + nbytes,
29262306a36Sopenharmony_ci			       args.dest_buf_sz,
29362306a36Sopenharmony_ci			       &ts_header,
29462306a36Sopenharmony_ci			       sizeof(ts_header));
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* write stuffing, if any */
29762306a36Sopenharmony_ci	nbytes += vidtv_pes_write_stuffing(&args, args.dest_offset + nbytes,
29862306a36Sopenharmony_ci					   need_pcr, last_pcr);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return nbytes;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ciu32 vidtv_pes_write_into(struct pes_write_args *args)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	u32 unaligned_bytes = (args->dest_offset % TS_PACKET_LEN);
30662306a36Sopenharmony_ci	struct pes_ts_header_write_args ts_header_args = {
30762306a36Sopenharmony_ci		.dest_buf		= args->dest_buf,
30862306a36Sopenharmony_ci		.dest_buf_sz		= args->dest_buf_sz,
30962306a36Sopenharmony_ci		.pid			= args->pid,
31062306a36Sopenharmony_ci		.pcr			= args->pcr,
31162306a36Sopenharmony_ci		.continuity_counter	= args->continuity_counter,
31262306a36Sopenharmony_ci	};
31362306a36Sopenharmony_ci	struct pes_header_write_args pes_header_args = {
31462306a36Sopenharmony_ci		.dest_buf		= args->dest_buf,
31562306a36Sopenharmony_ci		.dest_buf_sz		= args->dest_buf_sz,
31662306a36Sopenharmony_ci		.encoder_id		= args->encoder_id,
31762306a36Sopenharmony_ci		.send_pts		= args->send_pts,
31862306a36Sopenharmony_ci		.pts			= args->pts,
31962306a36Sopenharmony_ci		.send_dts		= args->send_dts,
32062306a36Sopenharmony_ci		.dts			= args->dts,
32162306a36Sopenharmony_ci		.stream_id		= args->stream_id,
32262306a36Sopenharmony_ci		.n_pes_h_s_bytes	= args->n_pes_h_s_bytes,
32362306a36Sopenharmony_ci		.access_unit_len	= args->access_unit_len,
32462306a36Sopenharmony_ci	};
32562306a36Sopenharmony_ci	u32 remaining_len = args->access_unit_len;
32662306a36Sopenharmony_ci	bool wrote_pes_header = false;
32762306a36Sopenharmony_ci	u64 last_pcr = args->pcr;
32862306a36Sopenharmony_ci	bool need_pcr = true;
32962306a36Sopenharmony_ci	u32 available_space;
33062306a36Sopenharmony_ci	u32 payload_size;
33162306a36Sopenharmony_ci	u32 stuff_bytes;
33262306a36Sopenharmony_ci	u32 nbytes = 0;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (unaligned_bytes) {
33562306a36Sopenharmony_ci		pr_warn_ratelimited("buffer is misaligned, while starting PES\n");
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		/* forcibly align and hope for the best */
33862306a36Sopenharmony_ci		nbytes += vidtv_memset(args->dest_buf,
33962306a36Sopenharmony_ci				       args->dest_offset + nbytes,
34062306a36Sopenharmony_ci				       args->dest_buf_sz,
34162306a36Sopenharmony_ci				       TS_FILL_BYTE,
34262306a36Sopenharmony_ci				       TS_PACKET_LEN - unaligned_bytes);
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	while (remaining_len) {
34662306a36Sopenharmony_ci		available_space = TS_PAYLOAD_LEN;
34762306a36Sopenharmony_ci		/*
34862306a36Sopenharmony_ci		 * The amount of space initially available in the TS packet.
34962306a36Sopenharmony_ci		 * if this is the beginning of the PES packet, take into account
35062306a36Sopenharmony_ci		 * the space needed for the TS header _and_ for the PES header
35162306a36Sopenharmony_ci		 */
35262306a36Sopenharmony_ci		if (!wrote_pes_header)
35362306a36Sopenharmony_ci			available_space -= vidtv_pes_h_get_len(args->send_pts,
35462306a36Sopenharmony_ci							       args->send_dts);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		/*
35762306a36Sopenharmony_ci		 * if the encoder has inserted stuffing bytes in the PES
35862306a36Sopenharmony_ci		 * header, account for them.
35962306a36Sopenharmony_ci		 */
36062306a36Sopenharmony_ci		available_space -= args->n_pes_h_s_bytes;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		/* Take the extra adaptation into account if need to send PCR */
36362306a36Sopenharmony_ci		if (need_pcr) {
36462306a36Sopenharmony_ci			available_space -= SIZE_PCR;
36562306a36Sopenharmony_ci			stuff_bytes = SIZE_PCR;
36662306a36Sopenharmony_ci		} else {
36762306a36Sopenharmony_ci			stuff_bytes = 0;
36862306a36Sopenharmony_ci		}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		/*
37162306a36Sopenharmony_ci		 * how much of the _actual_ payload should be written in this
37262306a36Sopenharmony_ci		 * packet.
37362306a36Sopenharmony_ci		 */
37462306a36Sopenharmony_ci		if (remaining_len >= available_space) {
37562306a36Sopenharmony_ci			payload_size = available_space;
37662306a36Sopenharmony_ci		} else {
37762306a36Sopenharmony_ci			/* Last frame should ensure 188-bytes PS alignment */
37862306a36Sopenharmony_ci			payload_size = remaining_len;
37962306a36Sopenharmony_ci			stuff_bytes += available_space - payload_size;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci			/*
38262306a36Sopenharmony_ci			 * Ensure that the stuff bytes will be within the
38362306a36Sopenharmony_ci			 * allowed range, decrementing the number of payload
38462306a36Sopenharmony_ci			 * bytes to write if needed.
38562306a36Sopenharmony_ci			 */
38662306a36Sopenharmony_ci			if (stuff_bytes > PES_TS_HEADER_MAX_STUFFING_BYTES) {
38762306a36Sopenharmony_ci				u32 tmp = stuff_bytes - PES_TS_HEADER_MAX_STUFFING_BYTES;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci				stuff_bytes = PES_TS_HEADER_MAX_STUFFING_BYTES;
39062306a36Sopenharmony_ci				payload_size -= tmp;
39162306a36Sopenharmony_ci			}
39262306a36Sopenharmony_ci		}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		/* write ts header */
39562306a36Sopenharmony_ci		ts_header_args.dest_offset = args->dest_offset + nbytes;
39662306a36Sopenharmony_ci		ts_header_args.wrote_pes_header	= wrote_pes_header;
39762306a36Sopenharmony_ci		ts_header_args.n_stuffing_bytes	= stuff_bytes;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		nbytes += vidtv_pes_write_ts_h(ts_header_args, need_pcr,
40062306a36Sopenharmony_ci					       &last_pcr);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		need_pcr = false;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		if (!wrote_pes_header) {
40562306a36Sopenharmony_ci			/* write the PES header only once */
40662306a36Sopenharmony_ci			pes_header_args.dest_offset = args->dest_offset +
40762306a36Sopenharmony_ci						      nbytes;
40862306a36Sopenharmony_ci			nbytes += vidtv_pes_write_h(&pes_header_args);
40962306a36Sopenharmony_ci			wrote_pes_header = true;
41062306a36Sopenharmony_ci		}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		/* write as much of the payload as we possibly can */
41362306a36Sopenharmony_ci		nbytes += vidtv_memcpy(args->dest_buf,
41462306a36Sopenharmony_ci				       args->dest_offset + nbytes,
41562306a36Sopenharmony_ci				       args->dest_buf_sz,
41662306a36Sopenharmony_ci				       args->from,
41762306a36Sopenharmony_ci				       payload_size);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		args->from += payload_size;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		remaining_len -= payload_size;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return nbytes;
42562306a36Sopenharmony_ci}
426