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