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