Commit | Line | Data |
---|---|---|
f142d3bd TR |
1 | /* |
2 | * Copyright (C) 2012 Avionic Design GmbH | |
3 | * | |
96d672e0 TR |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sub license, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice (including the | |
12 | * next paragraph) shall be included in all copies or substantial portions | |
13 | * of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
21 | * DEALINGS IN THE SOFTWARE. | |
f142d3bd TR |
22 | */ |
23 | ||
24 | #include <linux/bitops.h> | |
72b09896 | 25 | #include <linux/bug.h> |
f142d3bd TR |
26 | #include <linux/errno.h> |
27 | #include <linux/export.h> | |
28 | #include <linux/hdmi.h> | |
29 | #include <linux/string.h> | |
30 | ||
31 | static void hdmi_infoframe_checksum(void *buffer, size_t size) | |
32 | { | |
33 | u8 *ptr = buffer; | |
34 | u8 csum = 0; | |
35 | size_t i; | |
36 | ||
37 | /* compute checksum */ | |
38 | for (i = 0; i < size; i++) | |
39 | csum += ptr[i]; | |
40 | ||
41 | ptr[3] = 256 - csum; | |
42 | } | |
43 | ||
44 | /** | |
45 | * hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe | |
46 | * @frame: HDMI AVI infoframe | |
47 | * | |
48 | * Returns 0 on success or a negative error code on failure. | |
49 | */ | |
50 | int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) | |
51 | { | |
52 | memset(frame, 0, sizeof(*frame)); | |
53 | ||
54 | frame->type = HDMI_INFOFRAME_TYPE_AVI; | |
55 | frame->version = 2; | |
3c6b054d | 56 | frame->length = HDMI_AVI_INFOFRAME_SIZE; |
f142d3bd TR |
57 | |
58 | return 0; | |
59 | } | |
60 | EXPORT_SYMBOL(hdmi_avi_infoframe_init); | |
61 | ||
62 | /** | |
63 | * hdmi_avi_infoframe_pack() - write HDMI AVI infoframe to binary buffer | |
64 | * @frame: HDMI AVI infoframe | |
65 | * @buffer: destination buffer | |
66 | * @size: size of buffer | |
67 | * | |
68 | * Packs the information contained in the @frame structure into a binary | |
69 | * representation that can be written into the corresponding controller | |
70 | * registers. Also computes the checksum as required by section 5.3.5 of | |
71 | * the HDMI 1.4 specification. | |
72 | * | |
73 | * Returns the number of bytes packed into the binary buffer or a negative | |
74 | * error code on failure. | |
75 | */ | |
76 | ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, | |
77 | size_t size) | |
78 | { | |
79 | u8 *ptr = buffer; | |
80 | size_t length; | |
81 | ||
82 | length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; | |
83 | ||
84 | if (size < length) | |
85 | return -ENOSPC; | |
86 | ||
3b390f62 | 87 | memset(buffer, 0, size); |
f142d3bd TR |
88 | |
89 | ptr[0] = frame->type; | |
90 | ptr[1] = frame->version; | |
91 | ptr[2] = frame->length; | |
92 | ptr[3] = 0; /* checksum */ | |
93 | ||
94 | /* start infoframe payload */ | |
95 | ptr += HDMI_INFOFRAME_HEADER_SIZE; | |
96 | ||
97 | ptr[0] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3); | |
98 | ||
a5ad3dcf LD |
99 | /* |
100 | * Data byte 1, bit 4 has to be set if we provide the active format | |
101 | * aspect ratio | |
102 | */ | |
103 | if (frame->active_aspect & 0xf) | |
f142d3bd TR |
104 | ptr[0] |= BIT(4); |
105 | ||
974e0701 LD |
106 | /* Bit 3 and 2 indicate if we transmit horizontal/vertical bar data */ |
107 | if (frame->top_bar || frame->bottom_bar) | |
f142d3bd TR |
108 | ptr[0] |= BIT(3); |
109 | ||
974e0701 | 110 | if (frame->left_bar || frame->right_bar) |
f142d3bd TR |
111 | ptr[0] |= BIT(2); |
112 | ||
113 | ptr[1] = ((frame->colorimetry & 0x3) << 6) | | |
114 | ((frame->picture_aspect & 0x3) << 4) | | |
115 | (frame->active_aspect & 0xf); | |
116 | ||
117 | ptr[2] = ((frame->extended_colorimetry & 0x7) << 4) | | |
118 | ((frame->quantization_range & 0x3) << 2) | | |
119 | (frame->nups & 0x3); | |
120 | ||
121 | if (frame->itc) | |
122 | ptr[2] |= BIT(7); | |
123 | ||
124 | ptr[3] = frame->video_code & 0x7f; | |
125 | ||
126 | ptr[4] = ((frame->ycc_quantization_range & 0x3) << 6) | | |
127 | ((frame->content_type & 0x3) << 4) | | |
128 | (frame->pixel_repeat & 0xf); | |
129 | ||
130 | ptr[5] = frame->top_bar & 0xff; | |
131 | ptr[6] = (frame->top_bar >> 8) & 0xff; | |
132 | ptr[7] = frame->bottom_bar & 0xff; | |
133 | ptr[8] = (frame->bottom_bar >> 8) & 0xff; | |
134 | ptr[9] = frame->left_bar & 0xff; | |
135 | ptr[10] = (frame->left_bar >> 8) & 0xff; | |
136 | ptr[11] = frame->right_bar & 0xff; | |
137 | ptr[12] = (frame->right_bar >> 8) & 0xff; | |
138 | ||
139 | hdmi_infoframe_checksum(buffer, length); | |
140 | ||
141 | return length; | |
142 | } | |
143 | EXPORT_SYMBOL(hdmi_avi_infoframe_pack); | |
144 | ||
145 | /** | |
146 | * hdmi_spd_infoframe_init() - initialize an HDMI SPD infoframe | |
147 | * @frame: HDMI SPD infoframe | |
148 | * @vendor: vendor string | |
149 | * @product: product string | |
150 | * | |
151 | * Returns 0 on success or a negative error code on failure. | |
152 | */ | |
153 | int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, | |
154 | const char *vendor, const char *product) | |
155 | { | |
156 | memset(frame, 0, sizeof(*frame)); | |
157 | ||
158 | frame->type = HDMI_INFOFRAME_TYPE_SPD; | |
159 | frame->version = 1; | |
3c6b054d | 160 | frame->length = HDMI_SPD_INFOFRAME_SIZE; |
f142d3bd TR |
161 | |
162 | strncpy(frame->vendor, vendor, sizeof(frame->vendor)); | |
163 | strncpy(frame->product, product, sizeof(frame->product)); | |
164 | ||
165 | return 0; | |
166 | } | |
167 | EXPORT_SYMBOL(hdmi_spd_infoframe_init); | |
168 | ||
169 | /** | |
170 | * hdmi_spd_infoframe_pack() - write HDMI SPD infoframe to binary buffer | |
171 | * @frame: HDMI SPD infoframe | |
172 | * @buffer: destination buffer | |
173 | * @size: size of buffer | |
174 | * | |
175 | * Packs the information contained in the @frame structure into a binary | |
176 | * representation that can be written into the corresponding controller | |
177 | * registers. Also computes the checksum as required by section 5.3.5 of | |
178 | * the HDMI 1.4 specification. | |
179 | * | |
180 | * Returns the number of bytes packed into the binary buffer or a negative | |
181 | * error code on failure. | |
182 | */ | |
183 | ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, | |
184 | size_t size) | |
185 | { | |
186 | u8 *ptr = buffer; | |
187 | size_t length; | |
188 | ||
189 | length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; | |
190 | ||
191 | if (size < length) | |
192 | return -ENOSPC; | |
193 | ||
3b390f62 | 194 | memset(buffer, 0, size); |
f142d3bd TR |
195 | |
196 | ptr[0] = frame->type; | |
197 | ptr[1] = frame->version; | |
198 | ptr[2] = frame->length; | |
199 | ptr[3] = 0; /* checksum */ | |
200 | ||
201 | /* start infoframe payload */ | |
202 | ptr += HDMI_INFOFRAME_HEADER_SIZE; | |
203 | ||
204 | memcpy(ptr, frame->vendor, sizeof(frame->vendor)); | |
205 | memcpy(ptr + 8, frame->product, sizeof(frame->product)); | |
206 | ||
207 | ptr[24] = frame->sdi; | |
208 | ||
209 | hdmi_infoframe_checksum(buffer, length); | |
210 | ||
211 | return length; | |
212 | } | |
213 | EXPORT_SYMBOL(hdmi_spd_infoframe_pack); | |
214 | ||
215 | /** | |
216 | * hdmi_audio_infoframe_init() - initialize an HDMI audio infoframe | |
217 | * @frame: HDMI audio infoframe | |
218 | * | |
219 | * Returns 0 on success or a negative error code on failure. | |
220 | */ | |
221 | int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame) | |
222 | { | |
223 | memset(frame, 0, sizeof(*frame)); | |
224 | ||
225 | frame->type = HDMI_INFOFRAME_TYPE_AUDIO; | |
226 | frame->version = 1; | |
3c6b054d | 227 | frame->length = HDMI_AUDIO_INFOFRAME_SIZE; |
f142d3bd TR |
228 | |
229 | return 0; | |
230 | } | |
231 | EXPORT_SYMBOL(hdmi_audio_infoframe_init); | |
232 | ||
233 | /** | |
234 | * hdmi_audio_infoframe_pack() - write HDMI audio infoframe to binary buffer | |
235 | * @frame: HDMI audio infoframe | |
236 | * @buffer: destination buffer | |
237 | * @size: size of buffer | |
238 | * | |
239 | * Packs the information contained in the @frame structure into a binary | |
240 | * representation that can be written into the corresponding controller | |
241 | * registers. Also computes the checksum as required by section 5.3.5 of | |
242 | * the HDMI 1.4 specification. | |
243 | * | |
244 | * Returns the number of bytes packed into the binary buffer or a negative | |
245 | * error code on failure. | |
246 | */ | |
247 | ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, | |
248 | void *buffer, size_t size) | |
249 | { | |
250 | unsigned char channels; | |
251 | u8 *ptr = buffer; | |
252 | size_t length; | |
253 | ||
254 | length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; | |
255 | ||
256 | if (size < length) | |
257 | return -ENOSPC; | |
258 | ||
3b390f62 | 259 | memset(buffer, 0, size); |
f142d3bd TR |
260 | |
261 | if (frame->channels >= 2) | |
262 | channels = frame->channels - 1; | |
263 | else | |
264 | channels = 0; | |
265 | ||
266 | ptr[0] = frame->type; | |
267 | ptr[1] = frame->version; | |
268 | ptr[2] = frame->length; | |
269 | ptr[3] = 0; /* checksum */ | |
270 | ||
271 | /* start infoframe payload */ | |
272 | ptr += HDMI_INFOFRAME_HEADER_SIZE; | |
273 | ||
274 | ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7); | |
275 | ptr[1] = ((frame->sample_frequency & 0x7) << 2) | | |
276 | (frame->sample_size & 0x3); | |
277 | ptr[2] = frame->coding_type_ext & 0x1f; | |
278 | ptr[3] = frame->channel_allocation; | |
279 | ptr[4] = (frame->level_shift_value & 0xf) << 3; | |
280 | ||
281 | if (frame->downmix_inhibit) | |
282 | ptr[4] |= BIT(7); | |
283 | ||
284 | hdmi_infoframe_checksum(buffer, length); | |
285 | ||
286 | return length; | |
287 | } | |
288 | EXPORT_SYMBOL(hdmi_audio_infoframe_pack); | |
289 | ||
7d27becb | 290 | /** |
ae84b900 | 291 | * hdmi_vendor_infoframe_init() - initialize an HDMI vendor infoframe |
7d27becb LD |
292 | * @frame: HDMI vendor infoframe |
293 | * | |
294 | * Returns 0 on success or a negative error code on failure. | |
295 | */ | |
ae84b900 | 296 | int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame) |
7d27becb LD |
297 | { |
298 | memset(frame, 0, sizeof(*frame)); | |
299 | ||
300 | frame->type = HDMI_INFOFRAME_TYPE_VENDOR; | |
301 | frame->version = 1; | |
302 | ||
6cb3b7f1 | 303 | frame->oui = HDMI_IEEE_OUI; |
af3e95b4 | 304 | |
7d27becb LD |
305 | /* |
306 | * 0 is a valid value for s3d_struct, so we use a special "not set" | |
307 | * value | |
308 | */ | |
309 | frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID; | |
310 | ||
311 | return 0; | |
312 | } | |
ae84b900 | 313 | EXPORT_SYMBOL(hdmi_vendor_infoframe_init); |
7d27becb LD |
314 | |
315 | /** | |
ae84b900 | 316 | * hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary buffer |
7d27becb LD |
317 | * @frame: HDMI infoframe |
318 | * @buffer: destination buffer | |
319 | * @size: size of buffer | |
320 | * | |
321 | * Packs the information contained in the @frame structure into a binary | |
322 | * representation that can be written into the corresponding controller | |
323 | * registers. Also computes the checksum as required by section 5.3.5 of | |
324 | * the HDMI 1.4 specification. | |
325 | * | |
326 | * Returns the number of bytes packed into the binary buffer or a negative | |
327 | * error code on failure. | |
328 | */ | |
ae84b900 | 329 | ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, |
7d27becb LD |
330 | void *buffer, size_t size) |
331 | { | |
332 | u8 *ptr = buffer; | |
333 | size_t length; | |
334 | ||
335 | /* empty info frame */ | |
336 | if (frame->vic == 0 && frame->s3d_struct == HDMI_3D_STRUCTURE_INVALID) | |
337 | return -EINVAL; | |
338 | ||
339 | /* only one of those can be supplied */ | |
340 | if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) | |
341 | return -EINVAL; | |
342 | ||
343 | /* for side by side (half) we also need to provide 3D_Ext_Data */ | |
344 | if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) | |
345 | frame->length = 6; | |
346 | else | |
347 | frame->length = 5; | |
348 | ||
349 | length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; | |
350 | ||
351 | if (size < length) | |
352 | return -ENOSPC; | |
353 | ||
354 | memset(buffer, 0, size); | |
355 | ||
356 | ptr[0] = frame->type; | |
357 | ptr[1] = frame->version; | |
358 | ptr[2] = frame->length; | |
359 | ptr[3] = 0; /* checksum */ | |
360 | ||
361 | /* HDMI OUI */ | |
362 | ptr[4] = 0x03; | |
363 | ptr[5] = 0x0c; | |
364 | ptr[6] = 0x00; | |
365 | ||
366 | if (frame->vic) { | |
367 | ptr[7] = 0x1 << 5; /* video format */ | |
368 | ptr[8] = frame->vic; | |
369 | } else { | |
370 | ptr[7] = 0x2 << 5; /* video format */ | |
371 | ptr[8] = (frame->s3d_struct & 0xf) << 4; | |
372 | if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) | |
373 | ptr[9] = (frame->s3d_ext_data & 0xf) << 4; | |
374 | } | |
375 | ||
376 | hdmi_infoframe_checksum(buffer, length); | |
377 | ||
378 | return length; | |
379 | } | |
ae84b900 | 380 | EXPORT_SYMBOL(hdmi_vendor_infoframe_pack); |
7d27becb | 381 | |
af3e95b4 | 382 | /* |
ae84b900 | 383 | * hdmi_vendor_any_infoframe_pack() - write a vendor infoframe to binary buffer |
f142d3bd | 384 | */ |
ae84b900 LD |
385 | static ssize_t |
386 | hdmi_vendor_any_infoframe_pack(union hdmi_vendor_any_infoframe *frame, | |
387 | void *buffer, size_t size) | |
f142d3bd | 388 | { |
af3e95b4 | 389 | /* we only know about HDMI vendor infoframes */ |
6cb3b7f1 | 390 | if (frame->any.oui != HDMI_IEEE_OUI) |
af3e95b4 | 391 | return -EINVAL; |
f142d3bd | 392 | |
ae84b900 | 393 | return hdmi_vendor_infoframe_pack(&frame->hdmi, buffer, size); |
f142d3bd | 394 | } |
72b09896 DL |
395 | |
396 | /** | |
397 | * hdmi_infoframe_pack() - write a HDMI infoframe to binary buffer | |
398 | * @frame: HDMI infoframe | |
399 | * @buffer: destination buffer | |
400 | * @size: size of buffer | |
401 | * | |
402 | * Packs the information contained in the @frame structure into a binary | |
403 | * representation that can be written into the corresponding controller | |
404 | * registers. Also computes the checksum as required by section 5.3.5 of | |
405 | * the HDMI 1.4 specification. | |
406 | * | |
407 | * Returns the number of bytes packed into the binary buffer or a negative | |
408 | * error code on failure. | |
409 | */ | |
410 | ssize_t | |
411 | hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size) | |
412 | { | |
413 | ssize_t length; | |
414 | ||
415 | switch (frame->any.type) { | |
416 | case HDMI_INFOFRAME_TYPE_AVI: | |
417 | length = hdmi_avi_infoframe_pack(&frame->avi, buffer, size); | |
418 | break; | |
419 | case HDMI_INFOFRAME_TYPE_SPD: | |
420 | length = hdmi_spd_infoframe_pack(&frame->spd, buffer, size); | |
421 | break; | |
422 | case HDMI_INFOFRAME_TYPE_AUDIO: | |
423 | length = hdmi_audio_infoframe_pack(&frame->audio, buffer, size); | |
424 | break; | |
425 | case HDMI_INFOFRAME_TYPE_VENDOR: | |
ae84b900 LD |
426 | length = hdmi_vendor_any_infoframe_pack(&frame->vendor, |
427 | buffer, size); | |
72b09896 DL |
428 | break; |
429 | default: | |
430 | WARN(1, "Bad infoframe type %d\n", frame->any.type); | |
431 | length = -EINVAL; | |
432 | } | |
433 | ||
434 | return length; | |
435 | } | |
436 | EXPORT_SYMBOL(hdmi_infoframe_pack); |