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