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 | |