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