1f90cf607SDaniel W. S. Almeida // SPDX-License-Identifier: GPL-2.0
2f90cf607SDaniel W. S. Almeida /*
3f90cf607SDaniel W. S. Almeida  * Vidtv serves as a reference DVB driver and helps validate the existing APIs
4f90cf607SDaniel W. S. Almeida  * in the media subsystem. It can also aid developers working on userspace
5f90cf607SDaniel W. S. Almeida  * applications.
6f90cf607SDaniel W. S. Almeida  *
7f90cf607SDaniel W. S. Almeida  * This file contains the multiplexer logic for TS packets from different
8f90cf607SDaniel W. S. Almeida  * elementary streams
9f90cf607SDaniel W. S. Almeida  *
10f90cf607SDaniel W. S. Almeida  * Loosely based on libavcodec/mpegtsenc.c
11f90cf607SDaniel W. S. Almeida  *
12f90cf607SDaniel W. S. Almeida  * Copyright (C) 2020 Daniel W. S. Almeida
13f90cf607SDaniel W. S. Almeida  */
14f90cf607SDaniel W. S. Almeida 
158922e393SMauro Carvalho Chehab #include <linux/delay.h>
168922e393SMauro Carvalho Chehab #include <linux/dev_printk.h>
17f90cf607SDaniel W. S. Almeida #include <linux/jiffies.h>
18f90cf607SDaniel W. S. Almeida #include <linux/kernel.h>
19f90cf607SDaniel W. S. Almeida #include <linux/math64.h>
208922e393SMauro Carvalho Chehab #include <linux/ratelimit.h>
218922e393SMauro Carvalho Chehab #include <linux/slab.h>
228922e393SMauro Carvalho Chehab #include <linux/types.h>
238922e393SMauro Carvalho Chehab #include <linux/vmalloc.h>
2482d00a1aSMauro Carvalho Chehab 
25f90cf607SDaniel W. S. Almeida #include "vidtv_channel.h"
26f90cf607SDaniel W. S. Almeida #include "vidtv_common.h"
278922e393SMauro Carvalho Chehab #include "vidtv_encoder.h"
288922e393SMauro Carvalho Chehab #include "vidtv_mux.h"
298922e393SMauro Carvalho Chehab #include "vidtv_pes.h"
30f90cf607SDaniel W. S. Almeida #include "vidtv_psi.h"
318922e393SMauro Carvalho Chehab #include "vidtv_ts.h"
32f90cf607SDaniel W. S. Almeida 
33f90cf607SDaniel W. S. Almeida static struct vidtv_mux_pid_ctx
vidtv_mux_get_pid_ctx(struct vidtv_mux * m,u16 pid)34f90cf607SDaniel W. S. Almeida *vidtv_mux_get_pid_ctx(struct vidtv_mux *m, u16 pid)
35f90cf607SDaniel W. S. Almeida {
36f90cf607SDaniel W. S. Almeida 	struct vidtv_mux_pid_ctx *ctx;
37f90cf607SDaniel W. S. Almeida 
38f90cf607SDaniel W. S. Almeida 	hash_for_each_possible(m->pid_ctx, ctx, h, pid)
39f90cf607SDaniel W. S. Almeida 		if (ctx->pid == pid)
40f90cf607SDaniel W. S. Almeida 			return ctx;
41f90cf607SDaniel W. S. Almeida 	return NULL;
42f90cf607SDaniel W. S. Almeida }
43f90cf607SDaniel W. S. Almeida 
44f90cf607SDaniel W. S. Almeida static struct vidtv_mux_pid_ctx
vidtv_mux_create_pid_ctx_once(struct vidtv_mux * m,u16 pid)45f90cf607SDaniel W. S. Almeida *vidtv_mux_create_pid_ctx_once(struct vidtv_mux *m, u16 pid)
46f90cf607SDaniel W. S. Almeida {
47f90cf607SDaniel W. S. Almeida 	struct vidtv_mux_pid_ctx *ctx;
48f90cf607SDaniel W. S. Almeida 
49f90cf607SDaniel W. S. Almeida 	ctx = vidtv_mux_get_pid_ctx(m, pid);
50f90cf607SDaniel W. S. Almeida 	if (ctx)
513be80379SMauro Carvalho Chehab 		return ctx;
52f90cf607SDaniel W. S. Almeida 
53f90cf607SDaniel W. S. Almeida 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
543be80379SMauro Carvalho Chehab 	if (!ctx)
553be80379SMauro Carvalho Chehab 		return NULL;
563be80379SMauro Carvalho Chehab 
57f90cf607SDaniel W. S. Almeida 	ctx->pid = pid;
58f90cf607SDaniel W. S. Almeida 	ctx->cc  = 0;
59f90cf607SDaniel W. S. Almeida 	hash_add(m->pid_ctx, &ctx->h, pid);
60f90cf607SDaniel W. S. Almeida 
61f90cf607SDaniel W. S. Almeida 	return ctx;
62f90cf607SDaniel W. S. Almeida }
63f90cf607SDaniel W. S. Almeida 
vidtv_mux_pid_ctx_destroy(struct vidtv_mux * m)64f90cf607SDaniel W. S. Almeida static void vidtv_mux_pid_ctx_destroy(struct vidtv_mux *m)
65f90cf607SDaniel W. S. Almeida {
66f90cf607SDaniel W. S. Almeida 	struct vidtv_mux_pid_ctx *ctx;
67f90cf607SDaniel W. S. Almeida 	struct hlist_node *tmp;
68a8bd461cSMauro Carvalho Chehab 	int bkt;
69f90cf607SDaniel W. S. Almeida 
70f90cf607SDaniel W. S. Almeida 	hash_for_each_safe(m->pid_ctx, bkt, tmp, ctx, h) {
71f90cf607SDaniel W. S. Almeida 		hash_del(&ctx->h);
72f90cf607SDaniel W. S. Almeida 		kfree(ctx);
73f90cf607SDaniel W. S. Almeida 	}
74f90cf607SDaniel W. S. Almeida }
75f90cf607SDaniel W. S. Almeida 
vidtv_mux_pid_ctx_init(struct vidtv_mux * m)763be80379SMauro Carvalho Chehab static int vidtv_mux_pid_ctx_init(struct vidtv_mux *m)
773be80379SMauro Carvalho Chehab {
783be80379SMauro Carvalho Chehab 	struct vidtv_psi_table_pat_program *p = m->si.pat->program;
793be80379SMauro Carvalho Chehab 	u16 pid;
803be80379SMauro Carvalho Chehab 
813be80379SMauro Carvalho Chehab 	hash_init(m->pid_ctx);
823be80379SMauro Carvalho Chehab 	/* push the pcr pid ctx */
833be80379SMauro Carvalho Chehab 	if (!vidtv_mux_create_pid_ctx_once(m, m->pcr_pid))
843be80379SMauro Carvalho Chehab 		return -ENOMEM;
853be80379SMauro Carvalho Chehab 	/* push the NULL packet pid ctx */
863be80379SMauro Carvalho Chehab 	if (!vidtv_mux_create_pid_ctx_once(m, TS_NULL_PACKET_PID))
873be80379SMauro Carvalho Chehab 		goto free;
883be80379SMauro Carvalho Chehab 	/* push the PAT pid ctx */
893be80379SMauro Carvalho Chehab 	if (!vidtv_mux_create_pid_ctx_once(m, VIDTV_PAT_PID))
903be80379SMauro Carvalho Chehab 		goto free;
913be80379SMauro Carvalho Chehab 	/* push the SDT pid ctx */
923be80379SMauro Carvalho Chehab 	if (!vidtv_mux_create_pid_ctx_once(m, VIDTV_SDT_PID))
933be80379SMauro Carvalho Chehab 		goto free;
943be80379SMauro Carvalho Chehab 	/* push the NIT pid ctx */
953be80379SMauro Carvalho Chehab 	if (!vidtv_mux_create_pid_ctx_once(m, VIDTV_NIT_PID))
963be80379SMauro Carvalho Chehab 		goto free;
973be80379SMauro Carvalho Chehab 	/* push the EIT pid ctx */
983be80379SMauro Carvalho Chehab 	if (!vidtv_mux_create_pid_ctx_once(m, VIDTV_EIT_PID))
993be80379SMauro Carvalho Chehab 		goto free;
1003be80379SMauro Carvalho Chehab 
1013be80379SMauro Carvalho Chehab 	/* add a ctx for all PMT sections */
1023be80379SMauro Carvalho Chehab 	while (p) {
1033be80379SMauro Carvalho Chehab 		pid = vidtv_psi_get_pat_program_pid(p);
1043be80379SMauro Carvalho Chehab 		vidtv_mux_create_pid_ctx_once(m, pid);
1053be80379SMauro Carvalho Chehab 		p = p->next;
1063be80379SMauro Carvalho Chehab 	}
1073be80379SMauro Carvalho Chehab 
1083be80379SMauro Carvalho Chehab 	return 0;
1093be80379SMauro Carvalho Chehab 
1103be80379SMauro Carvalho Chehab free:
1113be80379SMauro Carvalho Chehab 	vidtv_mux_pid_ctx_destroy(m);
1123be80379SMauro Carvalho Chehab 	return -ENOMEM;
1133be80379SMauro Carvalho Chehab }
1143be80379SMauro Carvalho Chehab 
vidtv_mux_update_clk(struct vidtv_mux * m)115f90cf607SDaniel W. S. Almeida static void vidtv_mux_update_clk(struct vidtv_mux *m)
116f90cf607SDaniel W. S. Almeida {
117f90cf607SDaniel W. S. Almeida 	/* call this at every thread iteration */
118f90cf607SDaniel W. S. Almeida 	u64 elapsed_time;
119f90cf607SDaniel W. S. Almeida 
120880a8fc0SMauro Carvalho Chehab 	m->timing.past_jiffies = m->timing.current_jiffies;
121f90cf607SDaniel W. S. Almeida 	m->timing.current_jiffies = get_jiffies_64();
122f90cf607SDaniel W. S. Almeida 
123f90cf607SDaniel W. S. Almeida 	elapsed_time = jiffies_to_usecs(m->timing.current_jiffies -
124f90cf607SDaniel W. S. Almeida 					m->timing.past_jiffies);
125f90cf607SDaniel W. S. Almeida 
126f90cf607SDaniel W. S. Almeida 	/* update the 27Mhz clock proportionally to the elapsed time */
127f90cf607SDaniel W. S. Almeida 	m->timing.clk += (CLOCK_UNIT_27MHZ / USEC_PER_SEC) * elapsed_time;
128f90cf607SDaniel W. S. Almeida }
129f90cf607SDaniel W. S. Almeida 
vidtv_mux_push_si(struct vidtv_mux * m)130f90cf607SDaniel W. S. Almeida static u32 vidtv_mux_push_si(struct vidtv_mux *m)
131f90cf607SDaniel W. S. Almeida {
132c857b065SMauro Carvalho Chehab 	struct vidtv_psi_pat_write_args pat_args = {
133c857b065SMauro Carvalho Chehab 		.buf                = m->mux_buf,
134c857b065SMauro Carvalho Chehab 		.buf_sz             = m->mux_buf_sz,
135c857b065SMauro Carvalho Chehab 		.pat                = m->si.pat,
136c857b065SMauro Carvalho Chehab 	};
137c857b065SMauro Carvalho Chehab 	struct vidtv_psi_pmt_write_args pmt_args = {
138c857b065SMauro Carvalho Chehab 		.buf                = m->mux_buf,
139c857b065SMauro Carvalho Chehab 		.buf_sz             = m->mux_buf_sz,
140c857b065SMauro Carvalho Chehab 		.pcr_pid            = m->pcr_pid,
141c857b065SMauro Carvalho Chehab 	};
142c857b065SMauro Carvalho Chehab 	struct vidtv_psi_sdt_write_args sdt_args = {
143c857b065SMauro Carvalho Chehab 		.buf                = m->mux_buf,
144c857b065SMauro Carvalho Chehab 		.buf_sz             = m->mux_buf_sz,
145c857b065SMauro Carvalho Chehab 		.sdt                = m->si.sdt,
146c857b065SMauro Carvalho Chehab 	};
147c857b065SMauro Carvalho Chehab 	struct vidtv_psi_nit_write_args nit_args = {
148c857b065SMauro Carvalho Chehab 		.buf                = m->mux_buf,
149c857b065SMauro Carvalho Chehab 		.buf_sz             = m->mux_buf_sz,
150c857b065SMauro Carvalho Chehab 		.nit                = m->si.nit,
151c857b065SMauro Carvalho Chehab 
152c857b065SMauro Carvalho Chehab 	};
153c857b065SMauro Carvalho Chehab 	struct vidtv_psi_eit_write_args eit_args = {
154c857b065SMauro Carvalho Chehab 		.buf                = m->mux_buf,
155c857b065SMauro Carvalho Chehab 		.buf_sz             = m->mux_buf_sz,
156c857b065SMauro Carvalho Chehab 		.eit                = m->si.eit,
157c857b065SMauro Carvalho Chehab 	};
158a8bd461cSMauro Carvalho Chehab 	u32 initial_offset = m->mux_buf_offset;
159a8bd461cSMauro Carvalho Chehab 	struct vidtv_mux_pid_ctx *pat_ctx;
160a8bd461cSMauro Carvalho Chehab 	struct vidtv_mux_pid_ctx *pmt_ctx;
161a8bd461cSMauro Carvalho Chehab 	struct vidtv_mux_pid_ctx *sdt_ctx;
162a8bd461cSMauro Carvalho Chehab 	struct vidtv_mux_pid_ctx *nit_ctx;
163a8bd461cSMauro Carvalho Chehab 	struct vidtv_mux_pid_ctx *eit_ctx;
164a8bd461cSMauro Carvalho Chehab 	u32 nbytes;
165f90cf607SDaniel W. S. Almeida 	u16 pmt_pid;
166f90cf607SDaniel W. S. Almeida 	u32 i;
167f90cf607SDaniel W. S. Almeida 
168f90cf607SDaniel W. S. Almeida 	pat_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_PAT_PID);
169f90cf607SDaniel W. S. Almeida 	sdt_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_SDT_PID);
170c2f78f0cSDaniel W. S. Almeida 	nit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_NIT_PID);
1717a7899f6SDaniel W. S. Almeida 	eit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_EIT_PID);
172f90cf607SDaniel W. S. Almeida 
173f90cf607SDaniel W. S. Almeida 	pat_args.offset             = m->mux_buf_offset;
174f90cf607SDaniel W. S. Almeida 	pat_args.continuity_counter = &pat_ctx->cc;
175f90cf607SDaniel W. S. Almeida 
1767f957515SMauro Carvalho Chehab 	m->mux_buf_offset += vidtv_psi_pat_write_into(&pat_args);
177f90cf607SDaniel W. S. Almeida 
178039b7caeSMauro Carvalho Chehab 	for (i = 0; i < m->si.pat->num_pmt; ++i) {
179f90cf607SDaniel W. S. Almeida 		pmt_pid = vidtv_psi_pmt_get_pid(m->si.pmt_secs[i],
180f90cf607SDaniel W. S. Almeida 						m->si.pat);
181f90cf607SDaniel W. S. Almeida 
182f90cf607SDaniel W. S. Almeida 		if (pmt_pid > TS_LAST_VALID_PID) {
1839cfb4d36SMauro Carvalho Chehab 			dev_warn_ratelimited(m->dev,
1849cfb4d36SMauro Carvalho Chehab 					     "PID: %d not found\n", pmt_pid);
185f90cf607SDaniel W. S. Almeida 			continue;
186f90cf607SDaniel W. S. Almeida 		}
187f90cf607SDaniel W. S. Almeida 
188f90cf607SDaniel W. S. Almeida 		pmt_ctx = vidtv_mux_get_pid_ctx(m, pmt_pid);
189f90cf607SDaniel W. S. Almeida 
190f90cf607SDaniel W. S. Almeida 		pmt_args.offset             = m->mux_buf_offset;
191f90cf607SDaniel W. S. Almeida 		pmt_args.pmt                = m->si.pmt_secs[i];
192f90cf607SDaniel W. S. Almeida 		pmt_args.pid                = pmt_pid;
193f90cf607SDaniel W. S. Almeida 		pmt_args.continuity_counter = &pmt_ctx->cc;
194f90cf607SDaniel W. S. Almeida 
195f90cf607SDaniel W. S. Almeida 		/* write each section into buffer */
196db9569f6SMauro Carvalho Chehab 		m->mux_buf_offset += vidtv_psi_pmt_write_into(&pmt_args);
197f90cf607SDaniel W. S. Almeida 	}
198f90cf607SDaniel W. S. Almeida 
199f90cf607SDaniel W. S. Almeida 	sdt_args.offset             = m->mux_buf_offset;
200f90cf607SDaniel W. S. Almeida 	sdt_args.continuity_counter = &sdt_ctx->cc;
201f90cf607SDaniel W. S. Almeida 
2026286a4b7SMauro Carvalho Chehab 	m->mux_buf_offset += vidtv_psi_sdt_write_into(&sdt_args);
203f90cf607SDaniel W. S. Almeida 
204c2f78f0cSDaniel W. S. Almeida 	nit_args.offset             = m->mux_buf_offset;
205c2f78f0cSDaniel W. S. Almeida 	nit_args.continuity_counter = &nit_ctx->cc;
206c2f78f0cSDaniel W. S. Almeida 
2075a5b9fb1SMauro Carvalho Chehab 	m->mux_buf_offset += vidtv_psi_nit_write_into(&nit_args);
208c2f78f0cSDaniel W. S. Almeida 
2097a7899f6SDaniel W. S. Almeida 	eit_args.offset             = m->mux_buf_offset;
2107a7899f6SDaniel W. S. Almeida 	eit_args.continuity_counter = &eit_ctx->cc;
2117a7899f6SDaniel W. S. Almeida 
212b0879828SMauro Carvalho Chehab 	m->mux_buf_offset += vidtv_psi_eit_write_into(&eit_args);
2137a7899f6SDaniel W. S. Almeida 
214f90cf607SDaniel W. S. Almeida 	nbytes = m->mux_buf_offset - initial_offset;
215f90cf607SDaniel W. S. Almeida 
216f90cf607SDaniel W. S. Almeida 	m->num_streamed_si++;
217f90cf607SDaniel W. S. Almeida 
218f90cf607SDaniel W. S. Almeida 	return nbytes;
219f90cf607SDaniel W. S. Almeida }
220f90cf607SDaniel W. S. Almeida 
vidtv_mux_push_pcr(struct vidtv_mux * m)221f90cf607SDaniel W. S. Almeida static u32 vidtv_mux_push_pcr(struct vidtv_mux *m)
222f90cf607SDaniel W. S. Almeida {
223f90cf607SDaniel W. S. Almeida 	struct pcr_write_args args = {};
224f90cf607SDaniel W. S. Almeida 	struct vidtv_mux_pid_ctx *ctx;
225f90cf607SDaniel W. S. Almeida 	u32 nbytes = 0;
226f90cf607SDaniel W. S. Almeida 
227f90cf607SDaniel W. S. Almeida 	ctx                     = vidtv_mux_get_pid_ctx(m, m->pcr_pid);
228f90cf607SDaniel W. S. Almeida 	args.dest_buf           = m->mux_buf;
229f90cf607SDaniel W. S. Almeida 	args.pid                = m->pcr_pid;
230f90cf607SDaniel W. S. Almeida 	args.buf_sz             = m->mux_buf_sz;
231f90cf607SDaniel W. S. Almeida 	args.continuity_counter = &ctx->cc;
232f90cf607SDaniel W. S. Almeida 
233f90cf607SDaniel W. S. Almeida 	/* the 27Mhz clock will feed both parts of the PCR bitfield */
234f90cf607SDaniel W. S. Almeida 	args.pcr = m->timing.clk;
235f90cf607SDaniel W. S. Almeida 
236f90cf607SDaniel W. S. Almeida 	nbytes += vidtv_ts_pcr_write_into(args);
237f90cf607SDaniel W. S. Almeida 	m->mux_buf_offset += nbytes;
238f90cf607SDaniel W. S. Almeida 
239f90cf607SDaniel W. S. Almeida 	m->num_streamed_pcr++;
240f90cf607SDaniel W. S. Almeida 
241f90cf607SDaniel W. S. Almeida 	return nbytes;
242f90cf607SDaniel W. S. Almeida }
243f90cf607SDaniel W. S. Almeida 
vidtv_mux_should_push_pcr(struct vidtv_mux * m)244f90cf607SDaniel W. S. Almeida static bool vidtv_mux_should_push_pcr(struct vidtv_mux *m)
245f90cf607SDaniel W. S. Almeida {
246f90cf607SDaniel W. S. Almeida 	u64 next_pcr_at;
247f90cf607SDaniel W. S. Almeida 
248f90cf607SDaniel W. S. Almeida 	if (m->num_streamed_pcr == 0)
249f90cf607SDaniel W. S. Almeida 		return true;
250f90cf607SDaniel W. S. Almeida 
251f90cf607SDaniel W. S. Almeida 	next_pcr_at = m->timing.start_jiffies +
252f90cf607SDaniel W. S. Almeida 		      usecs_to_jiffies(m->num_streamed_pcr *
253f90cf607SDaniel W. S. Almeida 				       m->timing.pcr_period_usecs);
254f90cf607SDaniel W. S. Almeida 
255f90cf607SDaniel W. S. Almeida 	return time_after64(m->timing.current_jiffies, next_pcr_at);
256f90cf607SDaniel W. S. Almeida }
257f90cf607SDaniel W. S. Almeida 
vidtv_mux_should_push_si(struct vidtv_mux * m)258f90cf607SDaniel W. S. Almeida static bool vidtv_mux_should_push_si(struct vidtv_mux *m)
259f90cf607SDaniel W. S. Almeida {
260f90cf607SDaniel W. S. Almeida 	u64 next_si_at;
261f90cf607SDaniel W. S. Almeida 
262f90cf607SDaniel W. S. Almeida 	if (m->num_streamed_si == 0)
263f90cf607SDaniel W. S. Almeida 		return true;
264f90cf607SDaniel W. S. Almeida 
265f90cf607SDaniel W. S. Almeida 	next_si_at = m->timing.start_jiffies +
266f90cf607SDaniel W. S. Almeida 		     usecs_to_jiffies(m->num_streamed_si *
267f90cf607SDaniel W. S. Almeida 				      m->timing.si_period_usecs);
268f90cf607SDaniel W. S. Almeida 
269f90cf607SDaniel W. S. Almeida 	return time_after64(m->timing.current_jiffies, next_si_at);
270f90cf607SDaniel W. S. Almeida }
271f90cf607SDaniel W. S. Almeida 
vidtv_mux_packetize_access_units(struct vidtv_mux * m,struct vidtv_encoder * e)272f90cf607SDaniel W. S. Almeida static u32 vidtv_mux_packetize_access_units(struct vidtv_mux *m,
273f90cf607SDaniel W. S. Almeida 					    struct vidtv_encoder *e)
274f90cf607SDaniel W. S. Almeida {
275163d72a2SMauro Carvalho Chehab 	struct pes_write_args args = {
276163d72a2SMauro Carvalho Chehab 		.dest_buf           = m->mux_buf,
277163d72a2SMauro Carvalho Chehab 		.dest_buf_sz        = m->mux_buf_sz,
278163d72a2SMauro Carvalho Chehab 		.pid                = be16_to_cpu(e->es_pid),
279163d72a2SMauro Carvalho Chehab 		.encoder_id         = e->id,
280163d72a2SMauro Carvalho Chehab 		.stream_id          = be16_to_cpu(e->stream_id),
281163d72a2SMauro Carvalho Chehab 		.send_pts           = true,  /* forbidden value '01'... */
282163d72a2SMauro Carvalho Chehab 		.send_dts           = false, /* ...for PTS_DTS flags    */
283163d72a2SMauro Carvalho Chehab 	};
284f90cf607SDaniel W. S. Almeida 	struct vidtv_access_unit *au = e->access_units;
285a8bd461cSMauro Carvalho Chehab 	u32 initial_offset = m->mux_buf_offset;
286163d72a2SMauro Carvalho Chehab 	struct vidtv_mux_pid_ctx *pid_ctx;
287a8bd461cSMauro Carvalho Chehab 	u32 nbytes = 0;
288a8bd461cSMauro Carvalho Chehab 	u8 *buf = NULL;
289f90cf607SDaniel W. S. Almeida 
290163d72a2SMauro Carvalho Chehab 	/* see SMPTE 302M clause 6.4 */
291163d72a2SMauro Carvalho Chehab 	if (args.encoder_id == S302M) {
292163d72a2SMauro Carvalho Chehab 		args.send_dts = false;
293f90cf607SDaniel W. S. Almeida 		args.send_pts = true;
294163d72a2SMauro Carvalho Chehab 	}
295163d72a2SMauro Carvalho Chehab 
296163d72a2SMauro Carvalho Chehab 	pid_ctx = vidtv_mux_create_pid_ctx_once(m, be16_to_cpu(e->es_pid));
297163d72a2SMauro Carvalho Chehab 	args.continuity_counter = &pid_ctx->cc;
298f90cf607SDaniel W. S. Almeida 
299f90cf607SDaniel W. S. Almeida 	while (au) {
300f90cf607SDaniel W. S. Almeida 		buf                  = e->encoder_buf + au->offset;
301f90cf607SDaniel W. S. Almeida 		args.from            = buf;
302f90cf607SDaniel W. S. Almeida 		args.access_unit_len = au->nbytes;
303f90cf607SDaniel W. S. Almeida 		args.dest_offset     = m->mux_buf_offset;
304f90cf607SDaniel W. S. Almeida 		args.pts             = au->pts;
305a61d7d19SMauro Carvalho Chehab 		args.pcr	     = m->timing.clk;
306f90cf607SDaniel W. S. Almeida 
307163d72a2SMauro Carvalho Chehab 		m->mux_buf_offset += vidtv_pes_write_into(&args);
308f90cf607SDaniel W. S. Almeida 
309f90cf607SDaniel W. S. Almeida 		au = au->next;
310f90cf607SDaniel W. S. Almeida 	}
311f90cf607SDaniel W. S. Almeida 
312f90cf607SDaniel W. S. Almeida 	/*
313f90cf607SDaniel W. S. Almeida 	 * clear the encoder state once the ES data has been written to the mux
314f90cf607SDaniel W. S. Almeida 	 * buffer
315f90cf607SDaniel W. S. Almeida 	 */
316f90cf607SDaniel W. S. Almeida 	e->clear(e);
317f90cf607SDaniel W. S. Almeida 
318f90cf607SDaniel W. S. Almeida 	nbytes = m->mux_buf_offset - initial_offset;
319f90cf607SDaniel W. S. Almeida 	return nbytes;
320f90cf607SDaniel W. S. Almeida }
321f90cf607SDaniel W. S. Almeida 
vidtv_mux_poll_encoders(struct vidtv_mux * m)322f90cf607SDaniel W. S. Almeida static u32 vidtv_mux_poll_encoders(struct vidtv_mux *m)
323f90cf607SDaniel W. S. Almeida {
324f90cf607SDaniel W. S. Almeida 	struct vidtv_channel *cur_chnl = m->channels;
325f90cf607SDaniel W. S. Almeida 	struct vidtv_encoder *e = NULL;
326a8bd461cSMauro Carvalho Chehab 	u32 nbytes = 0;
327a8bd461cSMauro Carvalho Chehab 	u32 au_nbytes;
328f90cf607SDaniel W. S. Almeida 
329f90cf607SDaniel W. S. Almeida 	while (cur_chnl) {
330f90cf607SDaniel W. S. Almeida 		e = cur_chnl->encoders;
331f90cf607SDaniel W. S. Almeida 
332f90cf607SDaniel W. S. Almeida 		while (e) {
33337b288f5SDaniel W. S. Almeida 			e->encode(e);
334f90cf607SDaniel W. S. Almeida 			/* get the TS packets into the mux buffer */
335f90cf607SDaniel W. S. Almeida 			au_nbytes = vidtv_mux_packetize_access_units(m, e);
336f90cf607SDaniel W. S. Almeida 			nbytes += au_nbytes;
337f90cf607SDaniel W. S. Almeida 			m->mux_buf_offset += au_nbytes;
338f90cf607SDaniel W. S. Almeida 			/* grab next encoder */
339f90cf607SDaniel W. S. Almeida 			e = e->next;
340f90cf607SDaniel W. S. Almeida 		}
341f90cf607SDaniel W. S. Almeida 
342f90cf607SDaniel W. S. Almeida 		/* grab the next channel */
343f90cf607SDaniel W. S. Almeida 		cur_chnl = cur_chnl->next;
344f90cf607SDaniel W. S. Almeida 	}
345f90cf607SDaniel W. S. Almeida 
346f90cf607SDaniel W. S. Almeida 	return nbytes;
347f90cf607SDaniel W. S. Almeida }
348f90cf607SDaniel W. S. Almeida 
vidtv_mux_pad_with_nulls(struct vidtv_mux * m,u32 npkts)349f90cf607SDaniel W. S. Almeida static u32 vidtv_mux_pad_with_nulls(struct vidtv_mux *m, u32 npkts)
350f90cf607SDaniel W. S. Almeida {
351ec3eda53SMauro Carvalho Chehab 	struct null_packet_write_args args = {
352ec3eda53SMauro Carvalho Chehab 		.dest_buf           = m->mux_buf,
353ec3eda53SMauro Carvalho Chehab 		.buf_sz             = m->mux_buf_sz,
354ec3eda53SMauro Carvalho Chehab 		.dest_offset        = m->mux_buf_offset,
355ec3eda53SMauro Carvalho Chehab 	};
356f90cf607SDaniel W. S. Almeida 	u32 initial_offset = m->mux_buf_offset;
357f90cf607SDaniel W. S. Almeida 	struct vidtv_mux_pid_ctx *ctx;
358a8bd461cSMauro Carvalho Chehab 	u32 nbytes;
359a8bd461cSMauro Carvalho Chehab 	u32 i;
360f90cf607SDaniel W. S. Almeida 
361f90cf607SDaniel W. S. Almeida 	ctx = vidtv_mux_get_pid_ctx(m, TS_NULL_PACKET_PID);
362f90cf607SDaniel W. S. Almeida 
363f90cf607SDaniel W. S. Almeida 	args.continuity_counter = &ctx->cc;
364f90cf607SDaniel W. S. Almeida 
365f90cf607SDaniel W. S. Almeida 	for (i = 0; i < npkts; ++i) {
366f90cf607SDaniel W. S. Almeida 		m->mux_buf_offset += vidtv_ts_null_write_into(args);
367f90cf607SDaniel W. S. Almeida 		args.dest_offset  = m->mux_buf_offset;
368f90cf607SDaniel W. S. Almeida 	}
369f90cf607SDaniel W. S. Almeida 
370f90cf607SDaniel W. S. Almeida 	nbytes = m->mux_buf_offset - initial_offset;
371f90cf607SDaniel W. S. Almeida 
372f90cf607SDaniel W. S. Almeida 	/* sanity check */
373f90cf607SDaniel W. S. Almeida 	if (nbytes != npkts * TS_PACKET_LEN)
3749cfb4d36SMauro Carvalho Chehab 		dev_err_ratelimited(m->dev, "%d != %d\n",
3759cfb4d36SMauro Carvalho Chehab 				    nbytes, npkts * TS_PACKET_LEN);
376f90cf607SDaniel W. S. Almeida 
377f90cf607SDaniel W. S. Almeida 	return nbytes;
378f90cf607SDaniel W. S. Almeida }
379f90cf607SDaniel W. S. Almeida 
vidtv_mux_clear(struct vidtv_mux * m)380f90cf607SDaniel W. S. Almeida static void vidtv_mux_clear(struct vidtv_mux *m)
381f90cf607SDaniel W. S. Almeida {
382f90cf607SDaniel W. S. Almeida 	/* clear the packets currently in the mux */
383f90cf607SDaniel W. S. Almeida 	memset(m->mux_buf, 0, m->mux_buf_sz * sizeof(*m->mux_buf));
384f90cf607SDaniel W. S. Almeida 	/* point to the beginning of the buffer again */
385f90cf607SDaniel W. S. Almeida 	m->mux_buf_offset = 0;
386f90cf607SDaniel W. S. Almeida }
387f90cf607SDaniel W. S. Almeida 
38882d00a1aSMauro Carvalho Chehab #define ERR_RATE 10000000
vidtv_mux_tick(struct work_struct * work)389f90cf607SDaniel W. S. Almeida static void vidtv_mux_tick(struct work_struct *work)
390f90cf607SDaniel W. S. Almeida {
391f90cf607SDaniel W. S. Almeida 	struct vidtv_mux *m = container_of(work,
392f90cf607SDaniel W. S. Almeida 					   struct vidtv_mux,
393f90cf607SDaniel W. S. Almeida 					   mpeg_thread);
3949cf8572dSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &m->fe->dtv_property_cache;
395a8bd461cSMauro Carvalho Chehab 	u32 tot_bits = 0;
396f90cf607SDaniel W. S. Almeida 	u32 nbytes;
397f90cf607SDaniel W. S. Almeida 	u32 npkts;
398f90cf607SDaniel W. S. Almeida 
399f90cf607SDaniel W. S. Almeida 	while (m->streaming) {
400f90cf607SDaniel W. S. Almeida 		nbytes = 0;
401f90cf607SDaniel W. S. Almeida 
402f90cf607SDaniel W. S. Almeida 		vidtv_mux_update_clk(m);
403f90cf607SDaniel W. S. Almeida 
404f90cf607SDaniel W. S. Almeida 		if (vidtv_mux_should_push_pcr(m))
405f90cf607SDaniel W. S. Almeida 			nbytes += vidtv_mux_push_pcr(m);
406f90cf607SDaniel W. S. Almeida 
407f90cf607SDaniel W. S. Almeida 		if (vidtv_mux_should_push_si(m))
408f90cf607SDaniel W. S. Almeida 			nbytes += vidtv_mux_push_si(m);
409f90cf607SDaniel W. S. Almeida 
410f90cf607SDaniel W. S. Almeida 		nbytes += vidtv_mux_poll_encoders(m);
41137b288f5SDaniel W. S. Almeida 		nbytes += vidtv_mux_pad_with_nulls(m, 256);
412f90cf607SDaniel W. S. Almeida 
413f90cf607SDaniel W. S. Almeida 		npkts = nbytes / TS_PACKET_LEN;
414f90cf607SDaniel W. S. Almeida 
415f90cf607SDaniel W. S. Almeida 		/* if the buffer is not aligned there is a bug somewhere */
416f90cf607SDaniel W. S. Almeida 		if (nbytes % TS_PACKET_LEN)
4179cfb4d36SMauro Carvalho Chehab 			dev_err_ratelimited(m->dev, "Misaligned buffer\n");
418f90cf607SDaniel W. S. Almeida 
419f90cf607SDaniel W. S. Almeida 		if (m->on_new_packets_available_cb)
420f90cf607SDaniel W. S. Almeida 			m->on_new_packets_available_cb(m->priv,
421f90cf607SDaniel W. S. Almeida 						       m->mux_buf,
422f90cf607SDaniel W. S. Almeida 						       npkts);
423f90cf607SDaniel W. S. Almeida 
424f90cf607SDaniel W. S. Almeida 		vidtv_mux_clear(m);
425f90cf607SDaniel W. S. Almeida 
4269cf8572dSMauro Carvalho Chehab 		/*
4279cf8572dSMauro Carvalho Chehab 		 * Update bytes and packet counts at DVBv5 stats
4289cf8572dSMauro Carvalho Chehab 		 *
4299cf8572dSMauro Carvalho Chehab 		 * For now, both pre and post bit counts are identical,
4309cf8572dSMauro Carvalho Chehab 		 * but post BER count can be lower than pre BER, if the error
4319cf8572dSMauro Carvalho Chehab 		 * correction logic discards packages.
4329cf8572dSMauro Carvalho Chehab 		 */
43382d00a1aSMauro Carvalho Chehab 		c->pre_bit_count.stat[0].uvalue = nbytes * 8;
43482d00a1aSMauro Carvalho Chehab 		c->post_bit_count.stat[0].uvalue = nbytes * 8;
4359cf8572dSMauro Carvalho Chehab 		c->block_count.stat[0].uvalue += npkts;
4369cf8572dSMauro Carvalho Chehab 
43782d00a1aSMauro Carvalho Chehab 		/*
43882d00a1aSMauro Carvalho Chehab 		 * Even without any visible errors for the user, the pre-BER
43982d00a1aSMauro Carvalho Chehab 		 * stats usually have an error range up to 1E-6. So,
44082d00a1aSMauro Carvalho Chehab 		 * add some random error increment count to it.
44182d00a1aSMauro Carvalho Chehab 		 *
44282d00a1aSMauro Carvalho Chehab 		 * Please notice that this is a poor guy's implementation,
44382d00a1aSMauro Carvalho Chehab 		 * as it will produce one corrected bit error every time
44482d00a1aSMauro Carvalho Chehab 		 * ceil(total bytes / ERR_RATE) is incremented, without
44582d00a1aSMauro Carvalho Chehab 		 * any sort of (pseudo-)randomness.
44682d00a1aSMauro Carvalho Chehab 		 */
44782d00a1aSMauro Carvalho Chehab 		tot_bits += nbytes * 8;
44882d00a1aSMauro Carvalho Chehab 		if (tot_bits > ERR_RATE) {
44982d00a1aSMauro Carvalho Chehab 			c->pre_bit_error.stat[0].uvalue++;
45082d00a1aSMauro Carvalho Chehab 			tot_bits -= ERR_RATE;
45182d00a1aSMauro Carvalho Chehab 		}
45282d00a1aSMauro Carvalho Chehab 
453f90cf607SDaniel W. S. Almeida 		usleep_range(VIDTV_SLEEP_USECS, VIDTV_MAX_SLEEP_USECS);
454f90cf607SDaniel W. S. Almeida 	}
455f90cf607SDaniel W. S. Almeida }
456f90cf607SDaniel W. S. Almeida 
vidtv_mux_start_thread(struct vidtv_mux * m)457f90cf607SDaniel W. S. Almeida void vidtv_mux_start_thread(struct vidtv_mux *m)
458f90cf607SDaniel W. S. Almeida {
459f90cf607SDaniel W. S. Almeida 	if (m->streaming) {
4609cfb4d36SMauro Carvalho Chehab 		dev_warn_ratelimited(m->dev, "Already streaming. Skipping.\n");
461f90cf607SDaniel W. S. Almeida 		return;
462f90cf607SDaniel W. S. Almeida 	}
463f90cf607SDaniel W. S. Almeida 
464f90cf607SDaniel W. S. Almeida 	m->streaming = true;
465f90cf607SDaniel W. S. Almeida 	m->timing.start_jiffies = get_jiffies_64();
466f90cf607SDaniel W. S. Almeida 	schedule_work(&m->mpeg_thread);
467f90cf607SDaniel W. S. Almeida }
468f90cf607SDaniel W. S. Almeida 
vidtv_mux_stop_thread(struct vidtv_mux * m)469f90cf607SDaniel W. S. Almeida void vidtv_mux_stop_thread(struct vidtv_mux *m)
470f90cf607SDaniel W. S. Almeida {
471f90cf607SDaniel W. S. Almeida 	if (m->streaming) {
472f90cf607SDaniel W. S. Almeida 		m->streaming = false; /* thread will quit */
473f90cf607SDaniel W. S. Almeida 		cancel_work_sync(&m->mpeg_thread);
474f90cf607SDaniel W. S. Almeida 	}
475f90cf607SDaniel W. S. Almeida }
476f90cf607SDaniel W. S. Almeida 
vidtv_mux_init(struct dvb_frontend * fe,struct device * dev,struct vidtv_mux_init_args * args)4779cf8572dSMauro Carvalho Chehab struct vidtv_mux *vidtv_mux_init(struct dvb_frontend *fe,
4789cf8572dSMauro Carvalho Chehab 				 struct device *dev,
4790a33ab16SMauro Carvalho Chehab 				 struct vidtv_mux_init_args *args)
480f90cf607SDaniel W. S. Almeida {
4813be80379SMauro Carvalho Chehab 	struct vidtv_mux *m;
4823be80379SMauro Carvalho Chehab 
4833be80379SMauro Carvalho Chehab 	m = kzalloc(sizeof(*m), GFP_KERNEL);
4843be80379SMauro Carvalho Chehab 	if (!m)
4853be80379SMauro Carvalho Chehab 		return NULL;
486f90cf607SDaniel W. S. Almeida 
4879cfb4d36SMauro Carvalho Chehab 	m->dev = dev;
4889cf8572dSMauro Carvalho Chehab 	m->fe = fe;
4890a33ab16SMauro Carvalho Chehab 	m->timing.pcr_period_usecs = args->pcr_period_usecs;
4900a33ab16SMauro Carvalho Chehab 	m->timing.si_period_usecs  = args->si_period_usecs;
491f90cf607SDaniel W. S. Almeida 
4920a33ab16SMauro Carvalho Chehab 	m->mux_rate_kbytes_sec = args->mux_rate_kbytes_sec;
493f90cf607SDaniel W. S. Almeida 
4940a33ab16SMauro Carvalho Chehab 	m->on_new_packets_available_cb = args->on_new_packets_available_cb;
495f90cf607SDaniel W. S. Almeida 
4960a33ab16SMauro Carvalho Chehab 	m->mux_buf = vzalloc(args->mux_buf_sz);
4973be80379SMauro Carvalho Chehab 	if (!m->mux_buf)
4983be80379SMauro Carvalho Chehab 		goto free_mux;
4993be80379SMauro Carvalho Chehab 
5000a33ab16SMauro Carvalho Chehab 	m->mux_buf_sz = args->mux_buf_sz;
501f90cf607SDaniel W. S. Almeida 
5020a33ab16SMauro Carvalho Chehab 	m->pcr_pid = args->pcr_pid;
5030a33ab16SMauro Carvalho Chehab 	m->transport_stream_id = args->transport_stream_id;
5040a33ab16SMauro Carvalho Chehab 	m->priv = args->priv;
5050a33ab16SMauro Carvalho Chehab 	m->network_id = args->network_id;
5060a33ab16SMauro Carvalho Chehab 	m->network_name = kstrdup(args->network_name, GFP_KERNEL);
507*aae7598aSJiasheng Jiang 	if (!m->network_name)
508*aae7598aSJiasheng Jiang 		goto free_mux_buf;
509*aae7598aSJiasheng Jiang 
510880a8fc0SMauro Carvalho Chehab 	m->timing.current_jiffies = get_jiffies_64();
511f90cf607SDaniel W. S. Almeida 
5120a33ab16SMauro Carvalho Chehab 	if (args->channels)
5130a33ab16SMauro Carvalho Chehab 		m->channels = args->channels;
514f90cf607SDaniel W. S. Almeida 	else
5153be80379SMauro Carvalho Chehab 		if (vidtv_channels_init(m) < 0)
516*aae7598aSJiasheng Jiang 			goto free_mux_network_name;
517f90cf607SDaniel W. S. Almeida 
518f90cf607SDaniel W. S. Almeida 	/* will alloc data for pmt_sections after initializing pat */
5193be80379SMauro Carvalho Chehab 	if (vidtv_channel_si_init(m) < 0)
5203be80379SMauro Carvalho Chehab 		goto free_channels;
521f90cf607SDaniel W. S. Almeida 
522f90cf607SDaniel W. S. Almeida 	INIT_WORK(&m->mpeg_thread, vidtv_mux_tick);
523f90cf607SDaniel W. S. Almeida 
5243be80379SMauro Carvalho Chehab 	if (vidtv_mux_pid_ctx_init(m) < 0)
5253be80379SMauro Carvalho Chehab 		goto free_channel_si;
526f90cf607SDaniel W. S. Almeida 
527f90cf607SDaniel W. S. Almeida 	return m;
5283be80379SMauro Carvalho Chehab 
5293be80379SMauro Carvalho Chehab free_channel_si:
5303be80379SMauro Carvalho Chehab 	vidtv_channel_si_destroy(m);
5313be80379SMauro Carvalho Chehab free_channels:
5323be80379SMauro Carvalho Chehab 	vidtv_channels_destroy(m);
533*aae7598aSJiasheng Jiang free_mux_network_name:
534*aae7598aSJiasheng Jiang 	kfree(m->network_name);
5353be80379SMauro Carvalho Chehab free_mux_buf:
5363be80379SMauro Carvalho Chehab 	vfree(m->mux_buf);
5373be80379SMauro Carvalho Chehab free_mux:
5383be80379SMauro Carvalho Chehab 	kfree(m);
5393be80379SMauro Carvalho Chehab 	return NULL;
540f90cf607SDaniel W. S. Almeida }
541f90cf607SDaniel W. S. Almeida 
vidtv_mux_destroy(struct vidtv_mux * m)542f90cf607SDaniel W. S. Almeida void vidtv_mux_destroy(struct vidtv_mux *m)
543f90cf607SDaniel W. S. Almeida {
544f90cf607SDaniel W. S. Almeida 	vidtv_mux_stop_thread(m);
545f90cf607SDaniel W. S. Almeida 	vidtv_mux_pid_ctx_destroy(m);
546f90cf607SDaniel W. S. Almeida 	vidtv_channel_si_destroy(m);
547f90cf607SDaniel W. S. Almeida 	vidtv_channels_destroy(m);
548c2f78f0cSDaniel W. S. Almeida 	kfree(m->network_name);
549f90cf607SDaniel W. S. Almeida 	vfree(m->mux_buf);
550f90cf607SDaniel W. S. Almeida 	kfree(m);
551f90cf607SDaniel W. S. Almeida }
552