1// SPDX-License-Identifier: GPL-2.0
2/*
3 * V4L2 H264 helpers.
4 *
5 * Copyright (C) 2019 Collabora, Ltd.
6 *
7 * Author: Boris Brezillon <boris.brezillon@collabora.com>
8 */
9
10#include <linux/module.h>
11#include <linux/sort.h>
12
13#include <media/v4l2-h264.h>
14
15/**
16 * v4l2_h264_init_reflist_builder() - Initialize a P/B0/B1 reference list
17 *				      builder
18 *
19 * @b: the builder context to initialize
20 * @dec_params: decode parameters control
21 * @sps: SPS control
22 * @dpb: DPB to use when creating the reference list
23 */
24void
25v4l2_h264_init_reflist_builder(struct v4l2_h264_reflist_builder *b,
26		const struct v4l2_ctrl_h264_decode_params *dec_params,
27		const struct v4l2_ctrl_h264_sps *sps,
28		const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES])
29{
30	int cur_frame_num, max_frame_num;
31	unsigned int i;
32
33	max_frame_num = 1 << (sps->log2_max_frame_num_minus4 + 4);
34	cur_frame_num = dec_params->frame_num;
35
36	memset(b, 0, sizeof(*b));
37	if (!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC))
38		b->cur_pic_order_count = min(dec_params->bottom_field_order_cnt,
39					     dec_params->top_field_order_cnt);
40	else if (dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)
41		b->cur_pic_order_count = dec_params->bottom_field_order_cnt;
42	else
43		b->cur_pic_order_count = dec_params->top_field_order_cnt;
44
45	for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) {
46		u32 pic_order_count;
47
48		if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
49			continue;
50
51		b->refs[i].pic_num = dpb[i].pic_num;
52		if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
53			b->refs[i].longterm = true;
54
55		/*
56		 * Handle frame_num wraparound as described in section
57		 * '8.2.4.1 Decoding process for picture numbers' of the spec.
58		 * TODO: This logic will have to be adjusted when we start
59		 * supporting interlaced content.
60		 */
61		if (dpb[i].frame_num > cur_frame_num)
62			b->refs[i].frame_num = (int)dpb[i].frame_num -
63					       max_frame_num;
64		else
65			b->refs[i].frame_num = dpb[i].frame_num;
66
67		if (dpb[i].fields == V4L2_H264_FRAME_REF)
68			pic_order_count = min(dpb[i].top_field_order_cnt,
69					      dpb[i].bottom_field_order_cnt);
70		else if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)
71			pic_order_count = dpb[i].bottom_field_order_cnt;
72		else
73			pic_order_count = dpb[i].top_field_order_cnt;
74
75		b->refs[i].pic_order_count = pic_order_count;
76		b->unordered_reflist[b->num_valid] = i;
77		b->num_valid++;
78	}
79
80	for (i = b->num_valid; i < ARRAY_SIZE(b->unordered_reflist); i++)
81		b->unordered_reflist[i] = i;
82}
83EXPORT_SYMBOL_GPL(v4l2_h264_init_reflist_builder);
84
85static int v4l2_h264_p_ref_list_cmp(const void *ptra, const void *ptrb,
86				    const void *data)
87{
88	const struct v4l2_h264_reflist_builder *builder = data;
89	u8 idxa, idxb;
90
91	idxa = *((u8 *)ptra);
92	idxb = *((u8 *)ptrb);
93
94	if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES ||
95		    idxb >= V4L2_H264_NUM_DPB_ENTRIES))
96		return 1;
97
98	if (builder->refs[idxa].longterm != builder->refs[idxb].longterm) {
99		/* Short term pics first. */
100		if (!builder->refs[idxa].longterm)
101			return -1;
102		else
103			return 1;
104	}
105
106	/*
107	 * Short term pics in descending pic num order, long term ones in
108	 * ascending order.
109	 */
110	if (!builder->refs[idxa].longterm)
111		return builder->refs[idxb].frame_num <
112		       builder->refs[idxa].frame_num ?
113		       -1 : 1;
114
115	return builder->refs[idxa].pic_num < builder->refs[idxb].pic_num ?
116	       -1 : 1;
117}
118
119static int v4l2_h264_b0_ref_list_cmp(const void *ptra, const void *ptrb,
120				     const void *data)
121{
122	const struct v4l2_h264_reflist_builder *builder = data;
123	s32 poca, pocb;
124	u8 idxa, idxb;
125
126	idxa = *((u8 *)ptra);
127	idxb = *((u8 *)ptrb);
128
129	if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES ||
130		    idxb >= V4L2_H264_NUM_DPB_ENTRIES))
131		return 1;
132
133	if (builder->refs[idxa].longterm != builder->refs[idxb].longterm) {
134		/* Short term pics first. */
135		if (!builder->refs[idxa].longterm)
136			return -1;
137		else
138			return 1;
139	}
140
141	/* Long term pics in ascending pic num order. */
142	if (builder->refs[idxa].longterm)
143		return builder->refs[idxa].pic_num <
144		       builder->refs[idxb].pic_num ?
145		       -1 : 1;
146
147	poca = builder->refs[idxa].pic_order_count;
148	pocb = builder->refs[idxb].pic_order_count;
149
150	/*
151	 * Short term pics with POC < cur POC first in POC descending order
152	 * followed by short term pics with POC > cur POC in POC ascending
153	 * order.
154	 */
155	if ((poca < builder->cur_pic_order_count) !=
156	     (pocb < builder->cur_pic_order_count))
157		return poca < pocb ? -1 : 1;
158	else if (poca < builder->cur_pic_order_count)
159		return pocb < poca ? -1 : 1;
160
161	return poca < pocb ? -1 : 1;
162}
163
164static int v4l2_h264_b1_ref_list_cmp(const void *ptra, const void *ptrb,
165				     const void *data)
166{
167	const struct v4l2_h264_reflist_builder *builder = data;
168	s32 poca, pocb;
169	u8 idxa, idxb;
170
171	idxa = *((u8 *)ptra);
172	idxb = *((u8 *)ptrb);
173
174	if (WARN_ON(idxa >= V4L2_H264_NUM_DPB_ENTRIES ||
175		    idxb >= V4L2_H264_NUM_DPB_ENTRIES))
176		return 1;
177
178	if (builder->refs[idxa].longterm != builder->refs[idxb].longterm) {
179		/* Short term pics first. */
180		if (!builder->refs[idxa].longterm)
181			return -1;
182		else
183			return 1;
184	}
185
186	/* Long term pics in ascending pic num order. */
187	if (builder->refs[idxa].longterm)
188		return builder->refs[idxa].pic_num <
189		       builder->refs[idxb].pic_num ?
190		       -1 : 1;
191
192	poca = builder->refs[idxa].pic_order_count;
193	pocb = builder->refs[idxb].pic_order_count;
194
195	/*
196	 * Short term pics with POC > cur POC first in POC ascending order
197	 * followed by short term pics with POC < cur POC in POC descending
198	 * order.
199	 */
200	if ((poca < builder->cur_pic_order_count) !=
201	    (pocb < builder->cur_pic_order_count))
202		return pocb < poca ? -1 : 1;
203	else if (poca < builder->cur_pic_order_count)
204		return pocb < poca ? -1 : 1;
205
206	return poca < pocb ? -1 : 1;
207}
208
209/**
210 * v4l2_h264_build_p_ref_list() - Build the P reference list
211 *
212 * @builder: reference list builder context
213 * @reflist: 16-bytes array used to store the P reference list. Each entry
214 *	     is an index in the DPB
215 *
216 * This functions builds the P reference lists. This procedure is describe in
217 * section '8.2.4 Decoding process for reference picture lists construction'
218 * of the H264 spec. This function can be used by H264 decoder drivers that
219 * need to pass a P reference list to the hardware.
220 */
221void
222v4l2_h264_build_p_ref_list(const struct v4l2_h264_reflist_builder *builder,
223			   u8 *reflist)
224{
225	memcpy(reflist, builder->unordered_reflist,
226	       sizeof(builder->unordered_reflist[0]) * builder->num_valid);
227	sort_r(reflist, builder->num_valid, sizeof(*reflist),
228	       v4l2_h264_p_ref_list_cmp, NULL, builder);
229}
230EXPORT_SYMBOL_GPL(v4l2_h264_build_p_ref_list);
231
232/**
233 * v4l2_h264_build_b_ref_lists() - Build the B0/B1 reference lists
234 *
235 * @builder: reference list builder context
236 * @b0_reflist: 16-bytes array used to store the B0 reference list. Each entry
237 *		is an index in the DPB
238 * @b1_reflist: 16-bytes array used to store the B1 reference list. Each entry
239 *		is an index in the DPB
240 *
241 * This functions builds the B0/B1 reference lists. This procedure is described
242 * in section '8.2.4 Decoding process for reference picture lists construction'
243 * of the H264 spec. This function can be used by H264 decoder drivers that
244 * need to pass B0/B1 reference lists to the hardware.
245 */
246void
247v4l2_h264_build_b_ref_lists(const struct v4l2_h264_reflist_builder *builder,
248			    u8 *b0_reflist, u8 *b1_reflist)
249{
250	memcpy(b0_reflist, builder->unordered_reflist,
251	       sizeof(builder->unordered_reflist[0]) * builder->num_valid);
252	sort_r(b0_reflist, builder->num_valid, sizeof(*b0_reflist),
253	       v4l2_h264_b0_ref_list_cmp, NULL, builder);
254
255	memcpy(b1_reflist, builder->unordered_reflist,
256	       sizeof(builder->unordered_reflist[0]) * builder->num_valid);
257	sort_r(b1_reflist, builder->num_valid, sizeof(*b1_reflist),
258	       v4l2_h264_b1_ref_list_cmp, NULL, builder);
259
260	if (builder->num_valid > 1 &&
261	    !memcmp(b1_reflist, b0_reflist, builder->num_valid))
262		swap(b1_reflist[0], b1_reflist[1]);
263}
264EXPORT_SYMBOL_GPL(v4l2_h264_build_b_ref_lists);
265
266MODULE_LICENSE("GPL");
267MODULE_DESCRIPTION("V4L2 H264 Helpers");
268MODULE_AUTHOR("Boris Brezillon <boris.brezillon@collabora.com>");
269