linux_hdmi.c revision 1.5 1 /* $NetBSD: linux_hdmi.c,v 1.5 2022/07/09 18:18:36 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.5 2022/07/09 18:18:36 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 ssize_t
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 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
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
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
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
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
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
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
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
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
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
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
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
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], 8);
519 frame->sdi = p[24];
520
521 return 0;
522 }
523
524 /* Vendor infoframes */
525
526 int
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
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
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
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
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
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_AVI:
675 return hdmi_avi_infoframe_pack(&frame->avi, buf, size);
676 case HDMI_INFOFRAME_TYPE_DRM:
677 return hdmi_drm_infoframe_pack(&frame->drm, buf, size);
678 case HDMI_INFOFRAME_TYPE_SPD:
679 return hdmi_spd_infoframe_pack(&frame->spd, buf, size);
680 case HDMI_INFOFRAME_TYPE_VENDOR:
681 return hdmi_vendor_infoframe_pack(&frame->vendor.hdmi, buf,
682 size);
683 default:
684 return -EINVAL;
685 }
686 }
687
688 int
689 hdmi_infoframe_unpack(union hdmi_infoframe *frame, const void *buf,
690 size_t size)
691 {
692 struct hdmi_infoframe_header header;
693 int ret;
694
695 memset(frame, 0, sizeof(*frame));
696
697 ret = hdmi_infoframe_header_unpack(&header, buf, size);
698 if (ret)
699 return ret;
700 switch (header.type) {
701 case HDMI_INFOFRAME_TYPE_VENDOR:
702 return hdmi_vendor_infoframe_unpack(&frame->vendor.hdmi, buf,
703 size);
704 case HDMI_INFOFRAME_TYPE_AVI:
705 return hdmi_avi_infoframe_unpack(&frame->avi, buf, size);
706 case HDMI_INFOFRAME_TYPE_SPD:
707 return hdmi_spd_infoframe_unpack(&frame->spd, buf, size);
708 case HDMI_INFOFRAME_TYPE_AUDIO:
709 return hdmi_audio_infoframe_unpack(&frame->audio, buf, size);
710 case HDMI_INFOFRAME_TYPE_DRM:
711 return hdmi_drm_infoframe_unpack(&frame->drm, buf, size);
712 default:
713 return -EINVAL;
714 }
715 }
716
717 void
718 hdmi_infoframe_log(const char *level, struct device *device,
719 const union hdmi_infoframe *frame)
720 {
721
722 hexdump(printf, device_xname(device), frame, sizeof(*frame));
723 }
724