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