linux_hdmi.c revision 1.2 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