Home | History | Annotate | Line # | Download | only in linux
      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