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  */
24 void
25 v4l2_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 }
83 EXPORT_SYMBOL_GPL(v4l2_h264_init_reflist_builder);
84 
85 static 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 
119 static 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 
164 static 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  */
221 void
222 v4l2_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 }
230 EXPORT_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  */
246 void
247 v4l2_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 }
264 EXPORT_SYMBOL_GPL(v4l2_h264_build_b_ref_lists);
265 
266 MODULE_LICENSE("GPL");
267 MODULE_DESCRIPTION("V4L2 H264 Helpers");
268 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@collabora.com>");
269