| 1 | /* $NetBSD: hdmi.h,v 1.6 2015/11/14 18:04:05 jmcneill 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 | #ifndef _LINUX_HDMI_H_ |
| 33 | #define _LINUX_HDMI_H_ |
| 34 | |
| 35 | #include <sys/types.h> |
| 36 | #include <sys/param.h> |
| 37 | #include <sys/errno.h> |
| 38 | #include <sys/systm.h> |
| 39 | |
| 40 | enum hdmi_3d_structure { |
| 41 | HDMI_3D_STRUCTURE_INVALID = -1, |
| 42 | HDMI_3D_STRUCTURE_FRAME_PACKING = 0, |
| 43 | HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE = 1, |
| 44 | HDMI_3D_STRUCTURE_LINE_ALTERNATIVE = 2, |
| 45 | HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL = 3, |
| 46 | HDMI_3D_STRUCTURE_L_DEPTH = 4, |
| 47 | HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH = 5, |
| 48 | HDMI_3D_STRUCTURE_TOP_AND_BOTTOM = 6, |
| 49 | HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF = 8, |
| 50 | }; |
| 51 | |
| 52 | enum hdmi_active_aspect { |
| 53 | HDMI_ACTIVE_ASPECT_16_9_TOP = 2, |
| 54 | HDMI_ACTIVE_ASPECT_14_9_TOP = 3, |
| 55 | HDMI_ACTIVE_ASPECT_16_9_CENTER = 4, |
| 56 | HDMI_ACTIVE_ASPECT_PICTURE = 8, |
| 57 | HDMI_ACTIVE_ASPECT_4_3 = 9, |
| 58 | HDMI_ACTIVE_ASPECT_16_9 = 10, |
| 59 | HDMI_ACTIVE_ASPECT_14_9 = 11, |
| 60 | HDMI_ACTIVE_ASPECT_4_3_SP_14_9 = 13, |
| 61 | HDMI_ACTIVE_ASPECT_16_9_SP_14_9 = 14, |
| 62 | HDMI_ACTIVE_ASPECT_16_9_SP_4_3 = 15, |
| 63 | }; |
| 64 | |
| 65 | enum hdmi_audio_coding_type { |
| 66 | HDMI_AUDIO_CODING_TYPE_STREAM = 0, |
| 67 | HDMI_AUDIO_CODING_TYPE_PCM = 1, |
| 68 | HDMI_AUDIO_CODING_TYPE_AC3 = 2, |
| 69 | HDMI_AUDIO_CODING_TYPE_MPEG1 = 3, |
| 70 | HDMI_AUDIO_CODING_TYPE_MP3 = 4, |
| 71 | HDMI_AUDIO_CODING_TYPE_MPEG2 = 5, |
| 72 | HDMI_AUDIO_CODING_TYPE_AAC_LC = 6, |
| 73 | HDMI_AUDIO_CODING_TYPE_DTS = 7, |
| 74 | HDMI_AUDIO_CODING_TYPE_ATRAC = 8, |
| 75 | HDMI_AUDIO_CODING_TYPE_DSD = 9, |
| 76 | HDMI_AUDIO_CODING_TYPE_EAC3 = 10, |
| 77 | HDMI_AUDIO_CODING_TYPE_DTS_HD = 11, |
| 78 | HDMI_AUDIO_CODING_TYPE_MLP = 12, |
| 79 | HDMI_AUDIO_CODING_TYPE_DST = 13, |
| 80 | HDMI_AUDIO_CODING_TYPE_WMA_PRO = 14, |
| 81 | }; |
| 82 | |
| 83 | enum hdmi_audio_coding_type_ext { |
| 84 | HDMI_AUDIO_CODING_TYPE_EXT_STREAM = 0, |
| 85 | HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC = 1, |
| 86 | HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2 = 2, |
| 87 | HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND = 3, |
| 88 | }; |
| 89 | |
| 90 | enum hdmi_audio_sample_frequency { |
| 91 | HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM = 0, |
| 92 | HDMI_AUDIO_SAMPLE_FREQUENCY_32000 = 1, |
| 93 | HDMI_AUDIO_SAMPLE_FREQUENCY_44100 = 2, |
| 94 | HDMI_AUDIO_SAMPLE_FREQUENCY_48000 = 3, |
| 95 | HDMI_AUDIO_SAMPLE_FREQUENCY_88200 = 4, |
| 96 | HDMI_AUDIO_SAMPLE_FREQUENCY_96000 = 5, |
| 97 | HDMI_AUDIO_SAMPLE_FREQUENCY_176400 = 6, |
| 98 | HDMI_AUDIO_SAMPLE_FREQUENCY_192000 = 7, |
| 99 | }; |
| 100 | |
| 101 | enum hdmi_audio_sample_size { |
| 102 | HDMI_AUDIO_SAMPLE_SIZE_STREAM = 0, |
| 103 | HDMI_AUDIO_SAMPLE_SIZE_16 = 1, |
| 104 | HDMI_AUDIO_SAMPLE_SIZE_20 = 2, |
| 105 | HDMI_AUDIO_SAMPLE_SIZE_24 = 3, |
| 106 | }; |
| 107 | |
| 108 | enum hdmi_colorimetry { |
| 109 | HDMI_COLORIMETRY_NONE = 0, |
| 110 | HDMI_COLORIMETRY_ITU_601 = 1, |
| 111 | HDMI_COLORIMETRY_ITU_709 = 2, |
| 112 | HDMI_COLORIMETRY_EXTENDED = 3, |
| 113 | }; |
| 114 | |
| 115 | enum hdmi_colorspace { |
| 116 | HDMI_COLORSPACE_RGB = 0, |
| 117 | HDMI_COLORSPACE_YUV422 = 1, |
| 118 | HDMI_COLORSPACE_YUV444 = 2, |
| 119 | }; |
| 120 | |
| 121 | enum hdmi_content_type { |
| 122 | HDMI_CONTENT_TYPE_NONE = 0, |
| 123 | HDMI_CONTENT_TYPE_PHOTO = 1, |
| 124 | HDMI_CONTENT_TYPE_CINEMA = 2, |
| 125 | HDMI_CONTENT_TYPE_GAME = 3, |
| 126 | }; |
| 127 | |
| 128 | enum hdmi_extended_colorimetry { |
| 129 | HDMI_EXTENDED_COLORIMETRY_XV_YCC_601 = 0, |
| 130 | HDMI_EXTENDED_COLORIMETRY_XV_YCC_709 = 1, |
| 131 | HDMI_EXTENDED_COLORIMETRY_S_YCC_601 = 2, |
| 132 | HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601 = 3, |
| 133 | HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB = 4, |
| 134 | }; |
| 135 | |
| 136 | enum hdmi_nups { |
| 137 | HDMI_NUPS_UNKNOWN = 0, |
| 138 | HDMI_NUPS_HORIZONTAL = 1, |
| 139 | HDMI_NUPS_VERTICAL = 2, |
| 140 | HDMI_NUPS_BOTH = 3, |
| 141 | }; |
| 142 | |
| 143 | enum hdmi_picture_aspect { |
| 144 | HDMI_PICTURE_ASPECT_NONE = 0, |
| 145 | HDMI_PICTURE_ASPECT_4_3 = 1, |
| 146 | HDMI_PICTURE_ASPECT_16_9 = 2, |
| 147 | }; |
| 148 | |
| 149 | enum hdmi_quantization_range { |
| 150 | HDMI_QUANTIZATION_RANGE_DEFAULT = 0, |
| 151 | HDMI_QUANTIZATION_RANGE_LIMITED = 1, |
| 152 | HDMI_QUANTIZATION_RANGE_FULL = 2, |
| 153 | }; |
| 154 | |
| 155 | enum hdmi_scan_mode { |
| 156 | HDMI_SCAN_MODE_NONE = 0, |
| 157 | HDMI_SCAN_MODE_OVERSCAN = 1, |
| 158 | HDMI_SCAN_MODE_UNDERSCAN = 2, |
| 159 | }; |
| 160 | |
| 161 | enum hdmi_ycc_quantization_range { |
| 162 | HDMI_YCC_QUANTIZATION_RANGE_LIMITED = 0, |
| 163 | HDMI_YCC_QUANTIZATION_RANGE_FULL = 1, |
| 164 | }; |
| 165 | |
| 166 | enum hdmi_infoframe_type { |
| 167 | HDMI_INFOFRAME_TYPE_VENDOR = 0x81, |
| 168 | HDMI_INFOFRAME_TYPE_AVI = 0x82, |
| 169 | HDMI_INFOFRAME_TYPE_SPD = 0x83, |
| 170 | HDMI_INFOFRAME_TYPE_AUDIO = 0x84, |
| 171 | }; |
| 172 | |
| 173 | #define HDMI_INFOFRAME_SIZE(TYPE) \ |
| 174 | (HDMI_INFOFRAME_HEADER_SIZE + HDMI_##TYPE##_INFOFRAME_SIZE) |
| 175 | |
| 176 | #define 4 |
| 177 | struct { |
| 178 | enum hdmi_infoframe_type ; |
| 179 | uint8_t ; |
| 180 | uint8_t ; |
| 181 | /* checksum */ |
| 182 | }; |
| 183 | |
| 184 | static inline void |
| 185 | (struct hdmi_infoframe_header *, |
| 186 | enum hdmi_infoframe_type type, uint8_t vers, uint8_t length) |
| 187 | { |
| 188 | |
| 189 | header->type = type; |
| 190 | header->version = vers; |
| 191 | header->length = length; |
| 192 | } |
| 193 | |
| 194 | static inline int |
| 195 | (const struct hdmi_infoframe_header *, |
| 196 | uint8_t length, void *buf, size_t size) |
| 197 | { |
| 198 | uint8_t *const p = buf; |
| 199 | |
| 200 | if (length < HDMI_INFOFRAME_HEADER_SIZE) |
| 201 | return -ENOSPC; |
| 202 | if (size < length) |
| 203 | return -ENOSPC; |
| 204 | |
| 205 | p[0] = header->type; |
| 206 | p[1] = header->version; |
| 207 | p[2] = (length - HDMI_INFOFRAME_HEADER_SIZE); |
| 208 | p[3] = 0; /* checksum */ |
| 209 | |
| 210 | return HDMI_INFOFRAME_HEADER_SIZE; |
| 211 | } |
| 212 | |
| 213 | static inline void |
| 214 | hdmi_infoframe_checksum(void *buf, size_t length) |
| 215 | { |
| 216 | uint8_t *p = buf; |
| 217 | uint8_t checksum = 0; |
| 218 | |
| 219 | while (length--) |
| 220 | checksum += *p++; |
| 221 | |
| 222 | p = buf; |
| 223 | p[3] = (256 - checksum); |
| 224 | } |
| 225 | |
| 226 | #define HDMI_AUDIO_INFOFRAME_SIZE 10 |
| 227 | struct hdmi_audio_infoframe { |
| 228 | struct hdmi_infoframe_header ; |
| 229 | uint8_t channels; |
| 230 | enum hdmi_audio_coding_type coding_type; |
| 231 | enum hdmi_audio_sample_size sample_size; |
| 232 | enum hdmi_audio_sample_frequency sample_frequency; |
| 233 | enum hdmi_audio_coding_type_ext coding_type_ext; |
| 234 | uint8_t channel_allocation; |
| 235 | uint8_t level_shift_value; |
| 236 | bool downmix_inhibit; |
| 237 | }; |
| 238 | |
| 239 | static inline int |
| 240 | hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame) |
| 241 | { |
| 242 | static const struct hdmi_audio_infoframe zero_frame; |
| 243 | |
| 244 | *frame = zero_frame; |
| 245 | |
| 246 | hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_AUDIO, |
| 247 | 1, HDMI_AUDIO_INFOFRAME_SIZE); |
| 248 | |
| 249 | return 0; |
| 250 | } |
| 251 | |
| 252 | static inline ssize_t |
| 253 | hdmi_audio_infoframe_pack(const struct hdmi_audio_infoframe *frame, void *buf, |
| 254 | size_t size) |
| 255 | { |
| 256 | const size_t length = HDMI_INFOFRAME_HEADER_SIZE + |
| 257 | HDMI_AUDIO_INFOFRAME_SIZE; |
| 258 | uint8_t channels = 0; |
| 259 | uint8_t *p = buf; |
| 260 | int ret; |
| 261 | |
| 262 | KASSERT(frame->header.length == HDMI_AUDIO_INFOFRAME_SIZE); |
| 263 | |
| 264 | ret = hdmi_infoframe_header_pack(&frame->header, length, p, size); |
| 265 | if (ret < 0) |
| 266 | return ret; |
| 267 | KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE); |
| 268 | p += HDMI_INFOFRAME_HEADER_SIZE; |
| 269 | size -= HDMI_INFOFRAME_HEADER_SIZE; |
| 270 | |
| 271 | if (frame->channels >= 2) |
| 272 | channels = frame->channels - 1; |
| 273 | |
| 274 | p[0] = __SHIFTIN(frame->coding_type, __BITS(7,4)); |
| 275 | p[0] |= __SHIFTIN(channels, __BITS(2,0)); |
| 276 | |
| 277 | p[1] = __SHIFTIN(frame->sample_frequency, __BITS(4,2)); |
| 278 | p[1] |= __SHIFTIN(frame->sample_size, __BITS(1,0)); |
| 279 | |
| 280 | p[2] = __SHIFTIN(frame->coding_type_ext, __BITS(5,0)); |
| 281 | |
| 282 | p[3] = __SHIFTIN(frame->level_shift_value, __BITS(6, 3)); |
| 283 | |
| 284 | p[4] = __SHIFTIN(frame->downmix_inhibit? 1 : 0, __BIT(7)); |
| 285 | |
| 286 | /* PB6 to PB10 are reserved */ |
| 287 | p[5] = 0; |
| 288 | p[6] = 0; |
| 289 | p[7] = 0; |
| 290 | p[8] = 0; |
| 291 | p[9] = 0; |
| 292 | |
| 293 | CTASSERT(HDMI_AUDIO_INFOFRAME_SIZE == 10); |
| 294 | |
| 295 | hdmi_infoframe_checksum(buf, length); |
| 296 | |
| 297 | return length; |
| 298 | } |
| 299 | |
| 300 | #define HDMI_AVI_INFOFRAME_SIZE 13 |
| 301 | struct hdmi_avi_infoframe { |
| 302 | struct hdmi_infoframe_header ; |
| 303 | enum hdmi_colorspace colorspace; |
| 304 | enum hdmi_scan_mode scan_mode; |
| 305 | enum hdmi_colorimetry colorimetry; |
| 306 | enum hdmi_picture_aspect picture_aspect; |
| 307 | enum hdmi_active_aspect active_aspect; |
| 308 | bool itc; |
| 309 | enum hdmi_extended_colorimetry extended_colorimetry; |
| 310 | enum hdmi_quantization_range quantization_range; |
| 311 | enum hdmi_nups nups; |
| 312 | uint8_t video_code; |
| 313 | enum hdmi_ycc_quantization_range ycc_quantization_range; |
| 314 | enum hdmi_content_type content_type; |
| 315 | uint8_t pixel_repeat; |
| 316 | uint16_t top_bar; |
| 317 | uint16_t bottom_bar; |
| 318 | uint16_t left_bar; |
| 319 | uint16_t right_bar; |
| 320 | }; |
| 321 | |
| 322 | static inline int |
| 323 | hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) |
| 324 | { |
| 325 | static const struct hdmi_avi_infoframe zero_frame; |
| 326 | |
| 327 | *frame = zero_frame; |
| 328 | |
| 329 | hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_AVI, 2, |
| 330 | HDMI_AVI_INFOFRAME_SIZE); |
| 331 | |
| 332 | return 0; |
| 333 | } |
| 334 | |
| 335 | static inline ssize_t |
| 336 | hdmi_avi_infoframe_pack(const struct hdmi_avi_infoframe *frame, void *buf, |
| 337 | size_t size) |
| 338 | { |
| 339 | const size_t length = HDMI_INFOFRAME_HEADER_SIZE + |
| 340 | HDMI_AVI_INFOFRAME_SIZE; |
| 341 | uint8_t *p = buf; |
| 342 | int ret; |
| 343 | |
| 344 | KASSERT(frame->header.length == HDMI_AVI_INFOFRAME_SIZE); |
| 345 | |
| 346 | ret = hdmi_infoframe_header_pack(&frame->header, length, p, size); |
| 347 | if (ret < 0) |
| 348 | return ret; |
| 349 | KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE); |
| 350 | p += HDMI_INFOFRAME_HEADER_SIZE; |
| 351 | size -= HDMI_INFOFRAME_HEADER_SIZE; |
| 352 | |
| 353 | p[0] = __SHIFTIN(frame->colorspace, __BITS(6,5)); |
| 354 | p[0] |= __SHIFTIN(frame->active_aspect & 0xf? 1 : 0, __BIT(4)); |
| 355 | p[0] |= __SHIFTIN(frame->top_bar || frame->bottom_bar, __BIT(3)); |
| 356 | p[0] |= __SHIFTIN(frame->left_bar || frame->right_bar, __BIT(2)); |
| 357 | p[0] |= __SHIFTIN(frame->scan_mode, __BITS(1,0)); |
| 358 | |
| 359 | p[1] = __SHIFTIN(frame->colorimetry, __BITS(7,6)); |
| 360 | p[1] |= __SHIFTIN(frame->picture_aspect, __BITS(5,4)); |
| 361 | p[1] |= __SHIFTIN(frame->active_aspect, __BITS(3,0)); |
| 362 | |
| 363 | p[2] = __SHIFTIN(frame->itc? 1 : 0, __BIT(7)); |
| 364 | p[2] |= __SHIFTIN(frame->extended_colorimetry, __BITS(6,4)); |
| 365 | p[2] |= __SHIFTIN(frame->quantization_range, __BITS(3,2)); |
| 366 | p[2] |= __SHIFTIN(frame->nups, __BITS(1,0)); |
| 367 | |
| 368 | p[3] = frame->video_code; |
| 369 | |
| 370 | p[4] = __SHIFTIN(frame->ycc_quantization_range, __BITS(7,6)); |
| 371 | p[4] |= __SHIFTIN(frame->content_type, __BITS(5,4)); |
| 372 | p[4] |= __SHIFTIN(frame->pixel_repeat, __BITS(3,0)); |
| 373 | |
| 374 | le16enc(&p[5], frame->top_bar); |
| 375 | le16enc(&p[7], frame->bottom_bar); |
| 376 | le16enc(&p[9], frame->left_bar); |
| 377 | le16enc(&p[11], frame->right_bar); |
| 378 | CTASSERT(HDMI_AVI_INFOFRAME_SIZE == 13); |
| 379 | |
| 380 | hdmi_infoframe_checksum(buf, length); |
| 381 | |
| 382 | return length; |
| 383 | } |
| 384 | |
| 385 | #define HDMI_SPD_INFOFRAME_SIZE 25 |
| 386 | struct hdmi_spd_infoframe { |
| 387 | struct hdmi_infoframe_header ; |
| 388 | char vendor[8]; |
| 389 | char product[16]; |
| 390 | enum hdmi_spd_sdi { |
| 391 | HDMI_SPD_SDI_UNKNOWN = 0, |
| 392 | HDMI_SPD_SDI_DSTB = 1, |
| 393 | HDMI_SPD_SDI_DVDP = 2, |
| 394 | HDMI_SPD_SDI_DVHS = 3, |
| 395 | HDMI_SPD_SDI_HDDVR = 4, |
| 396 | HDMI_SPD_SDI_DVC = 5, |
| 397 | HDMI_SPD_SDI_DSC = 6, |
| 398 | HDMI_SPD_SDI_VCD = 7, |
| 399 | HDMI_SPD_SDI_GAME = 8, |
| 400 | HDMI_SPD_SDI_PC = 9, |
| 401 | HDMI_SPD_SDI_BD = 10, |
| 402 | HDMI_SPD_SDI_SACD = 11, |
| 403 | HDMI_SPD_SDI_HDDVD = 12, |
| 404 | HDMI_SPD_SDI_PMP = 13, |
| 405 | } sdi; |
| 406 | }; |
| 407 | |
| 408 | static inline int |
| 409 | hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, const char *vendor, |
| 410 | const char *product) |
| 411 | { |
| 412 | static const struct hdmi_spd_infoframe zero_frame; |
| 413 | |
| 414 | *frame = zero_frame; |
| 415 | |
| 416 | hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_SPD, |
| 417 | 1, HDMI_SPD_INFOFRAME_SIZE); |
| 418 | |
| 419 | (void)strlcpy(frame->vendor, vendor, sizeof(frame->vendor)); |
| 420 | (void)strlcpy(frame->product, product, sizeof(frame->product)); |
| 421 | |
| 422 | return 0; |
| 423 | } |
| 424 | |
| 425 | static inline ssize_t |
| 426 | hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buf, |
| 427 | size_t size) |
| 428 | { |
| 429 | const size_t length = HDMI_INFOFRAME_HEADER_SIZE + |
| 430 | HDMI_SPD_INFOFRAME_SIZE; |
| 431 | uint8_t *p = buf; |
| 432 | int ret; |
| 433 | |
| 434 | KASSERT(frame->header.length == HDMI_SPD_INFOFRAME_SIZE); |
| 435 | |
| 436 | ret = hdmi_infoframe_header_pack(&frame->header, length, p, size); |
| 437 | if (ret < 0) |
| 438 | return ret; |
| 439 | KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE); |
| 440 | p += HDMI_INFOFRAME_HEADER_SIZE; |
| 441 | size -= HDMI_INFOFRAME_HEADER_SIZE; |
| 442 | |
| 443 | (void)memcpy(&p[0], frame->vendor, 8); |
| 444 | (void)memcpy(&p[8], frame->product, 16); |
| 445 | p[24] = frame->sdi; |
| 446 | |
| 447 | hdmi_infoframe_checksum(buf, length); |
| 448 | |
| 449 | return length; |
| 450 | } |
| 451 | |
| 452 | #define HDMI_IEEE_OUI 0x000c03 |
| 453 | |
| 454 | struct hdmi_vendor_infoframe { |
| 455 | struct hdmi_infoframe_header ; |
| 456 | uint32_t oui; |
| 457 | uint8_t vic; |
| 458 | enum hdmi_3d_structure s3d_struct; |
| 459 | unsigned s3d_ext_data; |
| 460 | }; |
| 461 | |
| 462 | union hdmi_vendor_any_infoframe { |
| 463 | struct { |
| 464 | struct hdmi_infoframe_header ; |
| 465 | uint32_t oui; |
| 466 | } any; |
| 467 | struct hdmi_vendor_infoframe hdmi; |
| 468 | }; |
| 469 | |
| 470 | static inline int |
| 471 | hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame) |
| 472 | { |
| 473 | static const struct hdmi_vendor_infoframe zero_frame; |
| 474 | |
| 475 | *frame = zero_frame; |
| 476 | |
| 477 | hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_VENDOR, |
| 478 | 1, 0 /* depends on s3d_struct */); |
| 479 | |
| 480 | frame->oui = HDMI_IEEE_OUI; |
| 481 | frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID; |
| 482 | |
| 483 | return 0; |
| 484 | } |
| 485 | |
| 486 | static inline int |
| 487 | hdmi_vendor_infoframe_pack(const struct hdmi_vendor_infoframe *frame, |
| 488 | void *buf, size_t size) |
| 489 | { |
| 490 | uint8_t *p = buf; |
| 491 | size_t length; |
| 492 | int ret; |
| 493 | |
| 494 | /* Exactly one must be supplied. */ |
| 495 | if ((frame->vic == 0) == |
| 496 | (frame->s3d_struct == HDMI_3D_STRUCTURE_INVALID)) |
| 497 | return -EINVAL; |
| 498 | |
| 499 | length = HDMI_INFOFRAME_HEADER_SIZE + 6; |
| 500 | if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) |
| 501 | length += 1; |
| 502 | |
| 503 | ret = hdmi_infoframe_header_pack(&frame->header, length, p, size); |
| 504 | if (ret < 0) |
| 505 | return ret; |
| 506 | KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE); |
| 507 | p += HDMI_INFOFRAME_HEADER_SIZE; |
| 508 | size -= HDMI_INFOFRAME_HEADER_SIZE; |
| 509 | |
| 510 | p[0] = 0x03; |
| 511 | p[1] = 0x0c; |
| 512 | p[2] = 0x00; |
| 513 | |
| 514 | if (frame->vic == 0) { |
| 515 | p[3] = __SHIFTIN(0x2, __BITS(6,5)); |
| 516 | p[4] = __SHIFTIN(frame->s3d_struct, __BITS(7,4)); |
| 517 | if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) |
| 518 | p[9] = __SHIFTIN(frame->s3d_ext_data, __BITS(7,4)); |
| 519 | } else { |
| 520 | p[3] = __SHIFTIN(0x1, __BITS(6,5)); |
| 521 | p[4] = frame->vic; |
| 522 | } |
| 523 | |
| 524 | hdmi_infoframe_checksum(buf, length); |
| 525 | |
| 526 | return length; |
| 527 | } |
| 528 | |
| 529 | union hdmi_infoframe { |
| 530 | struct hdmi_infoframe_header any; |
| 531 | struct hdmi_avi_infoframe avi; |
| 532 | struct hdmi_spd_infoframe spd; |
| 533 | union hdmi_vendor_any_infoframe vendor; |
| 534 | }; |
| 535 | |
| 536 | static inline ssize_t |
| 537 | hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buf, size_t size) |
| 538 | { |
| 539 | |
| 540 | switch (frame->any.type) { |
| 541 | case HDMI_INFOFRAME_TYPE_AVI: |
| 542 | return hdmi_avi_infoframe_pack(&frame->avi, buf, size); |
| 543 | case HDMI_INFOFRAME_TYPE_SPD: |
| 544 | return hdmi_spd_infoframe_pack(&frame->spd, buf, size); |
| 545 | case HDMI_INFOFRAME_TYPE_VENDOR: |
| 546 | return hdmi_vendor_infoframe_pack(&frame->vendor.hdmi, buf, |
| 547 | size); |
| 548 | default: |
| 549 | return -EINVAL; |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | #endif /* _LINUX_HDMI_H_ */ |
| 554 | |