xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_hdmi.c (revision 4f8e99e704d21096ec7cab35064370408a54e05d)
1 /*	$NetBSD: linux_hdmi.c,v 1.2 2022/07/09 18:11:23 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.2 2022/07/09 18:11:23 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
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
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 int
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 	if (length < HDMI_INFOFRAME_HEADER_SIZE)
76 		return -ENOSPC;
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
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
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 HDMI_INFOFRAME_HEADER_SIZE;
115 }
116 
117 static void
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
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
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
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 	memset(frame, 0, sizeof(*frame));
196 
197 	ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
198 	if (ret)
199 		return ret;
200 	if (frame->header.length != HDMI_AUDIO_INFOFRAME_SIZE)
201 		return -EINVAL;
202 	p += HDMI_INFOFRAME_HEADER_SIZE;
203 	size -= HDMI_INFOFRAME_HEADER_SIZE;
204 
205 	frame->coding_type = __SHIFTOUT(p[0], __BITS(7,4));
206 	frame->channels = __SHIFTOUT(p[0], __BITS(2,0));
207 
208 	frame->sample_frequency = __SHIFTOUT(p[1], __BITS(4,2));
209 	frame->sample_size = __SHIFTOUT(p[1], __BITS(1,0));
210 
211 	frame->coding_type_ext = __SHIFTOUT(p[2], __BITS(5,0));
212 
213 	frame->level_shift_value = __SHIFTOUT(p[3], __BITS(6,3));
214 
215 	frame->downmix_inhibit = __SHIFTOUT(p[4], __BIT(7));
216 
217 	return 0;
218 }
219 
220 /* AVI infoframes */
221 
222 int
223 hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame)
224 {
225 	static const struct hdmi_avi_infoframe zero_frame;
226 
227 	*frame = zero_frame;
228 
229 	hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_AVI, 2,
230 	    HDMI_AVI_INFOFRAME_SIZE);
231 
232 	return 0;
233 }
234 
235 int
236 hdmi_avi_infoframe_check(const struct hdmi_avi_infoframe *frame)
237 {
238 	int ret;
239 
240 	ret = hdmi_infoframe_header_check(&frame->header,
241 	    HDMI_INFOFRAME_TYPE_AVI, 2, HDMI_AVI_INFOFRAME_SIZE);
242 	if (ret)
243 		return ret;
244 
245 	return 0;
246 }
247 
248 ssize_t
249 hdmi_avi_infoframe_pack(const struct hdmi_avi_infoframe *frame, void *buf,
250     size_t size)
251 {
252 	const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
253 	    HDMI_AVI_INFOFRAME_SIZE;
254 	uint8_t *p = buf;
255 	int ret;
256 
257 	KASSERT(frame->header.length == HDMI_AVI_INFOFRAME_SIZE);
258 
259 	ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
260 	if (ret < 0)
261 		return ret;
262 	KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
263 	p += HDMI_INFOFRAME_HEADER_SIZE;
264 	size -= HDMI_INFOFRAME_HEADER_SIZE;
265 
266 	p[0] = __SHIFTIN(frame->colorspace, __BITS(6,5));
267 	p[0] |= __SHIFTIN(frame->active_aspect & 0xf? 1 : 0, __BIT(4));
268 	p[0] |= __SHIFTIN(frame->top_bar || frame->bottom_bar, __BIT(3));
269 	p[0] |= __SHIFTIN(frame->left_bar || frame->right_bar, __BIT(2));
270 	p[0] |= __SHIFTIN(frame->scan_mode, __BITS(1,0));
271 
272 	p[1] = __SHIFTIN(frame->colorimetry, __BITS(7,6));
273 	p[1] |= __SHIFTIN(frame->picture_aspect, __BITS(5,4));
274 	p[1] |= __SHIFTIN(frame->active_aspect, __BITS(3,0));
275 
276 	p[2] = __SHIFTIN(frame->itc? 1 : 0, __BIT(7));
277 	p[2] |= __SHIFTIN(frame->extended_colorimetry, __BITS(6,4));
278 	p[2] |= __SHIFTIN(frame->quantization_range, __BITS(3,2));
279 	p[2] |= __SHIFTIN(frame->nups, __BITS(1,0));
280 
281 	p[3] = frame->video_code;
282 
283 	p[4] = __SHIFTIN(frame->ycc_quantization_range, __BITS(7,6));
284 	p[4] |= __SHIFTIN(frame->content_type, __BITS(5,4));
285 	p[4] |= __SHIFTIN(frame->pixel_repeat, __BITS(3,0));
286 
287 	le16enc(&p[5], frame->top_bar);
288 	le16enc(&p[7], frame->bottom_bar);
289 	le16enc(&p[9], frame->left_bar);
290 	le16enc(&p[11], frame->right_bar);
291 	CTASSERT(HDMI_AVI_INFOFRAME_SIZE == 13);
292 
293 	hdmi_infoframe_set_checksum(buf, length);
294 
295 	return length;
296 }
297 
298 static int
299 hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, const void *buf,
300     size_t size)
301 {
302 	const uint8_t *p = buf;
303 	int ret;
304 
305 	memset(frame, 0, sizeof(*frame));
306 
307 	ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
308 	if (ret)
309 		return ret;
310 	if (frame->header.length != HDMI_AVI_INFOFRAME_SIZE)
311 		return -EINVAL;
312 	p += HDMI_INFOFRAME_HEADER_SIZE;
313 	size -= HDMI_INFOFRAME_HEADER_SIZE;
314 
315 	frame->colorspace = __SHIFTOUT(p[0], __BITS(6,5));
316 	frame->scan_mode = __SHIFTOUT(p[0], __BITS(1,0));
317 
318 	frame->colorimetry = __SHIFTOUT(p[1], __BITS(7,6));
319 	frame->picture_aspect = __SHIFTOUT(p[1], __BITS(5,4));
320 	if (p[0] & __BIT(4))
321 		frame->active_aspect = __SHIFTOUT(p[1], __BITS(3,0));
322 
323 	frame->itc = __SHIFTOUT(p[2], __BIT(7));
324 	frame->extended_colorimetry = __SHIFTOUT(p[2], __BITS(6,4));
325 	frame->quantization_range = __SHIFTOUT(p[2], __BITS(3,2));
326 	frame->nups = __SHIFTOUT(p[2], __BITS(1,0));
327 
328 	frame->video_code = p[3];
329 
330 	frame->ycc_quantization_range = __SHIFTOUT(p[4], __BITS(7,6));
331 	frame->content_type = __SHIFTOUT(p[4], __BITS(5,4));
332 	frame->pixel_repeat = __SHIFTOUT(p[4], __BITS(3,0));
333 
334 	if (p[0] & __BIT(3)) {
335 		frame->top_bar = le16dec(&p[5]);
336 		frame->bottom_bar = le16dec(&p[7]);
337 	}
338 	if (p[0] & __BIT(2)) {
339 		frame->left_bar = le16dec(&p[9]);
340 		frame->right_bar = le16dec(&p[11]);
341 	}
342 
343 	return 0;
344 }
345 
346 /* DRM infoframes */
347 
348 int
349 hdmi_drm_infoframe_init(struct hdmi_drm_infoframe *frame)
350 {
351 	static const struct hdmi_drm_infoframe zero_frame;
352 
353 	*frame = zero_frame;
354 
355 	hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_DRM,
356 	    1, HDMI_DRM_INFOFRAME_SIZE);
357 
358 	return 0;
359 }
360 
361 int
362 hdmi_drm_infoframe_check(const struct hdmi_drm_infoframe *frame)
363 {
364 	int ret;
365 
366 	ret = hdmi_infoframe_header_check(&frame->header,
367 	    HDMI_INFOFRAME_TYPE_DRM, 1, HDMI_DRM_INFOFRAME_SIZE);
368 	if (ret)
369 		return ret;
370 
371 	return 0;
372 }
373 
374 __strong_alias(linux_hdmi_drm_infoframe_pack_only,linux_hdmi_drm_infoframe_pack) /* XXX */
375 
376 int
377 hdmi_drm_infoframe_pack(const struct hdmi_drm_infoframe *frame,
378     void *buf, size_t size)
379 {
380 	const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
381 	    HDMI_DRM_INFOFRAME_SIZE;
382 	uint8_t *p = buf;
383 	unsigned i;
384 	int ret;
385 
386 	KASSERT(frame->header.length == HDMI_DRM_INFOFRAME_SIZE);
387 
388 	ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
389 	if (ret < 0)
390 		return ret;
391 	KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
392 	p += HDMI_INFOFRAME_HEADER_SIZE;
393 	size -= HDMI_INFOFRAME_HEADER_SIZE;
394 
395 	p[0] = frame->eotf;
396 	p[1] = frame->metadata_type;
397 	for (i = 0; i < __arraycount(frame->display_primaries); i++) {
398 		le16enc(&p[2 + 4*i], frame->display_primaries[i].x);
399 		le16enc(&p[2 + 4*i + 2], frame->display_primaries[i].y);
400 	}
401 	le16enc(&p[14], frame->white_point.x);
402 	le16enc(&p[16], frame->white_point.y);
403 	le16enc(&p[18], frame->min_display_mastering_luminance);
404 	le16enc(&p[20], frame->max_display_mastering_luminance);
405 	le16enc(&p[22], frame->max_cll);
406 	le16enc(&p[24], frame->max_fall);
407 	CTASSERT(HDMI_DRM_INFOFRAME_SIZE == 26);
408 
409 	hdmi_infoframe_set_checksum(buf, length);
410 
411 	return length;
412 }
413 
414 static int
415 hdmi_drm_infoframe_unpack(struct hdmi_drm_infoframe *frame, const void *buf,
416     size_t size)
417 {
418 	const uint8_t *p = buf;
419 	unsigned i;
420 	int ret;
421 
422 	memset(frame, 0, sizeof(*frame));
423 
424 	ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
425 	if (ret)
426 		return ret;
427 	if (frame->header.length != HDMI_DRM_INFOFRAME_SIZE)
428 		return -EINVAL;
429 	p += HDMI_INFOFRAME_HEADER_SIZE;
430 	size -= HDMI_INFOFRAME_HEADER_SIZE;
431 
432 	frame->eotf = p[0];
433 	frame->metadata_type = p[1];
434 	for (i = 0; i < __arraycount(frame->display_primaries); i++) {
435 		frame->display_primaries[i].x = le16dec(&p[2 + 4*i]);
436 		frame->display_primaries[i].y = le16dec(&p[2 + 4*i + 2]);
437 	}
438 	frame->white_point.x = le16dec(&p[14]);
439 	frame->white_point.y = le16dec(&p[16]);
440 	frame->min_display_mastering_luminance = le16dec(&p[18]);
441 	frame->max_display_mastering_luminance = le16dec(&p[20]);
442 	frame->max_cll = le16dec(&p[22]);
443 	frame->max_fall = le16dec(&p[24]);
444 
445 	return 0;
446 }
447 
448 /* SPD infoframes */
449 
450 int
451 hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, const char *vendor,
452     const char *product)
453 {
454 	static const struct hdmi_spd_infoframe zero_frame;
455 
456 	*frame = zero_frame;
457 
458 	hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_SPD,
459 	    1, HDMI_SPD_INFOFRAME_SIZE);
460 
461 	strncpy(frame->vendor, vendor, sizeof(frame->vendor));
462 	strncpy(frame->product, product, sizeof(frame->product));
463 
464 	return 0;
465 }
466 
467 int
468 hdmi_spd_infoframe_check(const struct hdmi_spd_infoframe *frame)
469 {
470 	int ret;
471 
472 	ret = hdmi_infoframe_header_check(&frame->header,
473 	    HDMI_INFOFRAME_TYPE_SPD, 1, HDMI_SPD_INFOFRAME_SIZE);
474 	if (ret)
475 		return ret;
476 
477 	return 0;
478 }
479 
480 ssize_t
481 hdmi_spd_infoframe_pack(const struct hdmi_spd_infoframe *frame, void *buf,
482     size_t size)
483 {
484 	const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
485 	    HDMI_SPD_INFOFRAME_SIZE;
486 	uint8_t *p = buf;
487 	int ret;
488 
489 	KASSERT(frame->header.length == HDMI_SPD_INFOFRAME_SIZE);
490 
491 	ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
492 	if (ret < 0)
493 		return ret;
494 	KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
495 	p += HDMI_INFOFRAME_HEADER_SIZE;
496 	size -= HDMI_INFOFRAME_HEADER_SIZE;
497 
498 	memcpy(&p[0], frame->vendor, 8);
499 	memcpy(&p[8], frame->product, 16);
500 	p[24] = frame->sdi;
501 	CTASSERT(HDMI_SPD_INFOFRAME_SIZE == 25);
502 
503 	hdmi_infoframe_set_checksum(buf, length);
504 
505 	return length;
506 }
507 
508 static int
509 hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, const void *buf,
510     size_t size)
511 {
512 	const uint8_t *p = buf;
513 	int ret;
514 
515 	memset(frame, 0, sizeof(*frame));
516 
517 	ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
518 	if (ret)
519 		return ret;
520 	if (frame->header.length != HDMI_SPD_INFOFRAME_SIZE)
521 		return -EINVAL;
522 	p += HDMI_INFOFRAME_HEADER_SIZE;
523 	size -= HDMI_INFOFRAME_HEADER_SIZE;
524 
525 	memcpy(frame->vendor, &p[0], 8);
526 	memcpy(frame->product, &p[8], 8);
527 	frame->sdi = p[24];
528 
529 	return 0;
530 }
531 
532 /* Vendor infoframes */
533 
534 int
535 hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame)
536 {
537 	static const struct hdmi_vendor_infoframe zero_frame;
538 
539 	*frame = zero_frame;
540 
541 	hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_VENDOR,
542 	    1, 0 /* depends on s3d_struct */);
543 
544 	frame->oui = HDMI_IEEE_OUI;
545 	frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID;
546 
547 	return 0;
548 }
549 
550 static size_t
551 hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe *frame)
552 {
553 
554 	if (frame->vic) {
555 		return 5;
556 	} else if (frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) {
557 		if (frame->s3d_struct < HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
558 			return 5;
559 		else
560 			return 6;
561 	} else {
562 		return 4;
563 	}
564 }
565 
566 int
567 hdmi_vendor_infoframe_check(const struct hdmi_vendor_infoframe *frame)
568 {
569 
570 	if (frame->header.type != HDMI_INFOFRAME_TYPE_VENDOR ||
571 	    frame->header.version != 1)
572 		return -EINVAL;
573 	/* frame->header.length not used when packing */
574 
575 	/* At most one may be supplied.  */
576 	if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
577 		return -EINVAL;
578 
579 	return 0;
580 }
581 
582 int
583 hdmi_vendor_infoframe_pack(const struct hdmi_vendor_infoframe *frame,
584     void *buf, size_t size)
585 {
586 	uint8_t *p = buf;
587 	size_t length;
588 	int ret;
589 
590 	/* At most one may be supplied.  */
591 	if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
592 		return -EINVAL;
593 
594 	length = HDMI_INFOFRAME_HEADER_SIZE;
595 	length += hdmi_vendor_infoframe_length(frame);
596 
597 	ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
598 	if (ret < 0)
599 		return ret;
600 	KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
601 	p += HDMI_INFOFRAME_HEADER_SIZE;
602 	size -= HDMI_INFOFRAME_HEADER_SIZE;
603 
604 	p[0] = 0x03;
605 	p[1] = 0x0c;
606 	p[2] = 0x00;
607 
608 	if (frame->vic) {
609 		p[3] = __SHIFTIN(0x1, __BITS(6,5));
610 		p[4] = frame->vic;
611 	} else if (frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) {
612 		p[3] = __SHIFTIN(0x2, __BITS(6,5));
613 		p[4] = __SHIFTIN(frame->s3d_struct, __BITS(7,4));
614 		if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
615 			p[5] = __SHIFTIN(frame->s3d_ext_data, __BITS(7,4));
616 	} else {
617 		p[3] = __SHIFTIN(0x0, __BITS(6,5));
618 	}
619 
620 	hdmi_infoframe_set_checksum(buf, length);
621 
622 	return length;
623 }
624 
625 static int
626 hdmi_vendor_infoframe_unpack(struct hdmi_vendor_infoframe *frame,
627     const void *buf, size_t size)
628 {
629 	const uint8_t *p = buf;
630 	int ret;
631 
632 	memset(frame, 0, sizeof(*frame));
633 
634 	ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
635 	if (ret)
636 		return ret;
637 	if (frame->header.length < 4)
638 		return -EINVAL;
639 	p += HDMI_INFOFRAME_HEADER_SIZE;
640 	size -= HDMI_INFOFRAME_HEADER_SIZE;
641 
642 	if (p[0] != 0x03 || p[1] != 0x0c || p[2] != 0x00)
643 		return -EINVAL;
644 
645 	switch (__SHIFTOUT(p[3], __BITS(6,5))) {
646 	case 0x0:
647 		if (frame->header.length != 4)
648 			return -EINVAL;
649 		break;
650 	case 0x1:
651 		if (frame->header.length != 5)
652 			return -EINVAL;
653 		frame->vic = p[4];
654 		break;
655 	case 0x2:
656 		if (frame->header.length < 5)
657 			return -EINVAL;
658 		frame->s3d_struct = __SHIFTOUT(p[4], __BITS(7,4));
659 		if (frame->s3d_struct < HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) {
660 			if (frame->header.length != 5)
661 				return -EINVAL;
662 		} else {
663 			if (frame->header.length != 6)
664 				return -EINVAL;
665 			frame->s3d_ext_data = __SHIFTOUT(p[5], __BITS(7,4));
666 		}
667 		break;
668 	default:
669 		return -EINVAL;
670 	}
671 
672 	return 0;
673 }
674 
675 /* union infoframe */
676 
677 __strong_alias(linux_hdmi_infoframe_pack_only,linux_hdmi_infoframe_pack) /* XXX */
678 
679 ssize_t
680 hdmi_infoframe_pack(const union hdmi_infoframe *frame, void *buf, size_t size)
681 {
682 
683 	switch (frame->any.type) {
684 	case HDMI_INFOFRAME_TYPE_AVI:
685 		return hdmi_avi_infoframe_pack(&frame->avi, buf, size);
686 	case HDMI_INFOFRAME_TYPE_DRM:
687 		return hdmi_drm_infoframe_pack(&frame->drm, buf, size);
688 	case HDMI_INFOFRAME_TYPE_SPD:
689 		return hdmi_spd_infoframe_pack(&frame->spd, buf, size);
690 	case HDMI_INFOFRAME_TYPE_VENDOR:
691 		return hdmi_vendor_infoframe_pack(&frame->vendor.hdmi, buf,
692 		    size);
693 	default:
694 		return -EINVAL;
695 	}
696 }
697 
698 int
699 hdmi_infoframe_unpack(union hdmi_infoframe *frame, const void *buf,
700     size_t size)
701 {
702 	struct hdmi_infoframe_header header;
703 	int ret;
704 
705 	ret = hdmi_infoframe_header_unpack(&header, buf, size);
706 	if (ret)
707 		return ret;
708 	switch (header.type) {
709 	case HDMI_INFOFRAME_TYPE_AVI:
710 		return hdmi_avi_infoframe_unpack(&frame->avi, buf, size);
711 	case HDMI_INFOFRAME_TYPE_DRM:
712 		return hdmi_drm_infoframe_unpack(&frame->drm, buf, size);
713 	case HDMI_INFOFRAME_TYPE_SPD:
714 		return hdmi_spd_infoframe_unpack(&frame->spd, buf, size);
715 	case HDMI_INFOFRAME_TYPE_VENDOR:
716 		return hdmi_vendor_infoframe_unpack(&frame->vendor.hdmi, buf,
717 		    size);
718 	default:
719 		return -EINVAL;
720 	}
721 }
722 
723 void
724 hdmi_infoframe_log(const char *level, struct device *device,
725     const union hdmi_infoframe *frame)
726 {
727 
728 	hexdump(printf, device_xname(device), frame, sizeof(*frame));
729 }
730