xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_hdmi.c (revision 740b6d65d0be42fe12408cad0f55cfe8cac1007e)
1 /*	$NetBSD: linux_hdmi.c,v 1.10 2022/07/10 13:56:44 riastradh Exp $	*/
2 
3 /*-
4  * Copyright (c) 2014 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Taylor R. Campbell.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: linux_hdmi.c,v 1.10 2022/07/10 13:56:44 riastradh Exp $");
34 
35 #include <sys/types.h>
36 
37 #include <sys/device.h>
38 #include <sys/errno.h>
39 #include <sys/systm.h>
40 
41 #include <lib/libkern/libkern.h>
42 
43 #include <linux/hdmi.h>
44 
45 /* Infoframe headers */
46 
47 static void
hdmi_infoframe_header_init(struct hdmi_infoframe_header * header,enum hdmi_infoframe_type type,uint8_t vers,uint8_t length)48 hdmi_infoframe_header_init(struct hdmi_infoframe_header *header,
49     enum hdmi_infoframe_type type, uint8_t vers, uint8_t length)
50 {
51 
52 	header->type = type;
53 	header->version = vers;
54 	header->length = length;
55 }
56 
57 static int
hdmi_infoframe_header_check(const struct hdmi_infoframe_header * header,enum hdmi_infoframe_type type,uint8_t vers,uint8_t length)58 hdmi_infoframe_header_check(const struct hdmi_infoframe_header *header,
59     enum hdmi_infoframe_type type, uint8_t vers, uint8_t length)
60 {
61 
62 	if (header->type != type ||
63 	    header->version != vers ||
64 	    header->length != length)
65 		return -EINVAL;
66 	return 0;
67 }
68 
69 static ssize_t
hdmi_infoframe_header_pack(const struct hdmi_infoframe_header * header,uint8_t length,void * buf,size_t size)70 hdmi_infoframe_header_pack(const struct hdmi_infoframe_header *header,
71     uint8_t length, void *buf, size_t size)
72 {
73 	uint8_t *const p = buf;
74 
75 	KASSERT(length >= HDMI_INFOFRAME_HEADER_SIZE);
76 
77 	if (size < length)
78 		return -ENOSPC;
79 
80 	p[0] = header->type;
81 	p[1] = header->version;
82 	p[2] = (length - HDMI_INFOFRAME_HEADER_SIZE);
83 	p[3] = 0;		/* checksum */
84 
85 	return HDMI_INFOFRAME_HEADER_SIZE;
86 }
87 
88 static uint8_t
hdmi_infoframe_checksum(const void * buf,size_t length)89 hdmi_infoframe_checksum(const void *buf, size_t length)
90 {
91 	const uint8_t *p = buf;
92 	uint8_t checksum = 0;
93 
94 	while (length--)
95 		checksum += *p++;
96 
97 	return 256 - checksum;
98 }
99 
100 static int
hdmi_infoframe_header_unpack(struct hdmi_infoframe_header * header,const void * buf,size_t size)101 hdmi_infoframe_header_unpack(struct hdmi_infoframe_header *header,
102     const void *buf, size_t size)
103 {
104 	const uint8_t *const p = buf;
105 
106 	if (size < HDMI_INFOFRAME_HEADER_SIZE)
107 		return -EINVAL;
108 	if (p[2] > size - HDMI_INFOFRAME_HEADER_SIZE)
109 		return -EINVAL;
110 	if (hdmi_infoframe_checksum(buf, p[2] + HDMI_INFOFRAME_HEADER_SIZE))
111 		return -EINVAL;
112 
113 	hdmi_infoframe_header_init(header, p[0], p[1], p[2]);
114 	return 0;
115 }
116 
117 static void
hdmi_infoframe_set_checksum(void * buf,size_t length)118 hdmi_infoframe_set_checksum(void *buf, size_t length)
119 {
120 	uint8_t *p = buf;
121 
122 	p[3] = hdmi_infoframe_checksum(buf, length);
123 }
124 
125 /* Audio infoframes */
126 
127 int
hdmi_audio_infoframe_init(struct hdmi_audio_infoframe * frame)128 hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame)
129 {
130 	static const struct hdmi_audio_infoframe zero_frame;
131 
132 	*frame = zero_frame;
133 
134 	hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_AUDIO,
135 	    1, HDMI_AUDIO_INFOFRAME_SIZE);
136 
137 	return 0;
138 }
139 
140 ssize_t
hdmi_audio_infoframe_pack(const struct hdmi_audio_infoframe * frame,void * buf,size_t size)141 hdmi_audio_infoframe_pack(const struct hdmi_audio_infoframe *frame, void *buf,
142     size_t size)
143 {
144 	const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
145 	    HDMI_AUDIO_INFOFRAME_SIZE;
146 	uint8_t channels = 0;
147 	uint8_t *p = buf;
148 	int ret;
149 
150 	KASSERT(frame->header.length == HDMI_AUDIO_INFOFRAME_SIZE);
151 
152 	ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
153 	if (ret < 0)
154 		return ret;
155 	KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
156 	p += HDMI_INFOFRAME_HEADER_SIZE;
157 	size -= HDMI_INFOFRAME_HEADER_SIZE;
158 
159 	if (frame->channels >= 2)
160 		channels = frame->channels - 1;
161 
162 	p[0] = __SHIFTIN(frame->coding_type, __BITS(7,4));
163 	p[0] |= __SHIFTIN(channels, __BITS(2,0));
164 
165 	p[1] = __SHIFTIN(frame->sample_frequency, __BITS(4,2));
166 	p[1] |= __SHIFTIN(frame->sample_size, __BITS(1,0));
167 
168 	p[2] = __SHIFTIN(frame->coding_type_ext, __BITS(5,0));
169 
170 	p[3] = __SHIFTIN(frame->level_shift_value, __BITS(6,3));
171 
172 	p[4] = __SHIFTIN(frame->downmix_inhibit? 1 : 0, __BIT(7));
173 
174 	/* PB6 to PB10 are reserved */
175 	p[5] = 0;
176 	p[6] = 0;
177 	p[7] = 0;
178 	p[8] = 0;
179 	p[9] = 0;
180 
181 	CTASSERT(HDMI_AUDIO_INFOFRAME_SIZE == 10);
182 
183 	hdmi_infoframe_set_checksum(buf, length);
184 
185 	return length;
186 }
187 
188 static int
hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe * frame,const void * buf,size_t size)189 hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame,
190     const void *buf, size_t size)
191 {
192 	const uint8_t *p = buf;
193 	int ret;
194 
195 	ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
196 	if (ret)
197 		return ret;
198 	if (frame->header.length != HDMI_AUDIO_INFOFRAME_SIZE)
199 		return -EINVAL;
200 	p += HDMI_INFOFRAME_HEADER_SIZE;
201 	size -= HDMI_INFOFRAME_HEADER_SIZE;
202 
203 	frame->coding_type = __SHIFTOUT(p[0], __BITS(7,4));
204 	frame->channels = __SHIFTOUT(p[0], __BITS(2,0));
205 
206 	frame->sample_frequency = __SHIFTOUT(p[1], __BITS(4,2));
207 	frame->sample_size = __SHIFTOUT(p[1], __BITS(1,0));
208 
209 	frame->coding_type_ext = __SHIFTOUT(p[2], __BITS(5,0));
210 
211 	frame->level_shift_value = __SHIFTOUT(p[3], __BITS(6,3));
212 
213 	frame->downmix_inhibit = __SHIFTOUT(p[4], __BIT(7));
214 
215 	return 0;
216 }
217 
218 /* AVI infoframes */
219 
220 int
hdmi_avi_infoframe_init(struct hdmi_avi_infoframe * frame)221 hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame)
222 {
223 	static const struct hdmi_avi_infoframe zero_frame;
224 
225 	*frame = zero_frame;
226 
227 	hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_AVI, 2,
228 	    HDMI_AVI_INFOFRAME_SIZE);
229 
230 	return 0;
231 }
232 
233 int
hdmi_avi_infoframe_check(const struct hdmi_avi_infoframe * frame)234 hdmi_avi_infoframe_check(const struct hdmi_avi_infoframe *frame)
235 {
236 	int ret;
237 
238 	ret = hdmi_infoframe_header_check(&frame->header,
239 	    HDMI_INFOFRAME_TYPE_AVI, 2, HDMI_AVI_INFOFRAME_SIZE);
240 	if (ret)
241 		return ret;
242 
243 	return 0;
244 }
245 
246 ssize_t
hdmi_avi_infoframe_pack(const struct hdmi_avi_infoframe * frame,void * buf,size_t size)247 hdmi_avi_infoframe_pack(const struct hdmi_avi_infoframe *frame, void *buf,
248     size_t size)
249 {
250 	const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
251 	    HDMI_AVI_INFOFRAME_SIZE;
252 	uint8_t *p = buf;
253 	int ret;
254 
255 	KASSERT(frame->header.length == HDMI_AVI_INFOFRAME_SIZE);
256 
257 	ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
258 	if (ret < 0)
259 		return ret;
260 	KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
261 	p += HDMI_INFOFRAME_HEADER_SIZE;
262 	size -= HDMI_INFOFRAME_HEADER_SIZE;
263 
264 	p[0] = __SHIFTIN(frame->colorspace, __BITS(6,5));
265 	p[0] |= __SHIFTIN(frame->active_aspect & 0xf? 1 : 0, __BIT(4));
266 	p[0] |= __SHIFTIN(frame->top_bar || frame->bottom_bar, __BIT(3));
267 	p[0] |= __SHIFTIN(frame->left_bar || frame->right_bar, __BIT(2));
268 	p[0] |= __SHIFTIN(frame->scan_mode, __BITS(1,0));
269 
270 	p[1] = __SHIFTIN(frame->colorimetry, __BITS(7,6));
271 	p[1] |= __SHIFTIN(frame->picture_aspect, __BITS(5,4));
272 	p[1] |= __SHIFTIN(frame->active_aspect, __BITS(3,0));
273 
274 	p[2] = __SHIFTIN(frame->itc? 1 : 0, __BIT(7));
275 	p[2] |= __SHIFTIN(frame->extended_colorimetry, __BITS(6,4));
276 	p[2] |= __SHIFTIN(frame->quantization_range, __BITS(3,2));
277 	p[2] |= __SHIFTIN(frame->nups, __BITS(1,0));
278 
279 	p[3] = frame->video_code;
280 
281 	p[4] = __SHIFTIN(frame->ycc_quantization_range, __BITS(7,6));
282 	p[4] |= __SHIFTIN(frame->content_type, __BITS(5,4));
283 	p[4] |= __SHIFTIN(frame->pixel_repeat, __BITS(3,0));
284 
285 	le16enc(&p[5], frame->top_bar);
286 	le16enc(&p[7], frame->bottom_bar);
287 	le16enc(&p[9], frame->left_bar);
288 	le16enc(&p[11], frame->right_bar);
289 	CTASSERT(HDMI_AVI_INFOFRAME_SIZE == 13);
290 
291 	hdmi_infoframe_set_checksum(buf, length);
292 
293 	return length;
294 }
295 
296 static int
hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe * frame,const void * buf,size_t size)297 hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, const void *buf,
298     size_t size)
299 {
300 	const uint8_t *p = buf;
301 	int ret;
302 
303 	ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
304 	if (ret)
305 		return ret;
306 	if (frame->header.length != HDMI_AVI_INFOFRAME_SIZE)
307 		return -EINVAL;
308 	p += HDMI_INFOFRAME_HEADER_SIZE;
309 	size -= HDMI_INFOFRAME_HEADER_SIZE;
310 
311 	frame->colorspace = __SHIFTOUT(p[0], __BITS(6,5));
312 	frame->scan_mode = __SHIFTOUT(p[0], __BITS(1,0));
313 
314 	frame->colorimetry = __SHIFTOUT(p[1], __BITS(7,6));
315 	frame->picture_aspect = __SHIFTOUT(p[1], __BITS(5,4));
316 	if (p[0] & __BIT(4))
317 		frame->active_aspect = __SHIFTOUT(p[1], __BITS(3,0));
318 
319 	frame->itc = __SHIFTOUT(p[2], __BIT(7));
320 	frame->extended_colorimetry = __SHIFTOUT(p[2], __BITS(6,4));
321 	frame->quantization_range = __SHIFTOUT(p[2], __BITS(3,2));
322 	frame->nups = __SHIFTOUT(p[2], __BITS(1,0));
323 
324 	frame->video_code = p[3];
325 
326 	frame->ycc_quantization_range = __SHIFTOUT(p[4], __BITS(7,6));
327 	frame->content_type = __SHIFTOUT(p[4], __BITS(5,4));
328 	frame->pixel_repeat = __SHIFTOUT(p[4], __BITS(3,0));
329 
330 	if (p[0] & __BIT(3)) {
331 		frame->top_bar = le16dec(&p[5]);
332 		frame->bottom_bar = le16dec(&p[7]);
333 	}
334 	if (p[0] & __BIT(2)) {
335 		frame->left_bar = le16dec(&p[9]);
336 		frame->right_bar = le16dec(&p[11]);
337 	}
338 
339 	return 0;
340 }
341 
342 /* DRM infoframes */
343 
344 int
hdmi_drm_infoframe_init(struct hdmi_drm_infoframe * frame)345 hdmi_drm_infoframe_init(struct hdmi_drm_infoframe *frame)
346 {
347 	static const struct hdmi_drm_infoframe zero_frame;
348 
349 	*frame = zero_frame;
350 
351 	hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_DRM,
352 	    1, HDMI_DRM_INFOFRAME_SIZE);
353 
354 	return 0;
355 }
356 
357 int
hdmi_drm_infoframe_check(const struct hdmi_drm_infoframe * frame)358 hdmi_drm_infoframe_check(const struct hdmi_drm_infoframe *frame)
359 {
360 	int ret;
361 
362 	ret = hdmi_infoframe_header_check(&frame->header,
363 	    HDMI_INFOFRAME_TYPE_DRM, 1, HDMI_DRM_INFOFRAME_SIZE);
364 	if (ret)
365 		return ret;
366 
367 	return 0;
368 }
369 
__strong_alias(linux_hdmi_drm_infoframe_pack_only,linux_hdmi_drm_infoframe_pack)370 __strong_alias(linux_hdmi_drm_infoframe_pack_only,linux_hdmi_drm_infoframe_pack) /* XXX */
371 
372 ssize_t
373 hdmi_drm_infoframe_pack(const struct hdmi_drm_infoframe *frame,
374     void *buf, size_t size)
375 {
376 	const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
377 	    HDMI_DRM_INFOFRAME_SIZE;
378 	uint8_t *p = buf;
379 	unsigned i;
380 	int ret;
381 
382 	KASSERT(frame->header.length == HDMI_DRM_INFOFRAME_SIZE);
383 
384 	ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
385 	if (ret < 0)
386 		return ret;
387 	KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
388 	p += HDMI_INFOFRAME_HEADER_SIZE;
389 	size -= HDMI_INFOFRAME_HEADER_SIZE;
390 
391 	p[0] = frame->eotf;
392 	p[1] = frame->metadata_type;
393 	for (i = 0; i < __arraycount(frame->display_primaries); i++) {
394 		le16enc(&p[2 + 4*i], frame->display_primaries[i].x);
395 		le16enc(&p[2 + 4*i + 2], frame->display_primaries[i].y);
396 	}
397 	le16enc(&p[14], frame->white_point.x);
398 	le16enc(&p[16], frame->white_point.y);
399 	le16enc(&p[18], frame->min_display_mastering_luminance);
400 	le16enc(&p[20], frame->max_display_mastering_luminance);
401 	le16enc(&p[22], frame->max_cll);
402 	le16enc(&p[24], frame->max_fall);
403 	CTASSERT(HDMI_DRM_INFOFRAME_SIZE == 26);
404 
405 	hdmi_infoframe_set_checksum(buf, length);
406 
407 	return length;
408 }
409 
410 static int
hdmi_drm_infoframe_unpack(struct hdmi_drm_infoframe * frame,const void * buf,size_t size)411 hdmi_drm_infoframe_unpack(struct hdmi_drm_infoframe *frame, const void *buf,
412     size_t size)
413 {
414 	const uint8_t *p = buf;
415 	unsigned i;
416 	int ret;
417 
418 	ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
419 	if (ret)
420 		return ret;
421 	if (frame->header.length != HDMI_DRM_INFOFRAME_SIZE)
422 		return -EINVAL;
423 	p += HDMI_INFOFRAME_HEADER_SIZE;
424 	size -= HDMI_INFOFRAME_HEADER_SIZE;
425 
426 	frame->eotf = p[0];
427 	frame->metadata_type = p[1];
428 	for (i = 0; i < __arraycount(frame->display_primaries); i++) {
429 		frame->display_primaries[i].x = le16dec(&p[2 + 4*i]);
430 		frame->display_primaries[i].y = le16dec(&p[2 + 4*i + 2]);
431 	}
432 	frame->white_point.x = le16dec(&p[14]);
433 	frame->white_point.y = le16dec(&p[16]);
434 	frame->min_display_mastering_luminance = le16dec(&p[18]);
435 	frame->max_display_mastering_luminance = le16dec(&p[20]);
436 	frame->max_cll = le16dec(&p[22]);
437 	frame->max_fall = le16dec(&p[24]);
438 
439 	return 0;
440 }
441 
442 /* SPD infoframes */
443 
444 int
hdmi_spd_infoframe_init(struct hdmi_spd_infoframe * frame,const char * vendor,const char * product)445 hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, const char *vendor,
446     const char *product)
447 {
448 	static const struct hdmi_spd_infoframe zero_frame;
449 
450 	*frame = zero_frame;
451 
452 	hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_SPD,
453 	    1, HDMI_SPD_INFOFRAME_SIZE);
454 
455 	strncpy(frame->vendor, vendor, sizeof(frame->vendor));
456 	strncpy(frame->product, product, sizeof(frame->product));
457 
458 	return 0;
459 }
460 
461 int
hdmi_spd_infoframe_check(const struct hdmi_spd_infoframe * frame)462 hdmi_spd_infoframe_check(const struct hdmi_spd_infoframe *frame)
463 {
464 	int ret;
465 
466 	ret = hdmi_infoframe_header_check(&frame->header,
467 	    HDMI_INFOFRAME_TYPE_SPD, 1, HDMI_SPD_INFOFRAME_SIZE);
468 	if (ret)
469 		return ret;
470 
471 	return 0;
472 }
473 
474 ssize_t
hdmi_spd_infoframe_pack(const struct hdmi_spd_infoframe * frame,void * buf,size_t size)475 hdmi_spd_infoframe_pack(const struct hdmi_spd_infoframe *frame, void *buf,
476     size_t size)
477 {
478 	const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
479 	    HDMI_SPD_INFOFRAME_SIZE;
480 	uint8_t *p = buf;
481 	int ret;
482 
483 	KASSERT(frame->header.length == HDMI_SPD_INFOFRAME_SIZE);
484 
485 	ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
486 	if (ret < 0)
487 		return ret;
488 	KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
489 	p += HDMI_INFOFRAME_HEADER_SIZE;
490 	size -= HDMI_INFOFRAME_HEADER_SIZE;
491 
492 	memcpy(&p[0], frame->vendor, 8);
493 	memcpy(&p[8], frame->product, 16);
494 	p[24] = frame->sdi;
495 	CTASSERT(HDMI_SPD_INFOFRAME_SIZE == 25);
496 
497 	hdmi_infoframe_set_checksum(buf, length);
498 
499 	return length;
500 }
501 
502 static int
hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe * frame,const void * buf,size_t size)503 hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, const void *buf,
504     size_t size)
505 {
506 	const uint8_t *p = buf;
507 	int ret;
508 
509 	ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
510 	if (ret)
511 		return ret;
512 	if (frame->header.length != HDMI_SPD_INFOFRAME_SIZE)
513 		return -EINVAL;
514 	p += HDMI_INFOFRAME_HEADER_SIZE;
515 	size -= HDMI_INFOFRAME_HEADER_SIZE;
516 
517 	memcpy(frame->vendor, &p[0], 8);
518 	memcpy(frame->product, &p[8], 16);
519 	frame->sdi = p[24];
520 
521 	return 0;
522 }
523 
524 /* Vendor infoframes */
525 
526 int
hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe * frame)527 hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame)
528 {
529 	static const struct hdmi_vendor_infoframe zero_frame;
530 
531 	*frame = zero_frame;
532 
533 	hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_VENDOR,
534 	    1, 0 /* depends on s3d_struct */);
535 
536 	frame->oui = HDMI_IEEE_OUI;
537 	frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID;
538 
539 	return 0;
540 }
541 
542 static size_t
hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe * frame)543 hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe *frame)
544 {
545 
546 	if (frame->vic) {
547 		return 5;
548 	} else if (frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) {
549 		if (frame->s3d_struct < HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
550 			return 5;
551 		else
552 			return 6;
553 	} else {
554 		return 4;
555 	}
556 }
557 
558 int
hdmi_vendor_infoframe_check(const struct hdmi_vendor_infoframe * frame)559 hdmi_vendor_infoframe_check(const struct hdmi_vendor_infoframe *frame)
560 {
561 
562 	if (frame->header.type != HDMI_INFOFRAME_TYPE_VENDOR ||
563 	    frame->header.version != 1)
564 		return -EINVAL;
565 	/* frame->header.length not used when packing */
566 
567 	/* At most one may be supplied.  */
568 	if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
569 		return -EINVAL;
570 
571 	return 0;
572 }
573 
574 ssize_t
hdmi_vendor_infoframe_pack(const struct hdmi_vendor_infoframe * frame,void * buf,size_t size)575 hdmi_vendor_infoframe_pack(const struct hdmi_vendor_infoframe *frame,
576     void *buf, size_t size)
577 {
578 	uint8_t *p = buf;
579 	size_t length;
580 	int ret;
581 
582 	/* At most one may be supplied.  */
583 	if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
584 		return -EINVAL;
585 
586 	length = HDMI_INFOFRAME_HEADER_SIZE;
587 	length += hdmi_vendor_infoframe_length(frame);
588 
589 	ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
590 	if (ret < 0)
591 		return ret;
592 	KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
593 	p += HDMI_INFOFRAME_HEADER_SIZE;
594 	size -= HDMI_INFOFRAME_HEADER_SIZE;
595 
596 	p[0] = 0x03;
597 	p[1] = 0x0c;
598 	p[2] = 0x00;
599 
600 	if (frame->vic) {
601 		p[3] = __SHIFTIN(0x1, __BITS(6,5));
602 		p[4] = frame->vic;
603 	} else if (frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) {
604 		p[3] = __SHIFTIN(0x2, __BITS(6,5));
605 		p[4] = __SHIFTIN(frame->s3d_struct, __BITS(7,4));
606 		if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
607 			p[5] = __SHIFTIN(frame->s3d_ext_data, __BITS(7,4));
608 	} else {
609 		p[3] = __SHIFTIN(0x0, __BITS(6,5));
610 	}
611 
612 	hdmi_infoframe_set_checksum(buf, length);
613 
614 	return length;
615 }
616 
617 static int
hdmi_vendor_infoframe_unpack(struct hdmi_vendor_infoframe * frame,const void * buf,size_t size)618 hdmi_vendor_infoframe_unpack(struct hdmi_vendor_infoframe *frame,
619     const void *buf, size_t size)
620 {
621 	const uint8_t *p = buf;
622 	int ret;
623 
624 	ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
625 	if (ret)
626 		return ret;
627 	if (frame->header.length < 4)
628 		return -EINVAL;
629 	p += HDMI_INFOFRAME_HEADER_SIZE;
630 	size -= HDMI_INFOFRAME_HEADER_SIZE;
631 
632 	if (p[0] != 0x03 || p[1] != 0x0c || p[2] != 0x00)
633 		return -EINVAL;
634 
635 	switch (__SHIFTOUT(p[3], __BITS(6,5))) {
636 	case 0x0:
637 		if (frame->header.length != 4)
638 			return -EINVAL;
639 		break;
640 	case 0x1:
641 		if (frame->header.length != 5)
642 			return -EINVAL;
643 		frame->vic = p[4];
644 		break;
645 	case 0x2:
646 		if (frame->header.length < 5)
647 			return -EINVAL;
648 		frame->s3d_struct = __SHIFTOUT(p[4], __BITS(7,4));
649 		if (frame->s3d_struct < HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) {
650 			if (frame->header.length != 5)
651 				return -EINVAL;
652 		} else {
653 			if (frame->header.length != 6)
654 				return -EINVAL;
655 			frame->s3d_ext_data = __SHIFTOUT(p[5], __BITS(7,4));
656 		}
657 		break;
658 	default:
659 		return -EINVAL;
660 	}
661 
662 	return 0;
663 }
664 
665 /* union infoframe */
666 
__strong_alias(linux_hdmi_infoframe_pack_only,linux_hdmi_infoframe_pack)667 __strong_alias(linux_hdmi_infoframe_pack_only,linux_hdmi_infoframe_pack) /* XXX */
668 
669 ssize_t
670 hdmi_infoframe_pack(const union hdmi_infoframe *frame, void *buf, size_t size)
671 {
672 
673 	switch (frame->any.type) {
674 	case HDMI_INFOFRAME_TYPE_VENDOR:
675 		return hdmi_vendor_infoframe_pack(&frame->vendor.hdmi, buf,
676 		    size);
677 	case HDMI_INFOFRAME_TYPE_AVI:
678 		return hdmi_avi_infoframe_pack(&frame->avi, buf, size);
679 	case HDMI_INFOFRAME_TYPE_SPD:
680 		return hdmi_spd_infoframe_pack(&frame->spd, buf, size);
681 	case HDMI_INFOFRAME_TYPE_AUDIO:
682 		return hdmi_audio_infoframe_pack(&frame->audio, buf, size);
683 	case HDMI_INFOFRAME_TYPE_DRM:
684 		return hdmi_drm_infoframe_pack(&frame->drm, buf, size);
685 	default:
686 		return -EINVAL;
687 	}
688 }
689 
690 int
hdmi_infoframe_unpack(union hdmi_infoframe * frame,const void * buf,size_t size)691 hdmi_infoframe_unpack(union hdmi_infoframe *frame, const void *buf,
692     size_t size)
693 {
694 	int ret;
695 
696 	memset(frame, 0, sizeof(*frame));
697 
698 	ret = hdmi_infoframe_header_unpack(&frame->any, buf, size);
699 	if (ret)
700 		return ret;
701 	switch (frame->any.type) {
702 	case HDMI_INFOFRAME_TYPE_VENDOR:
703 		return hdmi_vendor_infoframe_unpack(&frame->vendor.hdmi, buf,
704 		    size);
705 	case HDMI_INFOFRAME_TYPE_AVI:
706 		return hdmi_avi_infoframe_unpack(&frame->avi, buf, size);
707 	case HDMI_INFOFRAME_TYPE_SPD:
708 		return hdmi_spd_infoframe_unpack(&frame->spd, buf, size);
709 	case HDMI_INFOFRAME_TYPE_AUDIO:
710 		return hdmi_audio_infoframe_unpack(&frame->audio, buf, size);
711 	case HDMI_INFOFRAME_TYPE_DRM:
712 		return hdmi_drm_infoframe_unpack(&frame->drm, buf, size);
713 	default:
714 		return -EINVAL;
715 	}
716 }
717 
718 void
hdmi_infoframe_log(const char * level,struct device * device,const union hdmi_infoframe * frame)719 hdmi_infoframe_log(const char *level, struct device *device,
720     const union hdmi_infoframe *frame)
721 {
722 
723 	hexdump(printf, device_xname(device), frame, sizeof(*frame));
724 }
725