18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Vidtv serves as a reference DVB driver and helps validate the existing APIs
48c2ecf20Sopenharmony_ci * in the media subsystem. It can also aid developers working on userspace
58c2ecf20Sopenharmony_ci * applications.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file contains the logic to translate the ES data for one access unit
88c2ecf20Sopenharmony_ci * from an encoder into MPEG TS packets. It does so by first encapsulating it
98c2ecf20Sopenharmony_ci * with a PES header and then splitting it into TS packets.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Copyright (C) 2020 Daniel W. S. Almeida
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/types.h>
178c2ecf20Sopenharmony_ci#include <linux/printk.h>
188c2ecf20Sopenharmony_ci#include <linux/ratelimit.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "vidtv_pes.h"
218c2ecf20Sopenharmony_ci#include "vidtv_common.h"
228c2ecf20Sopenharmony_ci#include "vidtv_encoder.h"
238c2ecf20Sopenharmony_ci#include "vidtv_ts.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define PRIVATE_STREAM_1_ID 0xbd /* private_stream_1. See SMPTE 302M-2007 p.6 */
268c2ecf20Sopenharmony_ci#define PES_HEADER_MAX_STUFFING_BYTES 32
278c2ecf20Sopenharmony_ci#define PES_TS_HEADER_MAX_STUFFING_BYTES 182
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic u32 vidtv_pes_op_get_len(bool send_pts, bool send_dts)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	u32 len = 0;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	/* the flags must always be sent */
348c2ecf20Sopenharmony_ci	len += sizeof(struct vidtv_pes_optional);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	/* From all optionals, we might send these for now */
378c2ecf20Sopenharmony_ci	if (send_pts && send_dts)
388c2ecf20Sopenharmony_ci		len += sizeof(struct vidtv_pes_optional_pts_dts);
398c2ecf20Sopenharmony_ci	else if (send_pts)
408c2ecf20Sopenharmony_ci		len += sizeof(struct vidtv_pes_optional_pts);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	return len;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define SIZE_PCR (6 + sizeof(struct vidtv_mpeg_ts_adaption))
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic u32 vidtv_pes_h_get_len(bool send_pts, bool send_dts)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	u32 len = 0;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* PES header length notwithstanding stuffing bytes */
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	len += sizeof(struct vidtv_mpeg_pes);
548c2ecf20Sopenharmony_ci	len += vidtv_pes_op_get_len(send_pts, send_dts);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	return len;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic u32 vidtv_pes_write_header_stuffing(struct pes_header_write_args *args)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	/*
628c2ecf20Sopenharmony_ci	 * This is a fixed 8-bit value equal to '0xFF' that can be inserted
638c2ecf20Sopenharmony_ci	 * by the encoder, for example to meet the requirements of the channel.
648c2ecf20Sopenharmony_ci	 * It is discarded by the decoder. No more than 32 stuffing bytes shall
658c2ecf20Sopenharmony_ci	 * be present in one PES packet header.
668c2ecf20Sopenharmony_ci	 */
678c2ecf20Sopenharmony_ci	if (args->n_pes_h_s_bytes > PES_HEADER_MAX_STUFFING_BYTES) {
688c2ecf20Sopenharmony_ci		pr_warn_ratelimited("More than %d stuffing bytes in PES packet header\n",
698c2ecf20Sopenharmony_ci				    PES_HEADER_MAX_STUFFING_BYTES);
708c2ecf20Sopenharmony_ci		args->n_pes_h_s_bytes = PES_HEADER_MAX_STUFFING_BYTES;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return vidtv_memset(args->dest_buf,
748c2ecf20Sopenharmony_ci			    args->dest_offset,
758c2ecf20Sopenharmony_ci			    args->dest_buf_sz,
768c2ecf20Sopenharmony_ci			    TS_FILL_BYTE,
778c2ecf20Sopenharmony_ci			    args->n_pes_h_s_bytes);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic u32 vidtv_pes_write_pts_dts(struct pes_header_write_args *args)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	u32 nbytes = 0;  /* the number of bytes written by this function */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	struct vidtv_pes_optional_pts pts = {};
858c2ecf20Sopenharmony_ci	struct vidtv_pes_optional_pts_dts pts_dts = {};
868c2ecf20Sopenharmony_ci	void *op = NULL;
878c2ecf20Sopenharmony_ci	size_t op_sz = 0;
888c2ecf20Sopenharmony_ci	u64 mask1;
898c2ecf20Sopenharmony_ci	u64 mask2;
908c2ecf20Sopenharmony_ci	u64 mask3;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (!args->send_pts && args->send_dts)
938c2ecf20Sopenharmony_ci		return 0;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	mask1 = GENMASK_ULL(32, 30);
968c2ecf20Sopenharmony_ci	mask2 = GENMASK_ULL(29, 15);
978c2ecf20Sopenharmony_ci	mask3 = GENMASK_ULL(14, 0);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* see ISO/IEC 13818-1 : 2000 p. 32 */
1008c2ecf20Sopenharmony_ci	if (args->send_pts && args->send_dts) {
1018c2ecf20Sopenharmony_ci		pts_dts.pts1 = (0x3 << 4) | ((args->pts & mask1) >> 29) | 0x1;
1028c2ecf20Sopenharmony_ci		pts_dts.pts2 = cpu_to_be16(((args->pts & mask2) >> 14) | 0x1);
1038c2ecf20Sopenharmony_ci		pts_dts.pts3 = cpu_to_be16(((args->pts & mask3) << 1) | 0x1);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		pts_dts.dts1 = (0x1 << 4) | ((args->dts & mask1) >> 29) | 0x1;
1068c2ecf20Sopenharmony_ci		pts_dts.dts2 = cpu_to_be16(((args->dts & mask2) >> 14) | 0x1);
1078c2ecf20Sopenharmony_ci		pts_dts.dts3 = cpu_to_be16(((args->dts & mask3) << 1) | 0x1);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		op = &pts_dts;
1108c2ecf20Sopenharmony_ci		op_sz = sizeof(pts_dts);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	} else if (args->send_pts) {
1138c2ecf20Sopenharmony_ci		pts.pts1 = (0x1 << 5) | ((args->pts & mask1) >> 29) | 0x1;
1148c2ecf20Sopenharmony_ci		pts.pts2 = cpu_to_be16(((args->pts & mask2) >> 14) | 0x1);
1158c2ecf20Sopenharmony_ci		pts.pts3 = cpu_to_be16(((args->pts & mask3) << 1) | 0x1);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		op = &pts;
1188c2ecf20Sopenharmony_ci		op_sz = sizeof(pts);
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* copy PTS/DTS optional */
1228c2ecf20Sopenharmony_ci	nbytes += vidtv_memcpy(args->dest_buf,
1238c2ecf20Sopenharmony_ci			       args->dest_offset + nbytes,
1248c2ecf20Sopenharmony_ci			       args->dest_buf_sz,
1258c2ecf20Sopenharmony_ci			       op,
1268c2ecf20Sopenharmony_ci			       op_sz);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return nbytes;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic u32 vidtv_pes_write_h(struct pes_header_write_args *args)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	u32 nbytes = 0;  /* the number of bytes written by this function */
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	struct vidtv_mpeg_pes pes_header          = {};
1368c2ecf20Sopenharmony_ci	struct vidtv_pes_optional pes_optional    = {};
1378c2ecf20Sopenharmony_ci	struct pes_header_write_args pts_dts_args;
1388c2ecf20Sopenharmony_ci	u32 stream_id = (args->encoder_id == S302M) ? PRIVATE_STREAM_1_ID : args->stream_id;
1398c2ecf20Sopenharmony_ci	u16 pes_opt_bitfield = 0x01 << 15;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	pes_header.bitfield = cpu_to_be32((PES_START_CODE_PREFIX << 8) | stream_id);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	pes_header.length = cpu_to_be16(vidtv_pes_op_get_len(args->send_pts,
1448c2ecf20Sopenharmony_ci							     args->send_dts) +
1458c2ecf20Sopenharmony_ci							     args->access_unit_len);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (args->send_pts && args->send_dts)
1488c2ecf20Sopenharmony_ci		pes_opt_bitfield |= (0x3 << 6);
1498c2ecf20Sopenharmony_ci	else if (args->send_pts)
1508c2ecf20Sopenharmony_ci		pes_opt_bitfield |= (0x1 << 7);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	pes_optional.bitfield = cpu_to_be16(pes_opt_bitfield);
1538c2ecf20Sopenharmony_ci	pes_optional.length = vidtv_pes_op_get_len(args->send_pts, args->send_dts) +
1548c2ecf20Sopenharmony_ci			      args->n_pes_h_s_bytes -
1558c2ecf20Sopenharmony_ci			      sizeof(struct vidtv_pes_optional);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* copy header */
1588c2ecf20Sopenharmony_ci	nbytes += vidtv_memcpy(args->dest_buf,
1598c2ecf20Sopenharmony_ci			       args->dest_offset + nbytes,
1608c2ecf20Sopenharmony_ci			       args->dest_buf_sz,
1618c2ecf20Sopenharmony_ci			       &pes_header,
1628c2ecf20Sopenharmony_ci			       sizeof(pes_header));
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* copy optional header bits */
1658c2ecf20Sopenharmony_ci	nbytes += vidtv_memcpy(args->dest_buf,
1668c2ecf20Sopenharmony_ci			       args->dest_offset + nbytes,
1678c2ecf20Sopenharmony_ci			       args->dest_buf_sz,
1688c2ecf20Sopenharmony_ci			       &pes_optional,
1698c2ecf20Sopenharmony_ci			       sizeof(pes_optional));
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* copy the timing information */
1728c2ecf20Sopenharmony_ci	pts_dts_args = *args;
1738c2ecf20Sopenharmony_ci	pts_dts_args.dest_offset = args->dest_offset + nbytes;
1748c2ecf20Sopenharmony_ci	nbytes += vidtv_pes_write_pts_dts(&pts_dts_args);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* write any PES header stuffing */
1778c2ecf20Sopenharmony_ci	nbytes += vidtv_pes_write_header_stuffing(args);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return nbytes;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic u32 vidtv_pes_write_pcr_bits(u8 *to, u32 to_offset, u64 pcr)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	/* Exact same from ffmpeg. PCR is a counter driven by a 27Mhz clock */
1858c2ecf20Sopenharmony_ci	u64 div;
1868c2ecf20Sopenharmony_ci	u64 rem;
1878c2ecf20Sopenharmony_ci	u8 *buf = to + to_offset;
1888c2ecf20Sopenharmony_ci	u64 pcr_low;
1898c2ecf20Sopenharmony_ci	u64 pcr_high;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	div = div64_u64_rem(pcr, 300, &rem);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	pcr_low = rem; /* pcr_low = pcr % 300 */
1948c2ecf20Sopenharmony_ci	pcr_high = div; /* pcr_high = pcr / 300 */
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	*buf++ = pcr_high >> 25;
1978c2ecf20Sopenharmony_ci	*buf++ = pcr_high >> 17;
1988c2ecf20Sopenharmony_ci	*buf++ = pcr_high >>  9;
1998c2ecf20Sopenharmony_ci	*buf++ = pcr_high >>  1;
2008c2ecf20Sopenharmony_ci	*buf++ = pcr_high <<  7 | pcr_low >> 8 | 0x7e;
2018c2ecf20Sopenharmony_ci	*buf++ = pcr_low;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return 6;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic u32 vidtv_pes_write_stuffing(struct pes_ts_header_write_args *args,
2078c2ecf20Sopenharmony_ci				    u32 dest_offset, bool need_pcr,
2088c2ecf20Sopenharmony_ci				    u64 *last_pcr)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct vidtv_mpeg_ts_adaption ts_adap = {};
2118c2ecf20Sopenharmony_ci	int stuff_nbytes;
2128c2ecf20Sopenharmony_ci	u32 nbytes = 0;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (!args->n_stuffing_bytes)
2158c2ecf20Sopenharmony_ci		return 0;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	ts_adap.random_access = 1;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* length _immediately_ following 'adaptation_field_length' */
2208c2ecf20Sopenharmony_ci	if (need_pcr) {
2218c2ecf20Sopenharmony_ci		ts_adap.PCR = 1;
2228c2ecf20Sopenharmony_ci		ts_adap.length = SIZE_PCR;
2238c2ecf20Sopenharmony_ci	} else {
2248c2ecf20Sopenharmony_ci		ts_adap.length = sizeof(ts_adap);
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci	stuff_nbytes = args->n_stuffing_bytes - ts_adap.length;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	ts_adap.length -= sizeof(ts_adap.length);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (unlikely(stuff_nbytes < 0))
2318c2ecf20Sopenharmony_ci		stuff_nbytes = 0;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	ts_adap.length += stuff_nbytes;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* write the adap after the TS header */
2368c2ecf20Sopenharmony_ci	nbytes += vidtv_memcpy(args->dest_buf,
2378c2ecf20Sopenharmony_ci			       dest_offset + nbytes,
2388c2ecf20Sopenharmony_ci			       args->dest_buf_sz,
2398c2ecf20Sopenharmony_ci			       &ts_adap,
2408c2ecf20Sopenharmony_ci			       sizeof(ts_adap));
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* write the optional PCR */
2438c2ecf20Sopenharmony_ci	if (need_pcr) {
2448c2ecf20Sopenharmony_ci		nbytes += vidtv_pes_write_pcr_bits(args->dest_buf,
2458c2ecf20Sopenharmony_ci						   dest_offset + nbytes,
2468c2ecf20Sopenharmony_ci						   args->pcr);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		*last_pcr = args->pcr;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* write the stuffing bytes, if are there anything left */
2528c2ecf20Sopenharmony_ci	if (stuff_nbytes)
2538c2ecf20Sopenharmony_ci		nbytes += vidtv_memset(args->dest_buf,
2548c2ecf20Sopenharmony_ci				       dest_offset + nbytes,
2558c2ecf20Sopenharmony_ci				       args->dest_buf_sz,
2568c2ecf20Sopenharmony_ci				       TS_FILL_BYTE,
2578c2ecf20Sopenharmony_ci				       stuff_nbytes);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/*
2608c2ecf20Sopenharmony_ci	 * The n_stuffing_bytes contain a pre-calculated value of
2618c2ecf20Sopenharmony_ci	 * the amount of data that this function would read, made from
2628c2ecf20Sopenharmony_ci	 * vidtv_pes_h_get_len(). If something went wrong, print a warning
2638c2ecf20Sopenharmony_ci	 */
2648c2ecf20Sopenharmony_ci	if (nbytes != args->n_stuffing_bytes)
2658c2ecf20Sopenharmony_ci		pr_warn_ratelimited("write size was %d, expected %d\n",
2668c2ecf20Sopenharmony_ci				    nbytes, args->n_stuffing_bytes);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return nbytes;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic u32 vidtv_pes_write_ts_h(struct pes_ts_header_write_args args,
2728c2ecf20Sopenharmony_ci				bool need_pcr, u64 *last_pcr)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	/* number of bytes written by this function */
2758c2ecf20Sopenharmony_ci	u32 nbytes = 0;
2768c2ecf20Sopenharmony_ci	struct vidtv_mpeg_ts ts_header = {};
2778c2ecf20Sopenharmony_ci	u16 payload_start = !args.wrote_pes_header;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	ts_header.sync_byte        = TS_SYNC_BYTE;
2808c2ecf20Sopenharmony_ci	ts_header.bitfield         = cpu_to_be16((payload_start << 14) | args.pid);
2818c2ecf20Sopenharmony_ci	ts_header.scrambling       = 0;
2828c2ecf20Sopenharmony_ci	ts_header.adaptation_field = (args.n_stuffing_bytes) > 0;
2838c2ecf20Sopenharmony_ci	ts_header.payload          = (args.n_stuffing_bytes) < PES_TS_HEADER_MAX_STUFFING_BYTES;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	ts_header.continuity_counter = *args.continuity_counter;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	vidtv_ts_inc_cc(args.continuity_counter);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* write the TS header */
2908c2ecf20Sopenharmony_ci	nbytes += vidtv_memcpy(args.dest_buf,
2918c2ecf20Sopenharmony_ci			       args.dest_offset + nbytes,
2928c2ecf20Sopenharmony_ci			       args.dest_buf_sz,
2938c2ecf20Sopenharmony_ci			       &ts_header,
2948c2ecf20Sopenharmony_ci			       sizeof(ts_header));
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* write stuffing, if any */
2978c2ecf20Sopenharmony_ci	nbytes += vidtv_pes_write_stuffing(&args, args.dest_offset + nbytes,
2988c2ecf20Sopenharmony_ci					   need_pcr, last_pcr);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	return nbytes;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ciu32 vidtv_pes_write_into(struct pes_write_args *args)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	u32 unaligned_bytes = (args->dest_offset % TS_PACKET_LEN);
3068c2ecf20Sopenharmony_ci	struct pes_ts_header_write_args ts_header_args = {
3078c2ecf20Sopenharmony_ci		.dest_buf		= args->dest_buf,
3088c2ecf20Sopenharmony_ci		.dest_buf_sz		= args->dest_buf_sz,
3098c2ecf20Sopenharmony_ci		.pid			= args->pid,
3108c2ecf20Sopenharmony_ci		.pcr			= args->pcr,
3118c2ecf20Sopenharmony_ci		.continuity_counter	= args->continuity_counter,
3128c2ecf20Sopenharmony_ci	};
3138c2ecf20Sopenharmony_ci	struct pes_header_write_args pes_header_args = {
3148c2ecf20Sopenharmony_ci		.dest_buf		= args->dest_buf,
3158c2ecf20Sopenharmony_ci		.dest_buf_sz		= args->dest_buf_sz,
3168c2ecf20Sopenharmony_ci		.encoder_id		= args->encoder_id,
3178c2ecf20Sopenharmony_ci		.send_pts		= args->send_pts,
3188c2ecf20Sopenharmony_ci		.pts			= args->pts,
3198c2ecf20Sopenharmony_ci		.send_dts		= args->send_dts,
3208c2ecf20Sopenharmony_ci		.dts			= args->dts,
3218c2ecf20Sopenharmony_ci		.stream_id		= args->stream_id,
3228c2ecf20Sopenharmony_ci		.n_pes_h_s_bytes	= args->n_pes_h_s_bytes,
3238c2ecf20Sopenharmony_ci		.access_unit_len	= args->access_unit_len,
3248c2ecf20Sopenharmony_ci	};
3258c2ecf20Sopenharmony_ci	u32 remaining_len = args->access_unit_len;
3268c2ecf20Sopenharmony_ci	bool wrote_pes_header = false;
3278c2ecf20Sopenharmony_ci	u64 last_pcr = args->pcr;
3288c2ecf20Sopenharmony_ci	bool need_pcr = true;
3298c2ecf20Sopenharmony_ci	u32 available_space;
3308c2ecf20Sopenharmony_ci	u32 payload_size;
3318c2ecf20Sopenharmony_ci	u32 stuff_bytes;
3328c2ecf20Sopenharmony_ci	u32 nbytes = 0;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (unaligned_bytes) {
3358c2ecf20Sopenharmony_ci		pr_warn_ratelimited("buffer is misaligned, while starting PES\n");
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		/* forcibly align and hope for the best */
3388c2ecf20Sopenharmony_ci		nbytes += vidtv_memset(args->dest_buf,
3398c2ecf20Sopenharmony_ci				       args->dest_offset + nbytes,
3408c2ecf20Sopenharmony_ci				       args->dest_buf_sz,
3418c2ecf20Sopenharmony_ci				       TS_FILL_BYTE,
3428c2ecf20Sopenharmony_ci				       TS_PACKET_LEN - unaligned_bytes);
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	while (remaining_len) {
3468c2ecf20Sopenharmony_ci		available_space = TS_PAYLOAD_LEN;
3478c2ecf20Sopenharmony_ci		/*
3488c2ecf20Sopenharmony_ci		 * The amount of space initially available in the TS packet.
3498c2ecf20Sopenharmony_ci		 * if this is the beginning of the PES packet, take into account
3508c2ecf20Sopenharmony_ci		 * the space needed for the TS header _and_ for the PES header
3518c2ecf20Sopenharmony_ci		 */
3528c2ecf20Sopenharmony_ci		if (!wrote_pes_header)
3538c2ecf20Sopenharmony_ci			available_space -= vidtv_pes_h_get_len(args->send_pts,
3548c2ecf20Sopenharmony_ci							       args->send_dts);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		/*
3578c2ecf20Sopenharmony_ci		 * if the encoder has inserted stuffing bytes in the PES
3588c2ecf20Sopenharmony_ci		 * header, account for them.
3598c2ecf20Sopenharmony_ci		 */
3608c2ecf20Sopenharmony_ci		available_space -= args->n_pes_h_s_bytes;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		/* Take the extra adaptation into account if need to send PCR */
3638c2ecf20Sopenharmony_ci		if (need_pcr) {
3648c2ecf20Sopenharmony_ci			available_space -= SIZE_PCR;
3658c2ecf20Sopenharmony_ci			stuff_bytes = SIZE_PCR;
3668c2ecf20Sopenharmony_ci		} else {
3678c2ecf20Sopenharmony_ci			stuff_bytes = 0;
3688c2ecf20Sopenharmony_ci		}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		/*
3718c2ecf20Sopenharmony_ci		 * how much of the _actual_ payload should be written in this
3728c2ecf20Sopenharmony_ci		 * packet.
3738c2ecf20Sopenharmony_ci		 */
3748c2ecf20Sopenharmony_ci		if (remaining_len >= available_space) {
3758c2ecf20Sopenharmony_ci			payload_size = available_space;
3768c2ecf20Sopenharmony_ci		} else {
3778c2ecf20Sopenharmony_ci			/* Last frame should ensure 188-bytes PS alignment */
3788c2ecf20Sopenharmony_ci			payload_size = remaining_len;
3798c2ecf20Sopenharmony_ci			stuff_bytes += available_space - payload_size;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci			/*
3828c2ecf20Sopenharmony_ci			 * Ensure that the stuff bytes will be within the
3838c2ecf20Sopenharmony_ci			 * allowed range, decrementing the number of payload
3848c2ecf20Sopenharmony_ci			 * bytes to write if needed.
3858c2ecf20Sopenharmony_ci			 */
3868c2ecf20Sopenharmony_ci			if (stuff_bytes > PES_TS_HEADER_MAX_STUFFING_BYTES) {
3878c2ecf20Sopenharmony_ci				u32 tmp = stuff_bytes - PES_TS_HEADER_MAX_STUFFING_BYTES;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci				stuff_bytes = PES_TS_HEADER_MAX_STUFFING_BYTES;
3908c2ecf20Sopenharmony_ci				payload_size -= tmp;
3918c2ecf20Sopenharmony_ci			}
3928c2ecf20Sopenharmony_ci		}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		/* write ts header */
3958c2ecf20Sopenharmony_ci		ts_header_args.dest_offset = args->dest_offset + nbytes;
3968c2ecf20Sopenharmony_ci		ts_header_args.wrote_pes_header	= wrote_pes_header;
3978c2ecf20Sopenharmony_ci		ts_header_args.n_stuffing_bytes	= stuff_bytes;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		nbytes += vidtv_pes_write_ts_h(ts_header_args, need_pcr,
4008c2ecf20Sopenharmony_ci					       &last_pcr);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		need_pcr = false;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci		if (!wrote_pes_header) {
4058c2ecf20Sopenharmony_ci			/* write the PES header only once */
4068c2ecf20Sopenharmony_ci			pes_header_args.dest_offset = args->dest_offset +
4078c2ecf20Sopenharmony_ci						      nbytes;
4088c2ecf20Sopenharmony_ci			nbytes += vidtv_pes_write_h(&pes_header_args);
4098c2ecf20Sopenharmony_ci			wrote_pes_header = true;
4108c2ecf20Sopenharmony_ci		}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		/* write as much of the payload as we possibly can */
4138c2ecf20Sopenharmony_ci		nbytes += vidtv_memcpy(args->dest_buf,
4148c2ecf20Sopenharmony_ci				       args->dest_offset + nbytes,
4158c2ecf20Sopenharmony_ci				       args->dest_buf_sz,
4168c2ecf20Sopenharmony_ci				       args->from,
4178c2ecf20Sopenharmony_ci				       payload_size);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		args->from += payload_size;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		remaining_len -= payload_size;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return nbytes;
4258c2ecf20Sopenharmony_ci}
426