1*740b6d65Sriastradh /* $NetBSD: linux_hdmi.c,v 1.10 2022/07/10 13:56:44 riastradh Exp $ */
2f3393e87Sriastradh
3f3393e87Sriastradh /*-
4f3393e87Sriastradh * Copyright (c) 2014 The NetBSD Foundation, Inc.
5f3393e87Sriastradh * All rights reserved.
6f3393e87Sriastradh *
7f3393e87Sriastradh * This code is derived from software contributed to The NetBSD Foundation
8f3393e87Sriastradh * by Taylor R. Campbell.
9f3393e87Sriastradh *
10f3393e87Sriastradh * Redistribution and use in source and binary forms, with or without
11f3393e87Sriastradh * modification, are permitted provided that the following conditions
12f3393e87Sriastradh * are met:
13f3393e87Sriastradh * 1. Redistributions of source code must retain the above copyright
14f3393e87Sriastradh * notice, this list of conditions and the following disclaimer.
15f3393e87Sriastradh * 2. Redistributions in binary form must reproduce the above copyright
16f3393e87Sriastradh * notice, this list of conditions and the following disclaimer in the
17f3393e87Sriastradh * documentation and/or other materials provided with the distribution.
18f3393e87Sriastradh *
19f3393e87Sriastradh * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20f3393e87Sriastradh * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21f3393e87Sriastradh * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22f3393e87Sriastradh * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23f3393e87Sriastradh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24f3393e87Sriastradh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25f3393e87Sriastradh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26f3393e87Sriastradh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27f3393e87Sriastradh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28f3393e87Sriastradh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29f3393e87Sriastradh * POSSIBILITY OF SUCH DAMAGE.
30f3393e87Sriastradh */
31f3393e87Sriastradh
32f3393e87Sriastradh #include <sys/cdefs.h>
33*740b6d65Sriastradh __KERNEL_RCSID(0, "$NetBSD: linux_hdmi.c,v 1.10 2022/07/10 13:56:44 riastradh Exp $");
34f3393e87Sriastradh
35f3393e87Sriastradh #include <sys/types.h>
36f3393e87Sriastradh
37f3393e87Sriastradh #include <sys/device.h>
38f3393e87Sriastradh #include <sys/errno.h>
39f3393e87Sriastradh #include <sys/systm.h>
40f3393e87Sriastradh
41f3393e87Sriastradh #include <lib/libkern/libkern.h>
42f3393e87Sriastradh
43f3393e87Sriastradh #include <linux/hdmi.h>
44f3393e87Sriastradh
45f3393e87Sriastradh /* Infoframe headers */
46f3393e87Sriastradh
47f3393e87Sriastradh static void
hdmi_infoframe_header_init(struct hdmi_infoframe_header * header,enum hdmi_infoframe_type type,uint8_t vers,uint8_t length)48f3393e87Sriastradh hdmi_infoframe_header_init(struct hdmi_infoframe_header *header,
49f3393e87Sriastradh enum hdmi_infoframe_type type, uint8_t vers, uint8_t length)
50f3393e87Sriastradh {
51f3393e87Sriastradh
52f3393e87Sriastradh header->type = type;
53f3393e87Sriastradh header->version = vers;
54f3393e87Sriastradh header->length = length;
55f3393e87Sriastradh }
56f3393e87Sriastradh
57f3393e87Sriastradh static int
hdmi_infoframe_header_check(const struct hdmi_infoframe_header * header,enum hdmi_infoframe_type type,uint8_t vers,uint8_t length)58f3393e87Sriastradh hdmi_infoframe_header_check(const struct hdmi_infoframe_header *header,
59f3393e87Sriastradh enum hdmi_infoframe_type type, uint8_t vers, uint8_t length)
60f3393e87Sriastradh {
61f3393e87Sriastradh
62f3393e87Sriastradh if (header->type != type ||
63f3393e87Sriastradh header->version != vers ||
64f3393e87Sriastradh header->length != length)
65f3393e87Sriastradh return -EINVAL;
66f3393e87Sriastradh return 0;
67f3393e87Sriastradh }
68f3393e87Sriastradh
69c7520856Sriastradh static ssize_t
hdmi_infoframe_header_pack(const struct hdmi_infoframe_header * header,uint8_t length,void * buf,size_t size)70f3393e87Sriastradh hdmi_infoframe_header_pack(const struct hdmi_infoframe_header *header,
71f3393e87Sriastradh uint8_t length, void *buf, size_t size)
72f3393e87Sriastradh {
73f3393e87Sriastradh uint8_t *const p = buf;
74f3393e87Sriastradh
75325fdf0fSriastradh KASSERT(length >= HDMI_INFOFRAME_HEADER_SIZE);
76325fdf0fSriastradh
77f3393e87Sriastradh if (size < length)
78f3393e87Sriastradh return -ENOSPC;
79f3393e87Sriastradh
80f3393e87Sriastradh p[0] = header->type;
81f3393e87Sriastradh p[1] = header->version;
82f3393e87Sriastradh p[2] = (length - HDMI_INFOFRAME_HEADER_SIZE);
83f3393e87Sriastradh p[3] = 0; /* checksum */
84f3393e87Sriastradh
85f3393e87Sriastradh return HDMI_INFOFRAME_HEADER_SIZE;
86f3393e87Sriastradh }
87f3393e87Sriastradh
88f3393e87Sriastradh static uint8_t
hdmi_infoframe_checksum(const void * buf,size_t length)89f3393e87Sriastradh hdmi_infoframe_checksum(const void *buf, size_t length)
90f3393e87Sriastradh {
91f3393e87Sriastradh const uint8_t *p = buf;
92f3393e87Sriastradh uint8_t checksum = 0;
93f3393e87Sriastradh
94f3393e87Sriastradh while (length--)
95f3393e87Sriastradh checksum += *p++;
96f3393e87Sriastradh
97f3393e87Sriastradh return 256 - checksum;
98f3393e87Sriastradh }
99f3393e87Sriastradh
100f3393e87Sriastradh static int
hdmi_infoframe_header_unpack(struct hdmi_infoframe_header * header,const void * buf,size_t size)101f3393e87Sriastradh hdmi_infoframe_header_unpack(struct hdmi_infoframe_header *header,
102f3393e87Sriastradh const void *buf, size_t size)
103f3393e87Sriastradh {
104f3393e87Sriastradh const uint8_t *const p = buf;
105f3393e87Sriastradh
106f3393e87Sriastradh if (size < HDMI_INFOFRAME_HEADER_SIZE)
107f3393e87Sriastradh return -EINVAL;
108f3393e87Sriastradh if (p[2] > size - HDMI_INFOFRAME_HEADER_SIZE)
109f3393e87Sriastradh return -EINVAL;
110f3393e87Sriastradh if (hdmi_infoframe_checksum(buf, p[2] + HDMI_INFOFRAME_HEADER_SIZE))
111f3393e87Sriastradh return -EINVAL;
112f3393e87Sriastradh
113f3393e87Sriastradh hdmi_infoframe_header_init(header, p[0], p[1], p[2]);
114add6c02dSriastradh return 0;
115f3393e87Sriastradh }
116f3393e87Sriastradh
117f3393e87Sriastradh static void
hdmi_infoframe_set_checksum(void * buf,size_t length)118f3393e87Sriastradh hdmi_infoframe_set_checksum(void *buf, size_t length)
119f3393e87Sriastradh {
120f3393e87Sriastradh uint8_t *p = buf;
121f3393e87Sriastradh
122f3393e87Sriastradh p[3] = hdmi_infoframe_checksum(buf, length);
123f3393e87Sriastradh }
124f3393e87Sriastradh
125f3393e87Sriastradh /* Audio infoframes */
126f3393e87Sriastradh
127f3393e87Sriastradh int
hdmi_audio_infoframe_init(struct hdmi_audio_infoframe * frame)128f3393e87Sriastradh hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame)
129f3393e87Sriastradh {
130f3393e87Sriastradh static const struct hdmi_audio_infoframe zero_frame;
131f3393e87Sriastradh
132f3393e87Sriastradh *frame = zero_frame;
133f3393e87Sriastradh
134f3393e87Sriastradh hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_AUDIO,
135f3393e87Sriastradh 1, HDMI_AUDIO_INFOFRAME_SIZE);
136f3393e87Sriastradh
137f3393e87Sriastradh return 0;
138f3393e87Sriastradh }
139f3393e87Sriastradh
140f3393e87Sriastradh ssize_t
hdmi_audio_infoframe_pack(const struct hdmi_audio_infoframe * frame,void * buf,size_t size)141f3393e87Sriastradh hdmi_audio_infoframe_pack(const struct hdmi_audio_infoframe *frame, void *buf,
142f3393e87Sriastradh size_t size)
143f3393e87Sriastradh {
144f3393e87Sriastradh const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
145f3393e87Sriastradh HDMI_AUDIO_INFOFRAME_SIZE;
146f3393e87Sriastradh uint8_t channels = 0;
147f3393e87Sriastradh uint8_t *p = buf;
148f3393e87Sriastradh int ret;
149f3393e87Sriastradh
150f3393e87Sriastradh KASSERT(frame->header.length == HDMI_AUDIO_INFOFRAME_SIZE);
151f3393e87Sriastradh
152f3393e87Sriastradh ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
153f3393e87Sriastradh if (ret < 0)
154f3393e87Sriastradh return ret;
155f3393e87Sriastradh KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
156f3393e87Sriastradh p += HDMI_INFOFRAME_HEADER_SIZE;
157f3393e87Sriastradh size -= HDMI_INFOFRAME_HEADER_SIZE;
158f3393e87Sriastradh
159f3393e87Sriastradh if (frame->channels >= 2)
160f3393e87Sriastradh channels = frame->channels - 1;
161f3393e87Sriastradh
162f3393e87Sriastradh p[0] = __SHIFTIN(frame->coding_type, __BITS(7,4));
163f3393e87Sriastradh p[0] |= __SHIFTIN(channels, __BITS(2,0));
164f3393e87Sriastradh
165f3393e87Sriastradh p[1] = __SHIFTIN(frame->sample_frequency, __BITS(4,2));
166f3393e87Sriastradh p[1] |= __SHIFTIN(frame->sample_size, __BITS(1,0));
167f3393e87Sriastradh
168f3393e87Sriastradh p[2] = __SHIFTIN(frame->coding_type_ext, __BITS(5,0));
169f3393e87Sriastradh
170f3393e87Sriastradh p[3] = __SHIFTIN(frame->level_shift_value, __BITS(6,3));
171f3393e87Sriastradh
172f3393e87Sriastradh p[4] = __SHIFTIN(frame->downmix_inhibit? 1 : 0, __BIT(7));
173f3393e87Sriastradh
174f3393e87Sriastradh /* PB6 to PB10 are reserved */
175f3393e87Sriastradh p[5] = 0;
176f3393e87Sriastradh p[6] = 0;
177f3393e87Sriastradh p[7] = 0;
178f3393e87Sriastradh p[8] = 0;
179f3393e87Sriastradh p[9] = 0;
180f3393e87Sriastradh
181f3393e87Sriastradh CTASSERT(HDMI_AUDIO_INFOFRAME_SIZE == 10);
182f3393e87Sriastradh
183f3393e87Sriastradh hdmi_infoframe_set_checksum(buf, length);
184f3393e87Sriastradh
185f3393e87Sriastradh return length;
186f3393e87Sriastradh }
187f3393e87Sriastradh
1884f8e99e7Sriastradh static int
hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe * frame,const void * buf,size_t size)189f3393e87Sriastradh hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame,
190f3393e87Sriastradh const void *buf, size_t size)
191f3393e87Sriastradh {
192f3393e87Sriastradh const uint8_t *p = buf;
193f3393e87Sriastradh int ret;
194f3393e87Sriastradh
195f3393e87Sriastradh ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
196f3393e87Sriastradh if (ret)
197f3393e87Sriastradh return ret;
198f3393e87Sriastradh if (frame->header.length != HDMI_AUDIO_INFOFRAME_SIZE)
199f3393e87Sriastradh return -EINVAL;
200f3393e87Sriastradh p += HDMI_INFOFRAME_HEADER_SIZE;
201f3393e87Sriastradh size -= HDMI_INFOFRAME_HEADER_SIZE;
202f3393e87Sriastradh
203f3393e87Sriastradh frame->coding_type = __SHIFTOUT(p[0], __BITS(7,4));
204f3393e87Sriastradh frame->channels = __SHIFTOUT(p[0], __BITS(2,0));
205f3393e87Sriastradh
206f3393e87Sriastradh frame->sample_frequency = __SHIFTOUT(p[1], __BITS(4,2));
207f3393e87Sriastradh frame->sample_size = __SHIFTOUT(p[1], __BITS(1,0));
208f3393e87Sriastradh
209f3393e87Sriastradh frame->coding_type_ext = __SHIFTOUT(p[2], __BITS(5,0));
210f3393e87Sriastradh
211f3393e87Sriastradh frame->level_shift_value = __SHIFTOUT(p[3], __BITS(6,3));
212f3393e87Sriastradh
213f3393e87Sriastradh frame->downmix_inhibit = __SHIFTOUT(p[4], __BIT(7));
214f3393e87Sriastradh
215f3393e87Sriastradh return 0;
216f3393e87Sriastradh }
217f3393e87Sriastradh
218f3393e87Sriastradh /* AVI infoframes */
219f3393e87Sriastradh
220f3393e87Sriastradh int
hdmi_avi_infoframe_init(struct hdmi_avi_infoframe * frame)221f3393e87Sriastradh hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame)
222f3393e87Sriastradh {
223f3393e87Sriastradh static const struct hdmi_avi_infoframe zero_frame;
224f3393e87Sriastradh
225f3393e87Sriastradh *frame = zero_frame;
226f3393e87Sriastradh
227f3393e87Sriastradh hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_AVI, 2,
228f3393e87Sriastradh HDMI_AVI_INFOFRAME_SIZE);
229f3393e87Sriastradh
230f3393e87Sriastradh return 0;
231f3393e87Sriastradh }
232f3393e87Sriastradh
233f3393e87Sriastradh int
hdmi_avi_infoframe_check(const struct hdmi_avi_infoframe * frame)234f3393e87Sriastradh hdmi_avi_infoframe_check(const struct hdmi_avi_infoframe *frame)
235f3393e87Sriastradh {
236f3393e87Sriastradh int ret;
237f3393e87Sriastradh
238f3393e87Sriastradh ret = hdmi_infoframe_header_check(&frame->header,
239f3393e87Sriastradh HDMI_INFOFRAME_TYPE_AVI, 2, HDMI_AVI_INFOFRAME_SIZE);
240f3393e87Sriastradh if (ret)
241f3393e87Sriastradh return ret;
242f3393e87Sriastradh
243f3393e87Sriastradh return 0;
244f3393e87Sriastradh }
245f3393e87Sriastradh
246f3393e87Sriastradh ssize_t
hdmi_avi_infoframe_pack(const struct hdmi_avi_infoframe * frame,void * buf,size_t size)247f3393e87Sriastradh hdmi_avi_infoframe_pack(const struct hdmi_avi_infoframe *frame, void *buf,
248f3393e87Sriastradh size_t size)
249f3393e87Sriastradh {
250f3393e87Sriastradh const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
251f3393e87Sriastradh HDMI_AVI_INFOFRAME_SIZE;
252f3393e87Sriastradh uint8_t *p = buf;
253f3393e87Sriastradh int ret;
254f3393e87Sriastradh
255f3393e87Sriastradh KASSERT(frame->header.length == HDMI_AVI_INFOFRAME_SIZE);
256f3393e87Sriastradh
257f3393e87Sriastradh ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
258f3393e87Sriastradh if (ret < 0)
259f3393e87Sriastradh return ret;
260f3393e87Sriastradh KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
261f3393e87Sriastradh p += HDMI_INFOFRAME_HEADER_SIZE;
262f3393e87Sriastradh size -= HDMI_INFOFRAME_HEADER_SIZE;
263f3393e87Sriastradh
264f3393e87Sriastradh p[0] = __SHIFTIN(frame->colorspace, __BITS(6,5));
265f3393e87Sriastradh p[0] |= __SHIFTIN(frame->active_aspect & 0xf? 1 : 0, __BIT(4));
266f3393e87Sriastradh p[0] |= __SHIFTIN(frame->top_bar || frame->bottom_bar, __BIT(3));
267f3393e87Sriastradh p[0] |= __SHIFTIN(frame->left_bar || frame->right_bar, __BIT(2));
268f3393e87Sriastradh p[0] |= __SHIFTIN(frame->scan_mode, __BITS(1,0));
269f3393e87Sriastradh
270f3393e87Sriastradh p[1] = __SHIFTIN(frame->colorimetry, __BITS(7,6));
271f3393e87Sriastradh p[1] |= __SHIFTIN(frame->picture_aspect, __BITS(5,4));
272f3393e87Sriastradh p[1] |= __SHIFTIN(frame->active_aspect, __BITS(3,0));
273f3393e87Sriastradh
274f3393e87Sriastradh p[2] = __SHIFTIN(frame->itc? 1 : 0, __BIT(7));
275f3393e87Sriastradh p[2] |= __SHIFTIN(frame->extended_colorimetry, __BITS(6,4));
276f3393e87Sriastradh p[2] |= __SHIFTIN(frame->quantization_range, __BITS(3,2));
277f3393e87Sriastradh p[2] |= __SHIFTIN(frame->nups, __BITS(1,0));
278f3393e87Sriastradh
279f3393e87Sriastradh p[3] = frame->video_code;
280f3393e87Sriastradh
281f3393e87Sriastradh p[4] = __SHIFTIN(frame->ycc_quantization_range, __BITS(7,6));
282f3393e87Sriastradh p[4] |= __SHIFTIN(frame->content_type, __BITS(5,4));
283f3393e87Sriastradh p[4] |= __SHIFTIN(frame->pixel_repeat, __BITS(3,0));
284f3393e87Sriastradh
285f3393e87Sriastradh le16enc(&p[5], frame->top_bar);
286f3393e87Sriastradh le16enc(&p[7], frame->bottom_bar);
287f3393e87Sriastradh le16enc(&p[9], frame->left_bar);
288f3393e87Sriastradh le16enc(&p[11], frame->right_bar);
289f3393e87Sriastradh CTASSERT(HDMI_AVI_INFOFRAME_SIZE == 13);
290f3393e87Sriastradh
291f3393e87Sriastradh hdmi_infoframe_set_checksum(buf, length);
292f3393e87Sriastradh
293f3393e87Sriastradh return length;
294f3393e87Sriastradh }
295f3393e87Sriastradh
2964f8e99e7Sriastradh static int
hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe * frame,const void * buf,size_t size)297f3393e87Sriastradh hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, const void *buf,
298f3393e87Sriastradh size_t size)
299f3393e87Sriastradh {
300f3393e87Sriastradh const uint8_t *p = buf;
301f3393e87Sriastradh int ret;
302f3393e87Sriastradh
303f3393e87Sriastradh ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
304f3393e87Sriastradh if (ret)
305f3393e87Sriastradh return ret;
306f3393e87Sriastradh if (frame->header.length != HDMI_AVI_INFOFRAME_SIZE)
307f3393e87Sriastradh return -EINVAL;
308f3393e87Sriastradh p += HDMI_INFOFRAME_HEADER_SIZE;
309f3393e87Sriastradh size -= HDMI_INFOFRAME_HEADER_SIZE;
310f3393e87Sriastradh
311f3393e87Sriastradh frame->colorspace = __SHIFTOUT(p[0], __BITS(6,5));
312f3393e87Sriastradh frame->scan_mode = __SHIFTOUT(p[0], __BITS(1,0));
313f3393e87Sriastradh
314f3393e87Sriastradh frame->colorimetry = __SHIFTOUT(p[1], __BITS(7,6));
315f3393e87Sriastradh frame->picture_aspect = __SHIFTOUT(p[1], __BITS(5,4));
316f3393e87Sriastradh if (p[0] & __BIT(4))
317f3393e87Sriastradh frame->active_aspect = __SHIFTOUT(p[1], __BITS(3,0));
318f3393e87Sriastradh
319f3393e87Sriastradh frame->itc = __SHIFTOUT(p[2], __BIT(7));
320f3393e87Sriastradh frame->extended_colorimetry = __SHIFTOUT(p[2], __BITS(6,4));
321f3393e87Sriastradh frame->quantization_range = __SHIFTOUT(p[2], __BITS(3,2));
322f3393e87Sriastradh frame->nups = __SHIFTOUT(p[2], __BITS(1,0));
323f3393e87Sriastradh
324f3393e87Sriastradh frame->video_code = p[3];
325f3393e87Sriastradh
326f3393e87Sriastradh frame->ycc_quantization_range = __SHIFTOUT(p[4], __BITS(7,6));
327f3393e87Sriastradh frame->content_type = __SHIFTOUT(p[4], __BITS(5,4));
328f3393e87Sriastradh frame->pixel_repeat = __SHIFTOUT(p[4], __BITS(3,0));
329f3393e87Sriastradh
330f3393e87Sriastradh if (p[0] & __BIT(3)) {
331f3393e87Sriastradh frame->top_bar = le16dec(&p[5]);
332f3393e87Sriastradh frame->bottom_bar = le16dec(&p[7]);
333f3393e87Sriastradh }
334f3393e87Sriastradh if (p[0] & __BIT(2)) {
335f3393e87Sriastradh frame->left_bar = le16dec(&p[9]);
336f3393e87Sriastradh frame->right_bar = le16dec(&p[11]);
337f3393e87Sriastradh }
338f3393e87Sriastradh
339f3393e87Sriastradh return 0;
340f3393e87Sriastradh }
341f3393e87Sriastradh
342f3393e87Sriastradh /* DRM infoframes */
343f3393e87Sriastradh
344f3393e87Sriastradh int
hdmi_drm_infoframe_init(struct hdmi_drm_infoframe * frame)345f3393e87Sriastradh hdmi_drm_infoframe_init(struct hdmi_drm_infoframe *frame)
346f3393e87Sriastradh {
347f3393e87Sriastradh static const struct hdmi_drm_infoframe zero_frame;
348f3393e87Sriastradh
349f3393e87Sriastradh *frame = zero_frame;
350f3393e87Sriastradh
351f3393e87Sriastradh hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_DRM,
352f3393e87Sriastradh 1, HDMI_DRM_INFOFRAME_SIZE);
353f3393e87Sriastradh
354f3393e87Sriastradh return 0;
355f3393e87Sriastradh }
356f3393e87Sriastradh
357f3393e87Sriastradh int
hdmi_drm_infoframe_check(const struct hdmi_drm_infoframe * frame)358f3393e87Sriastradh hdmi_drm_infoframe_check(const struct hdmi_drm_infoframe *frame)
359f3393e87Sriastradh {
360f3393e87Sriastradh int ret;
361f3393e87Sriastradh
362f3393e87Sriastradh ret = hdmi_infoframe_header_check(&frame->header,
363f3393e87Sriastradh HDMI_INFOFRAME_TYPE_DRM, 1, HDMI_DRM_INFOFRAME_SIZE);
364f3393e87Sriastradh if (ret)
365f3393e87Sriastradh return ret;
366f3393e87Sriastradh
367f3393e87Sriastradh return 0;
368f3393e87Sriastradh }
369f3393e87Sriastradh
__strong_alias(linux_hdmi_drm_infoframe_pack_only,linux_hdmi_drm_infoframe_pack)370f3393e87Sriastradh __strong_alias(linux_hdmi_drm_infoframe_pack_only,linux_hdmi_drm_infoframe_pack) /* XXX */
371f3393e87Sriastradh
372c7520856Sriastradh ssize_t
373f3393e87Sriastradh hdmi_drm_infoframe_pack(const struct hdmi_drm_infoframe *frame,
374f3393e87Sriastradh void *buf, size_t size)
375f3393e87Sriastradh {
376f3393e87Sriastradh const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
377f3393e87Sriastradh HDMI_DRM_INFOFRAME_SIZE;
378f3393e87Sriastradh uint8_t *p = buf;
379f3393e87Sriastradh unsigned i;
380f3393e87Sriastradh int ret;
381f3393e87Sriastradh
382f3393e87Sriastradh KASSERT(frame->header.length == HDMI_DRM_INFOFRAME_SIZE);
383f3393e87Sriastradh
384f3393e87Sriastradh ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
385f3393e87Sriastradh if (ret < 0)
386f3393e87Sriastradh return ret;
387f3393e87Sriastradh KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
388f3393e87Sriastradh p += HDMI_INFOFRAME_HEADER_SIZE;
389f3393e87Sriastradh size -= HDMI_INFOFRAME_HEADER_SIZE;
390f3393e87Sriastradh
391f3393e87Sriastradh p[0] = frame->eotf;
392f3393e87Sriastradh p[1] = frame->metadata_type;
393f3393e87Sriastradh for (i = 0; i < __arraycount(frame->display_primaries); i++) {
394f3393e87Sriastradh le16enc(&p[2 + 4*i], frame->display_primaries[i].x);
395f3393e87Sriastradh le16enc(&p[2 + 4*i + 2], frame->display_primaries[i].y);
396f3393e87Sriastradh }
397f3393e87Sriastradh le16enc(&p[14], frame->white_point.x);
398f3393e87Sriastradh le16enc(&p[16], frame->white_point.y);
399f3393e87Sriastradh le16enc(&p[18], frame->min_display_mastering_luminance);
400f3393e87Sriastradh le16enc(&p[20], frame->max_display_mastering_luminance);
401f3393e87Sriastradh le16enc(&p[22], frame->max_cll);
402f3393e87Sriastradh le16enc(&p[24], frame->max_fall);
403f3393e87Sriastradh CTASSERT(HDMI_DRM_INFOFRAME_SIZE == 26);
404f3393e87Sriastradh
405f3393e87Sriastradh hdmi_infoframe_set_checksum(buf, length);
406f3393e87Sriastradh
407f3393e87Sriastradh return length;
408f3393e87Sriastradh }
409f3393e87Sriastradh
4104f8e99e7Sriastradh static int
hdmi_drm_infoframe_unpack(struct hdmi_drm_infoframe * frame,const void * buf,size_t size)411f3393e87Sriastradh hdmi_drm_infoframe_unpack(struct hdmi_drm_infoframe *frame, const void *buf,
412f3393e87Sriastradh size_t size)
413f3393e87Sriastradh {
414f3393e87Sriastradh const uint8_t *p = buf;
415f3393e87Sriastradh unsigned i;
416f3393e87Sriastradh int ret;
417f3393e87Sriastradh
418f3393e87Sriastradh ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
419f3393e87Sriastradh if (ret)
420f3393e87Sriastradh return ret;
421f3393e87Sriastradh if (frame->header.length != HDMI_DRM_INFOFRAME_SIZE)
422f3393e87Sriastradh return -EINVAL;
423f3393e87Sriastradh p += HDMI_INFOFRAME_HEADER_SIZE;
424f3393e87Sriastradh size -= HDMI_INFOFRAME_HEADER_SIZE;
425f3393e87Sriastradh
426f3393e87Sriastradh frame->eotf = p[0];
427f3393e87Sriastradh frame->metadata_type = p[1];
428f3393e87Sriastradh for (i = 0; i < __arraycount(frame->display_primaries); i++) {
429f3393e87Sriastradh frame->display_primaries[i].x = le16dec(&p[2 + 4*i]);
430f3393e87Sriastradh frame->display_primaries[i].y = le16dec(&p[2 + 4*i + 2]);
431f3393e87Sriastradh }
432f3393e87Sriastradh frame->white_point.x = le16dec(&p[14]);
433f3393e87Sriastradh frame->white_point.y = le16dec(&p[16]);
434f3393e87Sriastradh frame->min_display_mastering_luminance = le16dec(&p[18]);
435f3393e87Sriastradh frame->max_display_mastering_luminance = le16dec(&p[20]);
436f3393e87Sriastradh frame->max_cll = le16dec(&p[22]);
437f3393e87Sriastradh frame->max_fall = le16dec(&p[24]);
438f3393e87Sriastradh
439f3393e87Sriastradh return 0;
440f3393e87Sriastradh }
441f3393e87Sriastradh
442f3393e87Sriastradh /* SPD infoframes */
443f3393e87Sriastradh
444f3393e87Sriastradh int
hdmi_spd_infoframe_init(struct hdmi_spd_infoframe * frame,const char * vendor,const char * product)445f3393e87Sriastradh hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, const char *vendor,
446f3393e87Sriastradh const char *product)
447f3393e87Sriastradh {
448f3393e87Sriastradh static const struct hdmi_spd_infoframe zero_frame;
449f3393e87Sriastradh
450f3393e87Sriastradh *frame = zero_frame;
451f3393e87Sriastradh
452f3393e87Sriastradh hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_SPD,
453f3393e87Sriastradh 1, HDMI_SPD_INFOFRAME_SIZE);
454f3393e87Sriastradh
455f3393e87Sriastradh strncpy(frame->vendor, vendor, sizeof(frame->vendor));
456f3393e87Sriastradh strncpy(frame->product, product, sizeof(frame->product));
457f3393e87Sriastradh
458f3393e87Sriastradh return 0;
459f3393e87Sriastradh }
460f3393e87Sriastradh
461f3393e87Sriastradh int
hdmi_spd_infoframe_check(const struct hdmi_spd_infoframe * frame)462f3393e87Sriastradh hdmi_spd_infoframe_check(const struct hdmi_spd_infoframe *frame)
463f3393e87Sriastradh {
464f3393e87Sriastradh int ret;
465f3393e87Sriastradh
466f3393e87Sriastradh ret = hdmi_infoframe_header_check(&frame->header,
467f3393e87Sriastradh HDMI_INFOFRAME_TYPE_SPD, 1, HDMI_SPD_INFOFRAME_SIZE);
468f3393e87Sriastradh if (ret)
469f3393e87Sriastradh return ret;
470f3393e87Sriastradh
471f3393e87Sriastradh return 0;
472f3393e87Sriastradh }
473f3393e87Sriastradh
474f3393e87Sriastradh ssize_t
hdmi_spd_infoframe_pack(const struct hdmi_spd_infoframe * frame,void * buf,size_t size)475f3393e87Sriastradh hdmi_spd_infoframe_pack(const struct hdmi_spd_infoframe *frame, void *buf,
476f3393e87Sriastradh size_t size)
477f3393e87Sriastradh {
478f3393e87Sriastradh const size_t length = HDMI_INFOFRAME_HEADER_SIZE +
479f3393e87Sriastradh HDMI_SPD_INFOFRAME_SIZE;
480f3393e87Sriastradh uint8_t *p = buf;
481f3393e87Sriastradh int ret;
482f3393e87Sriastradh
483f3393e87Sriastradh KASSERT(frame->header.length == HDMI_SPD_INFOFRAME_SIZE);
484f3393e87Sriastradh
485f3393e87Sriastradh ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
486f3393e87Sriastradh if (ret < 0)
487f3393e87Sriastradh return ret;
488f3393e87Sriastradh KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
489f3393e87Sriastradh p += HDMI_INFOFRAME_HEADER_SIZE;
490f3393e87Sriastradh size -= HDMI_INFOFRAME_HEADER_SIZE;
491f3393e87Sriastradh
492f3393e87Sriastradh memcpy(&p[0], frame->vendor, 8);
493f3393e87Sriastradh memcpy(&p[8], frame->product, 16);
494f3393e87Sriastradh p[24] = frame->sdi;
495f3393e87Sriastradh CTASSERT(HDMI_SPD_INFOFRAME_SIZE == 25);
496f3393e87Sriastradh
497f3393e87Sriastradh hdmi_infoframe_set_checksum(buf, length);
498f3393e87Sriastradh
499f3393e87Sriastradh return length;
500f3393e87Sriastradh }
501f3393e87Sriastradh
5024f8e99e7Sriastradh static int
hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe * frame,const void * buf,size_t size)503f3393e87Sriastradh hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, const void *buf,
504f3393e87Sriastradh size_t size)
505f3393e87Sriastradh {
506f3393e87Sriastradh const uint8_t *p = buf;
507f3393e87Sriastradh int ret;
508f3393e87Sriastradh
509f3393e87Sriastradh ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
510f3393e87Sriastradh if (ret)
511f3393e87Sriastradh return ret;
512f3393e87Sriastradh if (frame->header.length != HDMI_SPD_INFOFRAME_SIZE)
513f3393e87Sriastradh return -EINVAL;
514f3393e87Sriastradh p += HDMI_INFOFRAME_HEADER_SIZE;
515f3393e87Sriastradh size -= HDMI_INFOFRAME_HEADER_SIZE;
516f3393e87Sriastradh
517f3393e87Sriastradh memcpy(frame->vendor, &p[0], 8);
518*740b6d65Sriastradh memcpy(frame->product, &p[8], 16);
519f3393e87Sriastradh frame->sdi = p[24];
520f3393e87Sriastradh
521f3393e87Sriastradh return 0;
522f3393e87Sriastradh }
523f3393e87Sriastradh
524f3393e87Sriastradh /* Vendor infoframes */
525f3393e87Sriastradh
526f3393e87Sriastradh int
hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe * frame)527f3393e87Sriastradh hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame)
528f3393e87Sriastradh {
529f3393e87Sriastradh static const struct hdmi_vendor_infoframe zero_frame;
530f3393e87Sriastradh
531f3393e87Sriastradh *frame = zero_frame;
532f3393e87Sriastradh
533f3393e87Sriastradh hdmi_infoframe_header_init(&frame->header, HDMI_INFOFRAME_TYPE_VENDOR,
534f3393e87Sriastradh 1, 0 /* depends on s3d_struct */);
535f3393e87Sriastradh
536f3393e87Sriastradh frame->oui = HDMI_IEEE_OUI;
537f3393e87Sriastradh frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID;
538f3393e87Sriastradh
539f3393e87Sriastradh return 0;
540f3393e87Sriastradh }
541f3393e87Sriastradh
5424f8e99e7Sriastradh static size_t
hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe * frame)543f3393e87Sriastradh hdmi_vendor_infoframe_length(const struct hdmi_vendor_infoframe *frame)
544f3393e87Sriastradh {
545f3393e87Sriastradh
546f3393e87Sriastradh if (frame->vic) {
547f3393e87Sriastradh return 5;
548f3393e87Sriastradh } else if (frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) {
549f3393e87Sriastradh if (frame->s3d_struct < HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
550f3393e87Sriastradh return 5;
551f3393e87Sriastradh else
552f3393e87Sriastradh return 6;
553f3393e87Sriastradh } else {
554f3393e87Sriastradh return 4;
555f3393e87Sriastradh }
556f3393e87Sriastradh }
557f3393e87Sriastradh
558f3393e87Sriastradh int
hdmi_vendor_infoframe_check(const struct hdmi_vendor_infoframe * frame)559f3393e87Sriastradh hdmi_vendor_infoframe_check(const struct hdmi_vendor_infoframe *frame)
560f3393e87Sriastradh {
561f3393e87Sriastradh
562f3393e87Sriastradh if (frame->header.type != HDMI_INFOFRAME_TYPE_VENDOR ||
563f3393e87Sriastradh frame->header.version != 1)
564f3393e87Sriastradh return -EINVAL;
565f3393e87Sriastradh /* frame->header.length not used when packing */
566f3393e87Sriastradh
567f3393e87Sriastradh /* At most one may be supplied. */
568f3393e87Sriastradh if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
569f3393e87Sriastradh return -EINVAL;
570f3393e87Sriastradh
571f3393e87Sriastradh return 0;
572f3393e87Sriastradh }
573f3393e87Sriastradh
574c7520856Sriastradh ssize_t
hdmi_vendor_infoframe_pack(const struct hdmi_vendor_infoframe * frame,void * buf,size_t size)575f3393e87Sriastradh hdmi_vendor_infoframe_pack(const struct hdmi_vendor_infoframe *frame,
576f3393e87Sriastradh void *buf, size_t size)
577f3393e87Sriastradh {
578f3393e87Sriastradh uint8_t *p = buf;
579f3393e87Sriastradh size_t length;
580f3393e87Sriastradh int ret;
581f3393e87Sriastradh
582f3393e87Sriastradh /* At most one may be supplied. */
583f3393e87Sriastradh if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
584f3393e87Sriastradh return -EINVAL;
585f3393e87Sriastradh
586f3393e87Sriastradh length = HDMI_INFOFRAME_HEADER_SIZE;
587f3393e87Sriastradh length += hdmi_vendor_infoframe_length(frame);
588f3393e87Sriastradh
589f3393e87Sriastradh ret = hdmi_infoframe_header_pack(&frame->header, length, p, size);
590f3393e87Sriastradh if (ret < 0)
591f3393e87Sriastradh return ret;
592f3393e87Sriastradh KASSERT(ret == HDMI_INFOFRAME_HEADER_SIZE);
593f3393e87Sriastradh p += HDMI_INFOFRAME_HEADER_SIZE;
594f3393e87Sriastradh size -= HDMI_INFOFRAME_HEADER_SIZE;
595f3393e87Sriastradh
596f3393e87Sriastradh p[0] = 0x03;
597f3393e87Sriastradh p[1] = 0x0c;
598f3393e87Sriastradh p[2] = 0x00;
599f3393e87Sriastradh
600f3393e87Sriastradh if (frame->vic) {
601f3393e87Sriastradh p[3] = __SHIFTIN(0x1, __BITS(6,5));
602f3393e87Sriastradh p[4] = frame->vic;
603f3393e87Sriastradh } else if (frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) {
604f3393e87Sriastradh p[3] = __SHIFTIN(0x2, __BITS(6,5));
605f3393e87Sriastradh p[4] = __SHIFTIN(frame->s3d_struct, __BITS(7,4));
606f3393e87Sriastradh if (frame->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
607f3393e87Sriastradh p[5] = __SHIFTIN(frame->s3d_ext_data, __BITS(7,4));
608f3393e87Sriastradh } else {
609f3393e87Sriastradh p[3] = __SHIFTIN(0x0, __BITS(6,5));
610f3393e87Sriastradh }
611f3393e87Sriastradh
612f3393e87Sriastradh hdmi_infoframe_set_checksum(buf, length);
613f3393e87Sriastradh
614f3393e87Sriastradh return length;
615f3393e87Sriastradh }
616f3393e87Sriastradh
6174f8e99e7Sriastradh static int
hdmi_vendor_infoframe_unpack(struct hdmi_vendor_infoframe * frame,const void * buf,size_t size)618f3393e87Sriastradh hdmi_vendor_infoframe_unpack(struct hdmi_vendor_infoframe *frame,
619f3393e87Sriastradh const void *buf, size_t size)
620f3393e87Sriastradh {
621f3393e87Sriastradh const uint8_t *p = buf;
622f3393e87Sriastradh int ret;
623f3393e87Sriastradh
624f3393e87Sriastradh ret = hdmi_infoframe_header_unpack(&frame->header, p, size);
625f3393e87Sriastradh if (ret)
626f3393e87Sriastradh return ret;
627f3393e87Sriastradh if (frame->header.length < 4)
628f3393e87Sriastradh return -EINVAL;
629f3393e87Sriastradh p += HDMI_INFOFRAME_HEADER_SIZE;
630f3393e87Sriastradh size -= HDMI_INFOFRAME_HEADER_SIZE;
631f3393e87Sriastradh
632f3393e87Sriastradh if (p[0] != 0x03 || p[1] != 0x0c || p[2] != 0x00)
633f3393e87Sriastradh return -EINVAL;
634f3393e87Sriastradh
635f3393e87Sriastradh switch (__SHIFTOUT(p[3], __BITS(6,5))) {
636f3393e87Sriastradh case 0x0:
637f3393e87Sriastradh if (frame->header.length != 4)
638f3393e87Sriastradh return -EINVAL;
639f3393e87Sriastradh break;
640f3393e87Sriastradh case 0x1:
641f3393e87Sriastradh if (frame->header.length != 5)
642f3393e87Sriastradh return -EINVAL;
643f3393e87Sriastradh frame->vic = p[4];
644f3393e87Sriastradh break;
645f3393e87Sriastradh case 0x2:
646f3393e87Sriastradh if (frame->header.length < 5)
647f3393e87Sriastradh return -EINVAL;
648f3393e87Sriastradh frame->s3d_struct = __SHIFTOUT(p[4], __BITS(7,4));
649f3393e87Sriastradh if (frame->s3d_struct < HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) {
650f3393e87Sriastradh if (frame->header.length != 5)
651f3393e87Sriastradh return -EINVAL;
652f3393e87Sriastradh } else {
653f3393e87Sriastradh if (frame->header.length != 6)
654f3393e87Sriastradh return -EINVAL;
655f3393e87Sriastradh frame->s3d_ext_data = __SHIFTOUT(p[5], __BITS(7,4));
656f3393e87Sriastradh }
657f3393e87Sriastradh break;
658f3393e87Sriastradh default:
659f3393e87Sriastradh return -EINVAL;
660f3393e87Sriastradh }
661f3393e87Sriastradh
662f3393e87Sriastradh return 0;
663f3393e87Sriastradh }
664f3393e87Sriastradh
665f3393e87Sriastradh /* union infoframe */
666f3393e87Sriastradh
__strong_alias(linux_hdmi_infoframe_pack_only,linux_hdmi_infoframe_pack)667f3393e87Sriastradh __strong_alias(linux_hdmi_infoframe_pack_only,linux_hdmi_infoframe_pack) /* XXX */
668f3393e87Sriastradh
669f3393e87Sriastradh ssize_t
670f3393e87Sriastradh hdmi_infoframe_pack(const union hdmi_infoframe *frame, void *buf, size_t size)
671f3393e87Sriastradh {
672f3393e87Sriastradh
673f3393e87Sriastradh switch (frame->any.type) {
674f3393e87Sriastradh case HDMI_INFOFRAME_TYPE_VENDOR:
675f3393e87Sriastradh return hdmi_vendor_infoframe_pack(&frame->vendor.hdmi, buf,
676f3393e87Sriastradh size);
677d6e94fe5Sriastradh case HDMI_INFOFRAME_TYPE_AVI:
678d6e94fe5Sriastradh return hdmi_avi_infoframe_pack(&frame->avi, buf, size);
679d6e94fe5Sriastradh case HDMI_INFOFRAME_TYPE_SPD:
680d6e94fe5Sriastradh return hdmi_spd_infoframe_pack(&frame->spd, buf, size);
681d6e94fe5Sriastradh case HDMI_INFOFRAME_TYPE_AUDIO:
682d6e94fe5Sriastradh return hdmi_audio_infoframe_pack(&frame->audio, buf, size);
683d6e94fe5Sriastradh case HDMI_INFOFRAME_TYPE_DRM:
684d6e94fe5Sriastradh return hdmi_drm_infoframe_pack(&frame->drm, buf, size);
685f3393e87Sriastradh default:
686f3393e87Sriastradh return -EINVAL;
687f3393e87Sriastradh }
688f3393e87Sriastradh }
689f3393e87Sriastradh
690f3393e87Sriastradh int
hdmi_infoframe_unpack(union hdmi_infoframe * frame,const void * buf,size_t size)691f3393e87Sriastradh hdmi_infoframe_unpack(union hdmi_infoframe *frame, const void *buf,
692f3393e87Sriastradh size_t size)
693f3393e87Sriastradh {
694f3393e87Sriastradh int ret;
695f3393e87Sriastradh
6969f52d00eSriastradh memset(frame, 0, sizeof(*frame));
6979f52d00eSriastradh
69891cb1d74Sriastradh ret = hdmi_infoframe_header_unpack(&frame->any, buf, size);
699f3393e87Sriastradh if (ret)
700f3393e87Sriastradh return ret;
70191cb1d74Sriastradh switch (frame->any.type) {
702f3393e87Sriastradh case HDMI_INFOFRAME_TYPE_VENDOR:
703f3393e87Sriastradh return hdmi_vendor_infoframe_unpack(&frame->vendor.hdmi, buf,
704f3393e87Sriastradh size);
7055dd7fa73Sriastradh case HDMI_INFOFRAME_TYPE_AVI:
7065dd7fa73Sriastradh return hdmi_avi_infoframe_unpack(&frame->avi, buf, size);
7075dd7fa73Sriastradh case HDMI_INFOFRAME_TYPE_SPD:
7085dd7fa73Sriastradh return hdmi_spd_infoframe_unpack(&frame->spd, buf, size);
7095dd7fa73Sriastradh case HDMI_INFOFRAME_TYPE_AUDIO:
7105dd7fa73Sriastradh return hdmi_audio_infoframe_unpack(&frame->audio, buf, size);
7115dd7fa73Sriastradh case HDMI_INFOFRAME_TYPE_DRM:
7125dd7fa73Sriastradh return hdmi_drm_infoframe_unpack(&frame->drm, buf, size);
713f3393e87Sriastradh default:
714f3393e87Sriastradh return -EINVAL;
715f3393e87Sriastradh }
716f3393e87Sriastradh }
717f3393e87Sriastradh
718f3393e87Sriastradh void
hdmi_infoframe_log(const char * level,struct device * device,const union hdmi_infoframe * frame)719f3393e87Sriastradh hdmi_infoframe_log(const char *level, struct device *device,
720f3393e87Sriastradh const union hdmi_infoframe *frame)
721f3393e87Sriastradh {
722f3393e87Sriastradh
723f3393e87Sriastradh hexdump(printf, device_xname(device), frame, sizeof(*frame));
724f3393e87Sriastradh }
725