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
v4l2_h264_init_reflist_builder(struct v4l2_h264_reflist_builder * b,const struct v4l2_ctrl_h264_decode_params * dec_params,const struct v4l2_ctrl_h264_sps * sps,const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES])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
v4l2_h264_p_ref_list_cmp(const void * ptra,const void * ptrb,const void * data)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
v4l2_h264_b0_ref_list_cmp(const void * ptra,const void * ptrb,const void * data)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
v4l2_h264_b1_ref_list_cmp(const void * ptra,const void * ptrb,const void * data)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
v4l2_h264_build_p_ref_list(const struct v4l2_h264_reflist_builder * builder,u8 * reflist)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
v4l2_h264_build_b_ref_lists(const struct v4l2_h264_reflist_builder * builder,u8 * b0_reflist,u8 * b1_reflist)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