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 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 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 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 0; 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], 16); 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_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 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 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