xref: /openbsd-src/sys/dev/pci/drm/apple/parser.c (revision a5620f7a90bcfc006d78054c2225501f88bc8382)
15dd0baa8Skettenis // SPDX-License-Identifier: GPL-2.0-only OR MIT
25dd0baa8Skettenis /* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
35dd0baa8Skettenis 
45dd0baa8Skettenis #include <linux/kernel.h>
55dd0baa8Skettenis #include <linux/err.h>
65dd0baa8Skettenis #include <linux/math.h>
75dd0baa8Skettenis #include <linux/string.h>
85dd0baa8Skettenis #include <linux/slab.h>
95dd0baa8Skettenis 
105dd0baa8Skettenis #include <sound/pcm.h> // for sound format masks
115dd0baa8Skettenis 
125dd0baa8Skettenis #include "parser.h"
135dd0baa8Skettenis #include "trace.h"
145dd0baa8Skettenis 
155dd0baa8Skettenis #define DCP_PARSE_HEADER 0xd3
165dd0baa8Skettenis 
175dd0baa8Skettenis enum dcp_parse_type {
185dd0baa8Skettenis 	DCP_TYPE_DICTIONARY = 1,
195dd0baa8Skettenis 	DCP_TYPE_ARRAY = 2,
205dd0baa8Skettenis 	DCP_TYPE_INT64 = 4,
215dd0baa8Skettenis 	DCP_TYPE_STRING = 9,
225dd0baa8Skettenis 	DCP_TYPE_BLOB = 10,
235dd0baa8Skettenis 	DCP_TYPE_BOOL = 11
245dd0baa8Skettenis };
255dd0baa8Skettenis 
265dd0baa8Skettenis struct dcp_parse_tag {
275dd0baa8Skettenis 	unsigned int size : 24;
285dd0baa8Skettenis 	enum dcp_parse_type type : 5;
295dd0baa8Skettenis 	unsigned int padding : 2;
305dd0baa8Skettenis 	bool last : 1;
315dd0baa8Skettenis } __packed;
325dd0baa8Skettenis 
parse_bytes(struct dcp_parse_ctx * ctx,size_t count)335dd0baa8Skettenis static const void *parse_bytes(struct dcp_parse_ctx *ctx, size_t count)
345dd0baa8Skettenis {
355dd0baa8Skettenis 	const void *ptr = ctx->blob + ctx->pos;
365dd0baa8Skettenis 
375dd0baa8Skettenis 	if (ctx->pos + count > ctx->len)
385dd0baa8Skettenis 		return ERR_PTR(-EINVAL);
395dd0baa8Skettenis 
405dd0baa8Skettenis 	ctx->pos += count;
415dd0baa8Skettenis 	return ptr;
425dd0baa8Skettenis }
435dd0baa8Skettenis 
parse_u32(struct dcp_parse_ctx * ctx)445dd0baa8Skettenis static const u32 *parse_u32(struct dcp_parse_ctx *ctx)
455dd0baa8Skettenis {
465dd0baa8Skettenis 	return parse_bytes(ctx, sizeof(u32));
475dd0baa8Skettenis }
485dd0baa8Skettenis 
parse_tag(struct dcp_parse_ctx * ctx)495dd0baa8Skettenis static const struct dcp_parse_tag *parse_tag(struct dcp_parse_ctx *ctx)
505dd0baa8Skettenis {
515dd0baa8Skettenis 	const struct dcp_parse_tag *tag;
525dd0baa8Skettenis 
535dd0baa8Skettenis 	/* Align to 32-bits */
545dd0baa8Skettenis 	ctx->pos = round_up(ctx->pos, 4);
555dd0baa8Skettenis 
565dd0baa8Skettenis 	tag = parse_bytes(ctx, sizeof(struct dcp_parse_tag));
575dd0baa8Skettenis 
585dd0baa8Skettenis 	if (IS_ERR(tag))
595dd0baa8Skettenis 		return tag;
605dd0baa8Skettenis 
615dd0baa8Skettenis 	if (tag->padding)
625dd0baa8Skettenis 		return ERR_PTR(-EINVAL);
635dd0baa8Skettenis 
645dd0baa8Skettenis 	return tag;
655dd0baa8Skettenis }
665dd0baa8Skettenis 
parse_tag_of_type(struct dcp_parse_ctx * ctx,enum dcp_parse_type type)675dd0baa8Skettenis static const struct dcp_parse_tag *parse_tag_of_type(struct dcp_parse_ctx *ctx,
685dd0baa8Skettenis 					       enum dcp_parse_type type)
695dd0baa8Skettenis {
705dd0baa8Skettenis 	const struct dcp_parse_tag *tag = parse_tag(ctx);
715dd0baa8Skettenis 
725dd0baa8Skettenis 	if (IS_ERR(tag))
735dd0baa8Skettenis 		return tag;
745dd0baa8Skettenis 
755dd0baa8Skettenis 	if (tag->type != type)
765dd0baa8Skettenis 		return ERR_PTR(-EINVAL);
775dd0baa8Skettenis 
785dd0baa8Skettenis 	return tag;
795dd0baa8Skettenis }
805dd0baa8Skettenis 
skip(struct dcp_parse_ctx * handle)815dd0baa8Skettenis static int skip(struct dcp_parse_ctx *handle)
825dd0baa8Skettenis {
835dd0baa8Skettenis 	const struct dcp_parse_tag *tag = parse_tag(handle);
845dd0baa8Skettenis 	int ret = 0;
855dd0baa8Skettenis 	int i;
865dd0baa8Skettenis 
875dd0baa8Skettenis 	if (IS_ERR(tag))
885dd0baa8Skettenis 		return PTR_ERR(tag);
895dd0baa8Skettenis 
905dd0baa8Skettenis 	switch (tag->type) {
915dd0baa8Skettenis 	case DCP_TYPE_DICTIONARY:
925dd0baa8Skettenis 		for (i = 0; i < tag->size; ++i) {
935dd0baa8Skettenis 			ret |= skip(handle); /* key */
945dd0baa8Skettenis 			ret |= skip(handle); /* value */
955dd0baa8Skettenis 		}
965dd0baa8Skettenis 
975dd0baa8Skettenis 		return ret;
985dd0baa8Skettenis 
995dd0baa8Skettenis 	case DCP_TYPE_ARRAY:
1005dd0baa8Skettenis 		for (i = 0; i < tag->size; ++i)
1015dd0baa8Skettenis 			ret |= skip(handle);
1025dd0baa8Skettenis 
1035dd0baa8Skettenis 		return ret;
1045dd0baa8Skettenis 
1055dd0baa8Skettenis 	case DCP_TYPE_INT64:
1065dd0baa8Skettenis 		handle->pos += sizeof(s64);
1075dd0baa8Skettenis 		return 0;
1085dd0baa8Skettenis 
1095dd0baa8Skettenis 	case DCP_TYPE_STRING:
1105dd0baa8Skettenis 	case DCP_TYPE_BLOB:
1115dd0baa8Skettenis 		handle->pos += tag->size;
1125dd0baa8Skettenis 		return 0;
1135dd0baa8Skettenis 
1145dd0baa8Skettenis 	case DCP_TYPE_BOOL:
1155dd0baa8Skettenis 		return 0;
1165dd0baa8Skettenis 
1175dd0baa8Skettenis 	default:
1185dd0baa8Skettenis 		return -EINVAL;
1195dd0baa8Skettenis 	}
1205dd0baa8Skettenis }
1215dd0baa8Skettenis 
skip_pair(struct dcp_parse_ctx * handle)1225dd0baa8Skettenis static int skip_pair(struct dcp_parse_ctx *handle)
1235dd0baa8Skettenis {
1245dd0baa8Skettenis 	int ret;
1255dd0baa8Skettenis 
1265dd0baa8Skettenis 	ret = skip(handle);
1275dd0baa8Skettenis 	if (ret)
1285dd0baa8Skettenis 		return ret;
1295dd0baa8Skettenis 
1305dd0baa8Skettenis 	return skip(handle);
1315dd0baa8Skettenis }
1325dd0baa8Skettenis 
consume_string(struct dcp_parse_ctx * ctx,const char * specimen)1335dd0baa8Skettenis static bool consume_string(struct dcp_parse_ctx *ctx, const char *specimen)
1345dd0baa8Skettenis {
1355dd0baa8Skettenis 	const struct dcp_parse_tag *tag;
1365dd0baa8Skettenis 	const char *key;
1375dd0baa8Skettenis 	ctx->pos = round_up(ctx->pos, 4);
1385dd0baa8Skettenis 
1395dd0baa8Skettenis 	if (ctx->pos + sizeof(*tag) + strlen(specimen) - 1 > ctx->len)
1405dd0baa8Skettenis 		return false;
1415dd0baa8Skettenis 	tag = ctx->blob + ctx->pos;
1425dd0baa8Skettenis 	key = ctx->blob + ctx->pos + sizeof(*tag);
1435dd0baa8Skettenis 	if (tag->padding)
1445dd0baa8Skettenis 		return false;
1455dd0baa8Skettenis 
1465dd0baa8Skettenis 	if (tag->type != DCP_TYPE_STRING ||
1475dd0baa8Skettenis 	    tag->size != strlen(specimen) ||
1485dd0baa8Skettenis 	    strncmp(key, specimen, tag->size))
1495dd0baa8Skettenis 		return false;
1505dd0baa8Skettenis 
1515dd0baa8Skettenis 	skip(ctx);
1525dd0baa8Skettenis 	return true;
1535dd0baa8Skettenis }
1545dd0baa8Skettenis 
1555dd0baa8Skettenis /* Caller must free the result */
parse_string(struct dcp_parse_ctx * handle)1565dd0baa8Skettenis static char *parse_string(struct dcp_parse_ctx *handle)
1575dd0baa8Skettenis {
1585dd0baa8Skettenis 	const struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_STRING);
1595dd0baa8Skettenis 	const char *in;
1605dd0baa8Skettenis 	char *out;
1615dd0baa8Skettenis 
1625dd0baa8Skettenis 	if (IS_ERR(tag))
1635dd0baa8Skettenis 		return (void *)tag;
1645dd0baa8Skettenis 
1655dd0baa8Skettenis 	in = parse_bytes(handle, tag->size);
1665dd0baa8Skettenis 	if (IS_ERR(in))
1675dd0baa8Skettenis 		return (void *)in;
1685dd0baa8Skettenis 
1695dd0baa8Skettenis 	out = kmalloc(tag->size + 1, GFP_KERNEL);
1705dd0baa8Skettenis 
1715dd0baa8Skettenis 	memcpy(out, in, tag->size);
1725dd0baa8Skettenis 	out[tag->size] = '\0';
1735dd0baa8Skettenis 	return out;
1745dd0baa8Skettenis }
1755dd0baa8Skettenis 
parse_int(struct dcp_parse_ctx * handle,s64 * value)1765dd0baa8Skettenis static int parse_int(struct dcp_parse_ctx *handle, s64 *value)
1775dd0baa8Skettenis {
1785dd0baa8Skettenis 	const void *tag = parse_tag_of_type(handle, DCP_TYPE_INT64);
1795dd0baa8Skettenis 	const s64 *in;
1805dd0baa8Skettenis 
1815dd0baa8Skettenis 	if (IS_ERR(tag))
1825dd0baa8Skettenis 		return PTR_ERR(tag);
1835dd0baa8Skettenis 
1845dd0baa8Skettenis 	in = parse_bytes(handle, sizeof(s64));
1855dd0baa8Skettenis 
1865dd0baa8Skettenis 	if (IS_ERR(in))
1875dd0baa8Skettenis 		return PTR_ERR(in);
1885dd0baa8Skettenis 
1895dd0baa8Skettenis 	memcpy(value, in, sizeof(*value));
1905dd0baa8Skettenis 	return 0;
1915dd0baa8Skettenis }
1925dd0baa8Skettenis 
parse_bool(struct dcp_parse_ctx * handle,bool * b)1935dd0baa8Skettenis static int parse_bool(struct dcp_parse_ctx *handle, bool *b)
1945dd0baa8Skettenis {
1955dd0baa8Skettenis 	const struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BOOL);
1965dd0baa8Skettenis 
1975dd0baa8Skettenis 	if (IS_ERR(tag))
1985dd0baa8Skettenis 		return PTR_ERR(tag);
1995dd0baa8Skettenis 
2005dd0baa8Skettenis 	*b = !!tag->size;
2015dd0baa8Skettenis 	return 0;
2025dd0baa8Skettenis }
2035dd0baa8Skettenis 
parse_blob(struct dcp_parse_ctx * handle,size_t size,u8 const ** blob)2045dd0baa8Skettenis static int parse_blob(struct dcp_parse_ctx *handle, size_t size, u8 const **blob)
2055dd0baa8Skettenis {
2065dd0baa8Skettenis 	const struct dcp_parse_tag *tag = parse_tag_of_type(handle, DCP_TYPE_BLOB);
2075dd0baa8Skettenis 	const u8 *out;
2085dd0baa8Skettenis 
2095dd0baa8Skettenis 	if (IS_ERR(tag))
2105dd0baa8Skettenis 		return PTR_ERR(tag);
2115dd0baa8Skettenis 
2125dd0baa8Skettenis 	if (tag->size < size)
2135dd0baa8Skettenis 		return -EINVAL;
2145dd0baa8Skettenis 
2155dd0baa8Skettenis 	out = parse_bytes(handle, tag->size);
2165dd0baa8Skettenis 
2175dd0baa8Skettenis 	if (IS_ERR(out))
2185dd0baa8Skettenis 		return PTR_ERR(out);
2195dd0baa8Skettenis 
2205dd0baa8Skettenis 	*blob = out;
2215dd0baa8Skettenis 	return 0;
2225dd0baa8Skettenis }
2235dd0baa8Skettenis 
2245dd0baa8Skettenis struct iterator {
2255dd0baa8Skettenis 	struct dcp_parse_ctx *handle;
2265dd0baa8Skettenis 	u32 idx, len;
2275dd0baa8Skettenis };
2285dd0baa8Skettenis 
iterator_begin(struct dcp_parse_ctx * handle,struct iterator * it,bool dict)2295dd0baa8Skettenis static int iterator_begin(struct dcp_parse_ctx *handle, struct iterator *it,
2305dd0baa8Skettenis 			  bool dict)
2315dd0baa8Skettenis {
2325dd0baa8Skettenis 	const struct dcp_parse_tag *tag;
2335dd0baa8Skettenis 	enum dcp_parse_type type = dict ? DCP_TYPE_DICTIONARY : DCP_TYPE_ARRAY;
2345dd0baa8Skettenis 
2355dd0baa8Skettenis 	*it = (struct iterator) {
2365dd0baa8Skettenis 		.handle = handle,
2375dd0baa8Skettenis 		.idx = 0
2385dd0baa8Skettenis 	};
2395dd0baa8Skettenis 
2405dd0baa8Skettenis 	tag = parse_tag_of_type(it->handle, type);
2415dd0baa8Skettenis 	if (IS_ERR(tag))
2425dd0baa8Skettenis 		return PTR_ERR(tag);
2435dd0baa8Skettenis 
2445dd0baa8Skettenis 	it->len = tag->size;
2455dd0baa8Skettenis 	return 0;
2465dd0baa8Skettenis }
2475dd0baa8Skettenis 
2485dd0baa8Skettenis #define dcp_parse_foreach_in_array(handle, it)                                 \
2495dd0baa8Skettenis 	for (iterator_begin(handle, &it, false); it.idx < it.len; ++it.idx)
2505dd0baa8Skettenis #define dcp_parse_foreach_in_dict(handle, it)                                  \
2515dd0baa8Skettenis 	for (iterator_begin(handle, &it, true); it.idx < it.len; ++it.idx)
2525dd0baa8Skettenis 
parse(const void * blob,size_t size,struct dcp_parse_ctx * ctx)2535dd0baa8Skettenis int parse(const void *blob, size_t size, struct dcp_parse_ctx *ctx)
2545dd0baa8Skettenis {
2555dd0baa8Skettenis 	const u32 *header;
2565dd0baa8Skettenis 
2575dd0baa8Skettenis 	*ctx = (struct dcp_parse_ctx) {
2585dd0baa8Skettenis 		.blob = blob,
2595dd0baa8Skettenis 		.len = size,
2605dd0baa8Skettenis 		.pos = 0,
2615dd0baa8Skettenis 	};
2625dd0baa8Skettenis 
2635dd0baa8Skettenis 	header = parse_u32(ctx);
2645dd0baa8Skettenis 	if (IS_ERR(header))
2655dd0baa8Skettenis 		return PTR_ERR(header);
2665dd0baa8Skettenis 
2675dd0baa8Skettenis 	if (*header != DCP_PARSE_HEADER)
2685dd0baa8Skettenis 		return -EINVAL;
2695dd0baa8Skettenis 
2705dd0baa8Skettenis 	return 0;
2715dd0baa8Skettenis }
2725dd0baa8Skettenis 
parse_dimension(struct dcp_parse_ctx * handle,struct dimension * dim)2735dd0baa8Skettenis static int parse_dimension(struct dcp_parse_ctx *handle, struct dimension *dim)
2745dd0baa8Skettenis {
2755dd0baa8Skettenis 	struct iterator it;
2765dd0baa8Skettenis 	int ret = 0;
2775dd0baa8Skettenis 
2785dd0baa8Skettenis 	dcp_parse_foreach_in_dict(handle, it) {
2795dd0baa8Skettenis 		char *key = parse_string(it.handle);
2805dd0baa8Skettenis 
2815dd0baa8Skettenis 		if (IS_ERR(key))
2825dd0baa8Skettenis 			ret = PTR_ERR(key);
2835dd0baa8Skettenis 		else if (!strcmp(key, "Active"))
2845dd0baa8Skettenis 			ret = parse_int(it.handle, &dim->active);
2855dd0baa8Skettenis 		else if (!strcmp(key, "Total"))
2865dd0baa8Skettenis 			ret = parse_int(it.handle, &dim->total);
2875dd0baa8Skettenis 		else if (!strcmp(key, "FrontPorch"))
2885dd0baa8Skettenis 			ret = parse_int(it.handle, &dim->front_porch);
2895dd0baa8Skettenis 		else if (!strcmp(key, "SyncWidth"))
2905dd0baa8Skettenis 			ret = parse_int(it.handle, &dim->sync_width);
2915dd0baa8Skettenis 		else if (!strcmp(key, "PreciseSyncRate"))
2925dd0baa8Skettenis 			ret = parse_int(it.handle, &dim->precise_sync_rate);
2935dd0baa8Skettenis 		else
2945dd0baa8Skettenis 			skip(it.handle);
2955dd0baa8Skettenis 
2965dd0baa8Skettenis 		if (!IS_ERR_OR_NULL(key))
2975dd0baa8Skettenis 			kfree(key);
2985dd0baa8Skettenis 
2995dd0baa8Skettenis 		if (ret)
3005dd0baa8Skettenis 			return ret;
3015dd0baa8Skettenis 	}
3025dd0baa8Skettenis 
3035dd0baa8Skettenis 	return 0;
3045dd0baa8Skettenis }
3055dd0baa8Skettenis 
3065dd0baa8Skettenis struct color_mode {
3075dd0baa8Skettenis 	s64 colorimetry;
3085dd0baa8Skettenis 	s64 depth;
3095dd0baa8Skettenis 	s64 dynamic_range;
3105dd0baa8Skettenis 	s64 eotf;
3115dd0baa8Skettenis 	s64 id;
3125dd0baa8Skettenis 	s64 pixel_encoding;
3135dd0baa8Skettenis 	s64 score;
3145dd0baa8Skettenis };
3155dd0baa8Skettenis 
fill_color_mode(struct dcp_color_mode * color,struct color_mode * cmode)3165dd0baa8Skettenis static int fill_color_mode(struct dcp_color_mode *color,
3175dd0baa8Skettenis 			   struct color_mode *cmode)
3185dd0baa8Skettenis {
3195dd0baa8Skettenis 	if (color->score >= cmode->score)
3205dd0baa8Skettenis 		return 0;
3215dd0baa8Skettenis 
3225dd0baa8Skettenis 	if (cmode->colorimetry < 0 || cmode->colorimetry >= DCP_COLORIMETRY_COUNT)
3235dd0baa8Skettenis 		return -EINVAL;
3245dd0baa8Skettenis 	if (cmode->depth < 8 || cmode->depth > 12)
3255dd0baa8Skettenis 		return -EINVAL;
3265dd0baa8Skettenis 	if (cmode->dynamic_range < 0 || cmode->dynamic_range >= DCP_COLOR_YCBCR_RANGE_COUNT)
3275dd0baa8Skettenis 		return -EINVAL;
3285dd0baa8Skettenis 	if (cmode->eotf < 0 || cmode->eotf >= DCP_EOTF_COUNT)
3295dd0baa8Skettenis 		return -EINVAL;
3305dd0baa8Skettenis 	if (cmode->pixel_encoding < 0 || cmode->pixel_encoding >= DCP_COLOR_FORMAT_COUNT)
3315dd0baa8Skettenis 		return -EINVAL;
3325dd0baa8Skettenis 
3335dd0baa8Skettenis 	color->score = cmode->score;
3345dd0baa8Skettenis 	color->id = cmode->id;
3355dd0baa8Skettenis 	color->eotf = cmode->eotf;
3365dd0baa8Skettenis 	color->format = cmode->pixel_encoding;
3375dd0baa8Skettenis 	color->colorimetry = cmode->colorimetry;
3385dd0baa8Skettenis 	color->range = cmode->dynamic_range;
3395dd0baa8Skettenis 	color->depth = cmode->depth;
3405dd0baa8Skettenis 
3415dd0baa8Skettenis 	return 0;
3425dd0baa8Skettenis }
3435dd0baa8Skettenis 
parse_color_modes(struct dcp_parse_ctx * handle,struct dcp_display_mode * out)3445dd0baa8Skettenis static int parse_color_modes(struct dcp_parse_ctx *handle,
3455dd0baa8Skettenis 			     struct dcp_display_mode *out)
3465dd0baa8Skettenis {
3475dd0baa8Skettenis 	struct iterator outer_it;
3485dd0baa8Skettenis 	int ret = 0;
3495dd0baa8Skettenis 	out->sdr_444.score = -1;
3505dd0baa8Skettenis 	out->sdr_rgb.score = -1;
3515dd0baa8Skettenis 	out->best.score = -1;
3525dd0baa8Skettenis 
3535dd0baa8Skettenis 	dcp_parse_foreach_in_array(handle, outer_it) {
3545dd0baa8Skettenis 		struct iterator it;
3555dd0baa8Skettenis 		bool is_virtual = true;
3565dd0baa8Skettenis 		struct color_mode cmode;
3575dd0baa8Skettenis 
3585dd0baa8Skettenis 		dcp_parse_foreach_in_dict(handle, it) {
3595dd0baa8Skettenis 			char *key = parse_string(it.handle);
3605dd0baa8Skettenis 
3615dd0baa8Skettenis 			if (IS_ERR(key))
3625dd0baa8Skettenis 				ret = PTR_ERR(key);
3635dd0baa8Skettenis 			else if (!strcmp(key, "Colorimetry"))
3645dd0baa8Skettenis 				ret = parse_int(it.handle, &cmode.colorimetry);
3655dd0baa8Skettenis 			else if (!strcmp(key, "Depth"))
3665dd0baa8Skettenis 				ret = parse_int(it.handle, &cmode.depth);
3675dd0baa8Skettenis 			else if (!strcmp(key, "DynamicRange"))
3685dd0baa8Skettenis 				ret = parse_int(it.handle, &cmode.dynamic_range);
3695dd0baa8Skettenis 			else if (!strcmp(key, "EOTF"))
3705dd0baa8Skettenis 				ret = parse_int(it.handle, &cmode.eotf);
3715dd0baa8Skettenis 			else if (!strcmp(key, "ID"))
3725dd0baa8Skettenis 				ret = parse_int(it.handle, &cmode.id);
3735dd0baa8Skettenis 			else if (!strcmp(key, "IsVirtual"))
3745dd0baa8Skettenis 				ret = parse_bool(it.handle, &is_virtual);
3755dd0baa8Skettenis 			else if (!strcmp(key, "PixelEncoding"))
3765dd0baa8Skettenis 				ret = parse_int(it.handle, &cmode.pixel_encoding);
3775dd0baa8Skettenis 			else if (!strcmp(key, "Score"))
3785dd0baa8Skettenis 				ret = parse_int(it.handle, &cmode.score);
3795dd0baa8Skettenis 			else
3805dd0baa8Skettenis 				skip(it.handle);
3815dd0baa8Skettenis 
3825dd0baa8Skettenis 			if (!IS_ERR_OR_NULL(key))
3835dd0baa8Skettenis 				kfree(key);
3845dd0baa8Skettenis 
3855dd0baa8Skettenis 			if (ret)
3865dd0baa8Skettenis 				return ret;
3875dd0baa8Skettenis 		}
3885dd0baa8Skettenis 
3895dd0baa8Skettenis 		/* Skip virtual or partial entries */
3905dd0baa8Skettenis 		if (is_virtual || cmode.score < 0 || cmode.id < 0)
3915dd0baa8Skettenis 			continue;
3925dd0baa8Skettenis 
3935dd0baa8Skettenis 		trace_iomfb_color_mode(handle->dcp, cmode.id, cmode.score,
3945dd0baa8Skettenis 				       cmode.depth, cmode.colorimetry,
3955dd0baa8Skettenis 				       cmode.eotf, cmode.dynamic_range,
3965dd0baa8Skettenis 				       cmode.pixel_encoding);
3975dd0baa8Skettenis 
3985dd0baa8Skettenis 		if (cmode.eotf == DCP_EOTF_SDR_GAMMA) {
3995dd0baa8Skettenis 			if (cmode.pixel_encoding == DCP_COLOR_FORMAT_RGB &&
4005dd0baa8Skettenis 				cmode.depth <= 10)
4015dd0baa8Skettenis 				fill_color_mode(&out->sdr_rgb, &cmode);
4025dd0baa8Skettenis 			else if (cmode.pixel_encoding == DCP_COLOR_FORMAT_YCBCR444 &&
4035dd0baa8Skettenis 				cmode.depth <= 10)
4045dd0baa8Skettenis 				fill_color_mode(&out->sdr_444, &cmode);
4055dd0baa8Skettenis 			fill_color_mode(&out->sdr, &cmode);
4065dd0baa8Skettenis 		}
4075dd0baa8Skettenis 		fill_color_mode(&out->best, &cmode);
4085dd0baa8Skettenis 	}
4095dd0baa8Skettenis 
4105dd0baa8Skettenis 	return 0;
4115dd0baa8Skettenis }
4125dd0baa8Skettenis 
4135dd0baa8Skettenis /*
4145dd0baa8Skettenis  * Calculate the pixel clock for a mode given the 16:16 fixed-point refresh
4155dd0baa8Skettenis  * rate. The pixel clock is the refresh rate times the pixel count. DRM
4165dd0baa8Skettenis  * specifies the clock in kHz. The intermediate result may overflow a u32, so
4175dd0baa8Skettenis  * use a u64 where required.
4185dd0baa8Skettenis  */
calculate_clock(struct dimension * horiz,struct dimension * vert)4195dd0baa8Skettenis static u32 calculate_clock(struct dimension *horiz, struct dimension *vert)
4205dd0baa8Skettenis {
4215dd0baa8Skettenis 	u32 pixels = horiz->total * vert->total;
4225dd0baa8Skettenis 	u64 clock = mul_u32_u32(pixels, vert->precise_sync_rate);
4235dd0baa8Skettenis 
4245dd0baa8Skettenis 	return DIV_ROUND_CLOSEST_ULL(clock >> 16, 1000);
4255dd0baa8Skettenis }
4265dd0baa8Skettenis 
parse_mode(struct dcp_parse_ctx * handle,struct dcp_display_mode * out,s64 * score,int width_mm,int height_mm,unsigned notch_height)4275dd0baa8Skettenis static int parse_mode(struct dcp_parse_ctx *handle,
4285dd0baa8Skettenis 		      struct dcp_display_mode *out, s64 *score, int width_mm,
4295dd0baa8Skettenis 		      int height_mm, unsigned notch_height)
4305dd0baa8Skettenis {
4315dd0baa8Skettenis 	int ret = 0;
4325dd0baa8Skettenis 	struct iterator it;
4335dd0baa8Skettenis 	struct dimension horiz, vert;
4345dd0baa8Skettenis 	s64 id = -1;
4355dd0baa8Skettenis 	s64 best_color_mode = -1;
4365dd0baa8Skettenis 	bool is_virtual = false;
4375dd0baa8Skettenis 	struct drm_display_mode *mode = &out->mode;
4385dd0baa8Skettenis 
4395dd0baa8Skettenis 	dcp_parse_foreach_in_dict(handle, it) {
4405dd0baa8Skettenis 		char *key = parse_string(it.handle);
4415dd0baa8Skettenis 
4425dd0baa8Skettenis 		if (IS_ERR(key))
4435dd0baa8Skettenis 			ret = PTR_ERR(key);
4445dd0baa8Skettenis 		else if (is_virtual)
4455dd0baa8Skettenis 			skip(it.handle);
4465dd0baa8Skettenis 		else if (!strcmp(key, "HorizontalAttributes"))
4475dd0baa8Skettenis 			ret = parse_dimension(it.handle, &horiz);
4485dd0baa8Skettenis 		else if (!strcmp(key, "VerticalAttributes"))
4495dd0baa8Skettenis 			ret = parse_dimension(it.handle, &vert);
4505dd0baa8Skettenis 		else if (!strcmp(key, "ColorModes"))
4515dd0baa8Skettenis 			ret = parse_color_modes(it.handle, out);
4525dd0baa8Skettenis 		else if (!strcmp(key, "ID"))
4535dd0baa8Skettenis 			ret = parse_int(it.handle, &id);
4545dd0baa8Skettenis 		else if (!strcmp(key, "IsVirtual"))
4555dd0baa8Skettenis 			ret = parse_bool(it.handle, &is_virtual);
4565dd0baa8Skettenis 		else if (!strcmp(key, "Score"))
4575dd0baa8Skettenis 			ret = parse_int(it.handle, score);
4585dd0baa8Skettenis 		else
4595dd0baa8Skettenis 			skip(it.handle);
4605dd0baa8Skettenis 
4615dd0baa8Skettenis 		if (!IS_ERR_OR_NULL(key))
4625dd0baa8Skettenis 			kfree(key);
4635dd0baa8Skettenis 
4645dd0baa8Skettenis 		if (ret) {
4655dd0baa8Skettenis 			trace_iomfb_parse_mode_fail(id, &horiz, &vert, best_color_mode, is_virtual, *score);
4665dd0baa8Skettenis 			return ret;
4675dd0baa8Skettenis 		}
4685dd0baa8Skettenis 	}
4695dd0baa8Skettenis 	if (out->sdr_rgb.score >= 0)
4705dd0baa8Skettenis 		best_color_mode = out->sdr_rgb.id;
4715dd0baa8Skettenis 	else if (out->sdr_444.score >= 0)
4725dd0baa8Skettenis 		best_color_mode = out->sdr_444.id;
4735dd0baa8Skettenis 	else if (out->sdr.score >= 0)
4745dd0baa8Skettenis 		best_color_mode = out->sdr.id;
4755dd0baa8Skettenis 	else if (out->best.score >= 0)
4765dd0baa8Skettenis 		best_color_mode = out->best.id;
4775dd0baa8Skettenis 
4785dd0baa8Skettenis 	trace_iomfb_parse_mode_success(id, &horiz, &vert, best_color_mode,
4795dd0baa8Skettenis 				       is_virtual, *score);
4805dd0baa8Skettenis 
4815dd0baa8Skettenis 	/*
4825dd0baa8Skettenis 	 * Reject modes without valid color mode.
4835dd0baa8Skettenis 	 */
4845dd0baa8Skettenis 	if (best_color_mode < 0)
4855dd0baa8Skettenis 		return -EINVAL;
4865dd0baa8Skettenis 
4875dd0baa8Skettenis 	/*
4885dd0baa8Skettenis 	 * We need to skip virtual modes. In some cases, virtual modes are "too
4895dd0baa8Skettenis 	 * big" for the monitor and can cause breakage. It is unclear why the
4905dd0baa8Skettenis 	 * DCP reports these modes at all. Treat as a recoverable error.
4915dd0baa8Skettenis 	 */
4925dd0baa8Skettenis 	if (is_virtual)
4935dd0baa8Skettenis 		return -EINVAL;
4945dd0baa8Skettenis 
4955dd0baa8Skettenis 	/*
4965dd0baa8Skettenis 	 * HACK:
4975dd0baa8Skettenis 	 * Ignore the 120 Hz mode on j314/j316 (identified by resolution).
4985dd0baa8Skettenis 	 * DCP limits normal swaps to 60 Hz anyway and the 120 Hz mode might
4995dd0baa8Skettenis 	 * cause choppiness with X11.
5005dd0baa8Skettenis 	 * Just downscoring it and thus making the 60 Hz mode the preferred mode
5015dd0baa8Skettenis 	 * seems not enough for some user space.
5025dd0baa8Skettenis 	 */
5035dd0baa8Skettenis 	if (vert.precise_sync_rate >> 16 == 120 &&
5045dd0baa8Skettenis 	    ((horiz.active == 3024 && vert.active == 1964) ||
5055dd0baa8Skettenis 	     (horiz.active == 3456 && vert.active == 2234)))
5065dd0baa8Skettenis 		return -EINVAL;
5075dd0baa8Skettenis 
5085dd0baa8Skettenis 	/*
5095dd0baa8Skettenis 	 * HACK: reject refresh modes with a pixel clock above 926484,480 kHz
5105dd0baa8Skettenis 	 *       (bandwidth limit reported by dcp). This allows 4k 100Hz and
5115dd0baa8Skettenis 	 *       5k 60Hz but not much beyond.
5125dd0baa8Skettenis 	 *       DSC setup seems to require additional steps
5135dd0baa8Skettenis 	 */
5145dd0baa8Skettenis 	if (calculate_clock(&horiz, &vert) > 926484) {
5155dd0baa8Skettenis 		pr_info("dcp: rejecting mode %lldx%lld@%lld.%03lld (pixel clk:%d)\n",
5165dd0baa8Skettenis 			horiz.active, vert.active, vert.precise_sync_rate >> 16,
5175dd0baa8Skettenis 			((1000 * vert.precise_sync_rate) >> 16) % 1000,
5185dd0baa8Skettenis 			calculate_clock(&horiz, &vert));
5195dd0baa8Skettenis 		return -EINVAL;
5205dd0baa8Skettenis 	}
5215dd0baa8Skettenis 
5225dd0baa8Skettenis 	vert.active -= notch_height;
5235dd0baa8Skettenis 	vert.sync_width += notch_height;
5245dd0baa8Skettenis 
5255dd0baa8Skettenis 	/* From here we must succeed. Start filling out the mode. */
5265dd0baa8Skettenis 	*mode = (struct drm_display_mode) {
5275dd0baa8Skettenis 		.type = DRM_MODE_TYPE_DRIVER,
5285dd0baa8Skettenis 		.clock = calculate_clock(&horiz, &vert),
5295dd0baa8Skettenis 
5305dd0baa8Skettenis 		.vdisplay = vert.active,
5315dd0baa8Skettenis 		.vsync_start = vert.active + vert.front_porch,
5325dd0baa8Skettenis 		.vsync_end = vert.active + vert.front_porch + vert.sync_width,
5335dd0baa8Skettenis 		.vtotal = vert.total,
5345dd0baa8Skettenis 
5355dd0baa8Skettenis 		.hdisplay = horiz.active,
5365dd0baa8Skettenis 		.hsync_start = horiz.active + horiz.front_porch,
5375dd0baa8Skettenis 		.hsync_end = horiz.active + horiz.front_porch +
5385dd0baa8Skettenis 			     horiz.sync_width,
5395dd0baa8Skettenis 		.htotal = horiz.total,
5405dd0baa8Skettenis 
5415dd0baa8Skettenis 		.width_mm = width_mm,
5425dd0baa8Skettenis 		.height_mm = height_mm,
5435dd0baa8Skettenis 	};
5445dd0baa8Skettenis 
5455dd0baa8Skettenis 	drm_mode_set_name(mode);
5465dd0baa8Skettenis 
5475dd0baa8Skettenis 	out->timing_mode_id = id;
5485dd0baa8Skettenis 	out->color_mode_id = best_color_mode;
5495dd0baa8Skettenis 
5505dd0baa8Skettenis 	trace_iomfb_timing_mode(handle->dcp, id, *score, horiz.active,
5515dd0baa8Skettenis 				vert.active, vert.precise_sync_rate,
5525dd0baa8Skettenis 				best_color_mode);
5535dd0baa8Skettenis 
5545dd0baa8Skettenis 	return 0;
5555dd0baa8Skettenis }
5565dd0baa8Skettenis 
enumerate_modes(struct dcp_parse_ctx * handle,unsigned int * count,int width_mm,int height_mm,unsigned notch_height)5575dd0baa8Skettenis struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle,
5585dd0baa8Skettenis 					 unsigned int *count, int width_mm,
5595dd0baa8Skettenis 					 int height_mm, unsigned notch_height)
5605dd0baa8Skettenis {
5615dd0baa8Skettenis 	struct iterator it;
5625dd0baa8Skettenis 	int ret;
5635dd0baa8Skettenis 	struct dcp_display_mode *mode, *modes;
5645dd0baa8Skettenis 	struct dcp_display_mode *best_mode = NULL;
5655dd0baa8Skettenis 	s64 score, best_score = -1;
5665dd0baa8Skettenis 
5675dd0baa8Skettenis 	ret = iterator_begin(handle, &it, false);
5685dd0baa8Skettenis 
5695dd0baa8Skettenis 	if (ret)
5705dd0baa8Skettenis 		return ERR_PTR(ret);
5715dd0baa8Skettenis 
5725dd0baa8Skettenis 	/* Start with a worst case allocation */
5735dd0baa8Skettenis 	modes = kmalloc_array(it.len, sizeof(*modes), GFP_KERNEL);
5745dd0baa8Skettenis 	*count = 0;
5755dd0baa8Skettenis 
5765dd0baa8Skettenis 	if (!modes)
5775dd0baa8Skettenis 		return ERR_PTR(-ENOMEM);
5785dd0baa8Skettenis 
5795dd0baa8Skettenis 	for (; it.idx < it.len; ++it.idx) {
5805dd0baa8Skettenis 		mode = &modes[*count];
5815dd0baa8Skettenis 		ret = parse_mode(it.handle, mode, &score, width_mm, height_mm, notch_height);
5825dd0baa8Skettenis 
5835dd0baa8Skettenis 		/* Errors for a single mode are recoverable -- just skip it. */
5845dd0baa8Skettenis 		if (ret)
5855dd0baa8Skettenis 			continue;
5865dd0baa8Skettenis 
5875dd0baa8Skettenis 		/* Process a successful mode */
5885dd0baa8Skettenis 		(*count)++;
5895dd0baa8Skettenis 
5905dd0baa8Skettenis 		if (score > best_score) {
5915dd0baa8Skettenis 			best_score = score;
5925dd0baa8Skettenis 			best_mode = mode;
5935dd0baa8Skettenis 		}
5945dd0baa8Skettenis 	}
5955dd0baa8Skettenis 
5965dd0baa8Skettenis 	if (best_mode != NULL)
5975dd0baa8Skettenis 		best_mode->mode.type |= DRM_MODE_TYPE_PREFERRED;
5985dd0baa8Skettenis 
5995dd0baa8Skettenis 	return modes;
6005dd0baa8Skettenis }
6015dd0baa8Skettenis 
parse_display_attributes(struct dcp_parse_ctx * handle,int * width_mm,int * height_mm)6025dd0baa8Skettenis int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm,
6035dd0baa8Skettenis 			     int *height_mm)
6045dd0baa8Skettenis {
6055dd0baa8Skettenis 	int ret = 0;
6065dd0baa8Skettenis 	struct iterator it;
6075dd0baa8Skettenis 	s64 width_cm = 0, height_cm = 0;
6085dd0baa8Skettenis 
6095dd0baa8Skettenis 	dcp_parse_foreach_in_dict(handle, it) {
6105dd0baa8Skettenis 		char *key = parse_string(it.handle);
6115dd0baa8Skettenis 
6125dd0baa8Skettenis 		if (IS_ERR(key))
6135dd0baa8Skettenis 			ret = PTR_ERR(key);
6145dd0baa8Skettenis 		else if (!strcmp(key, "MaxHorizontalImageSize"))
6155dd0baa8Skettenis 			ret = parse_int(it.handle, &width_cm);
6165dd0baa8Skettenis 		else if (!strcmp(key, "MaxVerticalImageSize"))
6175dd0baa8Skettenis 			ret = parse_int(it.handle, &height_cm);
6185dd0baa8Skettenis 		else
6195dd0baa8Skettenis 			skip(it.handle);
6205dd0baa8Skettenis 
6215dd0baa8Skettenis 		if (!IS_ERR_OR_NULL(key))
6225dd0baa8Skettenis 			kfree(key);
6235dd0baa8Skettenis 
6245dd0baa8Skettenis 		if (ret)
6255dd0baa8Skettenis 			return ret;
6265dd0baa8Skettenis 	}
6275dd0baa8Skettenis 
6285dd0baa8Skettenis 	/* 1cm = 10mm */
6295dd0baa8Skettenis 	*width_mm = 10 * width_cm;
6305dd0baa8Skettenis 	*height_mm = 10 * height_cm;
6315dd0baa8Skettenis 
6325dd0baa8Skettenis 	return 0;
6335dd0baa8Skettenis }
6345dd0baa8Skettenis 
parse_epic_service_init(struct dcp_parse_ctx * handle,const char ** name,const char ** class,s64 * unit)6355dd0baa8Skettenis int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name,
6365dd0baa8Skettenis 			    const char **class, s64 *unit)
6375dd0baa8Skettenis {
6385dd0baa8Skettenis 	int ret = 0;
6395dd0baa8Skettenis 	struct iterator it;
6405dd0baa8Skettenis 	bool parsed_unit = false;
6415dd0baa8Skettenis 	bool parsed_name = false;
6425dd0baa8Skettenis 	bool parsed_class = false;
6435dd0baa8Skettenis 
6445dd0baa8Skettenis 	*name = ERR_PTR(-ENOENT);
6455dd0baa8Skettenis 	*class = ERR_PTR(-ENOENT);
6465dd0baa8Skettenis 
6475dd0baa8Skettenis 	dcp_parse_foreach_in_dict(handle, it) {
6485dd0baa8Skettenis 		char *key = parse_string(it.handle);
6495dd0baa8Skettenis 
6505dd0baa8Skettenis 		if (IS_ERR(key)) {
6515dd0baa8Skettenis 			ret = PTR_ERR(key);
6525dd0baa8Skettenis 			break;
6535dd0baa8Skettenis 		}
6545dd0baa8Skettenis 
6555dd0baa8Skettenis 		if (!strcmp(key, "EPICName")) {
6565dd0baa8Skettenis 			*name = parse_string(it.handle);
6575dd0baa8Skettenis 			if (IS_ERR(*name))
6585dd0baa8Skettenis 				ret = PTR_ERR(*name);
6595dd0baa8Skettenis 			else
6605dd0baa8Skettenis 				parsed_name = true;
6615dd0baa8Skettenis 		} else if (!strcmp(key, "EPICProviderClass")) {
6625dd0baa8Skettenis 			*class = parse_string(it.handle);
6635dd0baa8Skettenis 			if (IS_ERR(*class))
6645dd0baa8Skettenis 				ret = PTR_ERR(*class);
6655dd0baa8Skettenis 			else
6665dd0baa8Skettenis 				parsed_class = true;
6675dd0baa8Skettenis 		} else if (!strcmp(key, "EPICUnit")) {
6685dd0baa8Skettenis 			ret = parse_int(it.handle, unit);
6695dd0baa8Skettenis 			if (!ret)
6705dd0baa8Skettenis 				parsed_unit = true;
6715dd0baa8Skettenis 		} else {
6725dd0baa8Skettenis 			skip(it.handle);
6735dd0baa8Skettenis 		}
6745dd0baa8Skettenis 
6755dd0baa8Skettenis 		kfree(key);
6765dd0baa8Skettenis 		if (ret)
6775dd0baa8Skettenis 			break;
6785dd0baa8Skettenis 	}
6795dd0baa8Skettenis 
6805dd0baa8Skettenis 	if (!parsed_unit || !parsed_name || !parsed_class)
6815dd0baa8Skettenis 		ret = -ENOENT;
6825dd0baa8Skettenis 
6835dd0baa8Skettenis 	if (ret) {
6845dd0baa8Skettenis 		if (!IS_ERR(*name)) {
6855dd0baa8Skettenis 			kfree(*name);
6865dd0baa8Skettenis 			*name = ERR_PTR(ret);
6875dd0baa8Skettenis 		}
6885dd0baa8Skettenis 		if (!IS_ERR(*class)) {
6895dd0baa8Skettenis 			kfree(*class);
6905dd0baa8Skettenis 			*class = ERR_PTR(ret);
6915dd0baa8Skettenis 		}
6925dd0baa8Skettenis 	}
6935dd0baa8Skettenis 
6945dd0baa8Skettenis 	return ret;
6955dd0baa8Skettenis }
6965dd0baa8Skettenis 
parse_sample_rate_bit(struct dcp_parse_ctx * handle,unsigned int * ratebit)697*a5620f7aSkettenis static int parse_sample_rate_bit(struct dcp_parse_ctx *handle, unsigned int *ratebit)
6985dd0baa8Skettenis {
6995dd0baa8Skettenis 	s64 rate;
7005dd0baa8Skettenis 	int ret = parse_int(handle, &rate);
7015dd0baa8Skettenis 
7025dd0baa8Skettenis 	if (ret)
7035dd0baa8Skettenis 		return ret;
7045dd0baa8Skettenis 
7055dd0baa8Skettenis 	*ratebit = snd_pcm_rate_to_rate_bit(rate);
7065dd0baa8Skettenis 	if (*ratebit == SNDRV_PCM_RATE_KNOT) {
7075dd0baa8Skettenis 		/*
7085dd0baa8Skettenis 		 * The rate wasn't recognized, and unless we supply
7095dd0baa8Skettenis 		 * a supplementary constraint, the SNDRV_PCM_RATE_KNOT bit
7105dd0baa8Skettenis 		 * will allow any rate. So clear it.
7115dd0baa8Skettenis 		 */
7125dd0baa8Skettenis 		*ratebit = 0;
7135dd0baa8Skettenis 	}
7145dd0baa8Skettenis 
7155dd0baa8Skettenis 	return 0;
7165dd0baa8Skettenis }
7175dd0baa8Skettenis 
parse_sample_fmtbit(struct dcp_parse_ctx * handle,u64 * fmtbit)718*a5620f7aSkettenis static int parse_sample_fmtbit(struct dcp_parse_ctx *handle, u64 *fmtbit)
7195dd0baa8Skettenis {
7205dd0baa8Skettenis 	s64 sample_size;
7215dd0baa8Skettenis 	int ret = parse_int(handle, &sample_size);
7225dd0baa8Skettenis 
7235dd0baa8Skettenis 	if (ret)
7245dd0baa8Skettenis 		return ret;
7255dd0baa8Skettenis 
7265dd0baa8Skettenis 	switch (sample_size) {
7275dd0baa8Skettenis 	case 16:
7285dd0baa8Skettenis 		*fmtbit = SNDRV_PCM_FMTBIT_S16;
7295dd0baa8Skettenis 		break;
7305dd0baa8Skettenis 	case 20:
7315dd0baa8Skettenis 		*fmtbit = SNDRV_PCM_FMTBIT_S20;
7325dd0baa8Skettenis 		break;
7335dd0baa8Skettenis 	case 24:
7345dd0baa8Skettenis 		*fmtbit = SNDRV_PCM_FMTBIT_S24;
7355dd0baa8Skettenis 		break;
7365dd0baa8Skettenis 	case 32:
7375dd0baa8Skettenis 		*fmtbit = SNDRV_PCM_FMTBIT_S32;
7385dd0baa8Skettenis 		break;
7395dd0baa8Skettenis 	default:
7405dd0baa8Skettenis 		*fmtbit = 0;
7415dd0baa8Skettenis 		break;
7425dd0baa8Skettenis 	}
7435dd0baa8Skettenis 
7445dd0baa8Skettenis 	return 0;
7455dd0baa8Skettenis }
7465dd0baa8Skettenis 
7475dd0baa8Skettenis static struct {
7485dd0baa8Skettenis 	const char *label;
7495dd0baa8Skettenis 	u8 type;
7505dd0baa8Skettenis } chan_position_names[] = {
7515dd0baa8Skettenis 	{ "Front Left", SNDRV_CHMAP_FL },
7525dd0baa8Skettenis 	{ "Front Right", SNDRV_CHMAP_FR },
7535dd0baa8Skettenis 	{ "Rear Left", SNDRV_CHMAP_RL },
7545dd0baa8Skettenis 	{ "Rear Right", SNDRV_CHMAP_RR },
7555dd0baa8Skettenis 	{ "Front Center", SNDRV_CHMAP_FC },
7565dd0baa8Skettenis 	{ "Low Frequency Effects", SNDRV_CHMAP_LFE },
7575dd0baa8Skettenis 	{ "Rear Center", SNDRV_CHMAP_RC },
7585dd0baa8Skettenis 	{ "Front Left Center", SNDRV_CHMAP_FLC },
7595dd0baa8Skettenis 	{ "Front Right Center", SNDRV_CHMAP_FRC },
7605dd0baa8Skettenis 	{ "Rear Left Center", SNDRV_CHMAP_RLC },
7615dd0baa8Skettenis 	{ "Rear Right Center", SNDRV_CHMAP_RRC },
7625dd0baa8Skettenis 	{ "Front Left Wide", SNDRV_CHMAP_FLW },
7635dd0baa8Skettenis 	{ "Front Right Wide", SNDRV_CHMAP_FRW },
7645dd0baa8Skettenis 	{ "Front Left High", SNDRV_CHMAP_FLH },
7655dd0baa8Skettenis 	{ "Front Center High", SNDRV_CHMAP_FCH },
7665dd0baa8Skettenis 	{ "Front Right High", SNDRV_CHMAP_FRH },
7675dd0baa8Skettenis 	{ "Top Center", SNDRV_CHMAP_TC },
7685dd0baa8Skettenis };
7695dd0baa8Skettenis 
append_chmap(struct snd_pcm_chmap_elem * chmap,u8 type)7705dd0baa8Skettenis static void append_chmap(struct snd_pcm_chmap_elem *chmap, u8 type)
7715dd0baa8Skettenis {
7725dd0baa8Skettenis 	if (!chmap || chmap->channels >= ARRAY_SIZE(chmap->map))
7735dd0baa8Skettenis 		return;
7745dd0baa8Skettenis 
7755dd0baa8Skettenis 	chmap->map[chmap->channels] = type;
7765dd0baa8Skettenis 	chmap->channels++;
7775dd0baa8Skettenis }
7785dd0baa8Skettenis 
parse_chmap(struct dcp_parse_ctx * handle,struct snd_pcm_chmap_elem * chmap)7795dd0baa8Skettenis static int parse_chmap(struct dcp_parse_ctx *handle, struct snd_pcm_chmap_elem *chmap)
7805dd0baa8Skettenis {
7815dd0baa8Skettenis 	struct iterator it;
7825dd0baa8Skettenis 	int i, ret;
7835dd0baa8Skettenis 
7845dd0baa8Skettenis 	if (!chmap) {
7855dd0baa8Skettenis 		skip(handle);
7865dd0baa8Skettenis 		return 0;
7875dd0baa8Skettenis 	}
7885dd0baa8Skettenis 
7895dd0baa8Skettenis 	chmap->channels = 0;
7905dd0baa8Skettenis 
7915dd0baa8Skettenis 	dcp_parse_foreach_in_array(handle, it) {
7925dd0baa8Skettenis 		for (i = 0; i < ARRAY_SIZE(chan_position_names); i++)
7935dd0baa8Skettenis 			if (consume_string(it.handle, chan_position_names[i].label))
7945dd0baa8Skettenis 				break;
7955dd0baa8Skettenis 
7965dd0baa8Skettenis 		if (i == ARRAY_SIZE(chan_position_names)) {
7975dd0baa8Skettenis 			ret = skip(it.handle);
7985dd0baa8Skettenis 			if (ret)
7995dd0baa8Skettenis 				return ret;
8005dd0baa8Skettenis 
8015dd0baa8Skettenis 			append_chmap(chmap, SNDRV_CHMAP_UNKNOWN);
8025dd0baa8Skettenis 			continue;
8035dd0baa8Skettenis 		}
8045dd0baa8Skettenis 
8055dd0baa8Skettenis 		append_chmap(chmap, chan_position_names[i].type);
8065dd0baa8Skettenis 	}
8075dd0baa8Skettenis 
8085dd0baa8Skettenis 	return 0;
8095dd0baa8Skettenis }
8105dd0baa8Skettenis 
parse_chan_layout_element(struct dcp_parse_ctx * handle,unsigned int * nchans_out,struct snd_pcm_chmap_elem * chmap)8115dd0baa8Skettenis static int parse_chan_layout_element(struct dcp_parse_ctx *handle,
8125dd0baa8Skettenis 				     unsigned int *nchans_out,
8135dd0baa8Skettenis 				     struct snd_pcm_chmap_elem *chmap)
8145dd0baa8Skettenis {
8155dd0baa8Skettenis 	struct iterator it;
8165dd0baa8Skettenis 	int ret;
8175dd0baa8Skettenis 	s64 nchans = 0;
8185dd0baa8Skettenis 
8195dd0baa8Skettenis 	dcp_parse_foreach_in_dict(handle, it) {
8205dd0baa8Skettenis 		if (consume_string(it.handle, "ActiveChannelCount"))
8215dd0baa8Skettenis 			ret = parse_int(it.handle, &nchans);
8225dd0baa8Skettenis 		else if (consume_string(it.handle, "ChannelLayout"))
8235dd0baa8Skettenis 			ret = parse_chmap(it.handle, chmap);
8245dd0baa8Skettenis 		else
8255dd0baa8Skettenis 			ret = skip_pair(it.handle);
8265dd0baa8Skettenis 
8275dd0baa8Skettenis 		if (ret)
8285dd0baa8Skettenis 			return ret;
8295dd0baa8Skettenis 	}
8305dd0baa8Skettenis 
8315dd0baa8Skettenis 	if (nchans_out)
8325dd0baa8Skettenis 		*nchans_out = nchans;
8335dd0baa8Skettenis 
8345dd0baa8Skettenis 	return 0;
8355dd0baa8Skettenis }
8365dd0baa8Skettenis 
parse_nchans_mask(struct dcp_parse_ctx * handle,unsigned int * mask)8375dd0baa8Skettenis static int parse_nchans_mask(struct dcp_parse_ctx *handle, unsigned int *mask)
8385dd0baa8Skettenis {
8395dd0baa8Skettenis 	struct iterator it;
8405dd0baa8Skettenis 	int ret;
8415dd0baa8Skettenis 
8425dd0baa8Skettenis 	*mask = 0;
8435dd0baa8Skettenis 
8445dd0baa8Skettenis 	dcp_parse_foreach_in_array(handle, it) {
8455dd0baa8Skettenis 		int nchans;
8465dd0baa8Skettenis 
8475dd0baa8Skettenis 		ret = parse_chan_layout_element(it.handle, &nchans, NULL);
8485dd0baa8Skettenis 		if (ret)
8495dd0baa8Skettenis 			return ret;
8505dd0baa8Skettenis 		*mask |= 1 << nchans;
8515dd0baa8Skettenis 	}
8525dd0baa8Skettenis 
8535dd0baa8Skettenis 	return 0;
8545dd0baa8Skettenis }
8555dd0baa8Skettenis 
parse_avep_element(struct dcp_parse_ctx * handle,struct dcp_sound_format_mask * sieve,struct dcp_sound_format_mask * hits)8565dd0baa8Skettenis static int parse_avep_element(struct dcp_parse_ctx *handle,
8575dd0baa8Skettenis 			      struct dcp_sound_format_mask *sieve,
8585dd0baa8Skettenis 			      struct dcp_sound_format_mask *hits)
8595dd0baa8Skettenis {
8605dd0baa8Skettenis 	struct dcp_sound_format_mask mask = {0, 0, 0};
8615dd0baa8Skettenis 	struct iterator it;
8625dd0baa8Skettenis 	int ret;
8635dd0baa8Skettenis 
8645dd0baa8Skettenis 	dcp_parse_foreach_in_dict(handle, it) {
8655dd0baa8Skettenis 		if (consume_string(handle, "StreamSampleRate"))
8665dd0baa8Skettenis 			ret = parse_sample_rate_bit(it.handle, &mask.rates);
8675dd0baa8Skettenis 		else if (consume_string(handle, "SampleSize"))
8685dd0baa8Skettenis 			ret = parse_sample_fmtbit(it.handle, &mask.formats);
8695dd0baa8Skettenis 		else if (consume_string(handle, "AudioChannelLayoutElements"))
8705dd0baa8Skettenis 			ret = parse_nchans_mask(it.handle, &mask.nchans);
8715dd0baa8Skettenis 		else
8725dd0baa8Skettenis 			ret = skip_pair(it.handle);
8735dd0baa8Skettenis 
8745dd0baa8Skettenis 		if (ret)
8755dd0baa8Skettenis 			return ret;
8765dd0baa8Skettenis 	}
8775dd0baa8Skettenis 
8785dd0baa8Skettenis 	trace_avep_sound_mode(handle->dcp, mask.rates, mask.formats, mask.nchans);
8795dd0baa8Skettenis 
8805dd0baa8Skettenis 	if (!(mask.rates & sieve->rates) || !(mask.formats & sieve->formats) ||
8815dd0baa8Skettenis 		!(mask.nchans & sieve->nchans))
8825dd0baa8Skettenis 	    return 0;
8835dd0baa8Skettenis 
8845dd0baa8Skettenis 	if (hits) {
8855dd0baa8Skettenis 		hits->rates |= mask.rates;
8865dd0baa8Skettenis 		hits->formats |= mask.formats;
8875dd0baa8Skettenis 		hits->nchans |= mask.nchans;
8885dd0baa8Skettenis 	}
8895dd0baa8Skettenis 
8905dd0baa8Skettenis 	return 1;
8915dd0baa8Skettenis }
8925dd0baa8Skettenis 
parse_mode_in_avep_element(struct dcp_parse_ctx * handle,unsigned int selected_nchans,struct snd_pcm_chmap_elem * chmap,struct dcp_sound_cookie * cookie)8935dd0baa8Skettenis static int parse_mode_in_avep_element(struct dcp_parse_ctx *handle,
8945dd0baa8Skettenis 				      unsigned int selected_nchans,
8955dd0baa8Skettenis 				      struct snd_pcm_chmap_elem *chmap,
8965dd0baa8Skettenis 				      struct dcp_sound_cookie *cookie)
8975dd0baa8Skettenis {
8985dd0baa8Skettenis 	struct iterator it;
8995dd0baa8Skettenis 	struct dcp_parse_ctx save_handle;
9005dd0baa8Skettenis 	int ret;
9015dd0baa8Skettenis 
9025dd0baa8Skettenis 	dcp_parse_foreach_in_dict(handle, it) {
9035dd0baa8Skettenis 		if (consume_string(it.handle, "AudioChannelLayoutElements")) {
9045dd0baa8Skettenis 			struct iterator inner_it;
9055dd0baa8Skettenis 			int nchans;
9065dd0baa8Skettenis 
9075dd0baa8Skettenis 			dcp_parse_foreach_in_array(it.handle, inner_it) {
9085dd0baa8Skettenis 				save_handle = *it.handle;
9095dd0baa8Skettenis 				ret = parse_chan_layout_element(inner_it.handle,
9105dd0baa8Skettenis 								&nchans, NULL);
9115dd0baa8Skettenis 				if (ret)
9125dd0baa8Skettenis 					return ret;
9135dd0baa8Skettenis 
9145dd0baa8Skettenis 				if (nchans != selected_nchans)
9155dd0baa8Skettenis 					continue;
9165dd0baa8Skettenis 
9175dd0baa8Skettenis 				/*
9185dd0baa8Skettenis 				 * Now that we know this layout matches the
9195dd0baa8Skettenis 				 * selected channel number, reread the element
9205dd0baa8Skettenis 				 * and fill in the channel map.
9215dd0baa8Skettenis 				 */
9225dd0baa8Skettenis 				*inner_it.handle = save_handle;
9235dd0baa8Skettenis 				ret = parse_chan_layout_element(inner_it.handle,
9245dd0baa8Skettenis 								NULL, chmap);
9255dd0baa8Skettenis 				if (ret)
9265dd0baa8Skettenis 					return ret;
9275dd0baa8Skettenis 			}
9285dd0baa8Skettenis 		} else if (consume_string(it.handle, "ElementData")) {
9295dd0baa8Skettenis 			const u8 *blob;
9305dd0baa8Skettenis 
9315dd0baa8Skettenis 			ret = parse_blob(it.handle, sizeof(*cookie), &blob);
9325dd0baa8Skettenis 			if (ret)
9335dd0baa8Skettenis 				return ret;
9345dd0baa8Skettenis 
9355dd0baa8Skettenis 			if (cookie)
9365dd0baa8Skettenis 				memcpy(cookie, blob, sizeof(*cookie));
9375dd0baa8Skettenis 		} else {
9385dd0baa8Skettenis 			ret = skip_pair(it.handle);
9395dd0baa8Skettenis 			if (ret)
9405dd0baa8Skettenis 				return ret;
9415dd0baa8Skettenis 		}
9425dd0baa8Skettenis 	}
9435dd0baa8Skettenis 
9445dd0baa8Skettenis 	return 0;
9455dd0baa8Skettenis }
9465dd0baa8Skettenis 
parse_sound_constraints(struct dcp_parse_ctx * handle,struct dcp_sound_format_mask * sieve,struct dcp_sound_format_mask * hits)9475dd0baa8Skettenis int parse_sound_constraints(struct dcp_parse_ctx *handle,
9485dd0baa8Skettenis 			    struct dcp_sound_format_mask *sieve,
9495dd0baa8Skettenis 			    struct dcp_sound_format_mask *hits)
9505dd0baa8Skettenis {
9515dd0baa8Skettenis 	int ret;
9525dd0baa8Skettenis 	struct iterator it;
9535dd0baa8Skettenis 
9545dd0baa8Skettenis 	if (hits) {
9555dd0baa8Skettenis 		hits->rates = 0;
9565dd0baa8Skettenis 		hits->formats = 0;
9575dd0baa8Skettenis 		hits->nchans = 0;
9585dd0baa8Skettenis 	}
9595dd0baa8Skettenis 
9605dd0baa8Skettenis 	dcp_parse_foreach_in_array(handle, it) {
9615dd0baa8Skettenis 		ret = parse_avep_element(it.handle, sieve, hits);
9625dd0baa8Skettenis 
9635dd0baa8Skettenis 		if (ret < 0)
9645dd0baa8Skettenis 			return ret;
9655dd0baa8Skettenis 	}
9665dd0baa8Skettenis 
9675dd0baa8Skettenis 	return 0;
9685dd0baa8Skettenis }
9695dd0baa8Skettenis EXPORT_SYMBOL_GPL(parse_sound_constraints);
9705dd0baa8Skettenis 
parse_sound_mode(struct dcp_parse_ctx * handle,struct dcp_sound_format_mask * sieve,struct snd_pcm_chmap_elem * chmap,struct dcp_sound_cookie * cookie)9715dd0baa8Skettenis int parse_sound_mode(struct dcp_parse_ctx *handle,
9725dd0baa8Skettenis 		     struct dcp_sound_format_mask *sieve,
9735dd0baa8Skettenis 		     struct snd_pcm_chmap_elem *chmap,
9745dd0baa8Skettenis 		     struct dcp_sound_cookie *cookie)
9755dd0baa8Skettenis {
9765dd0baa8Skettenis 	struct dcp_parse_ctx save_handle;
9775dd0baa8Skettenis 	struct iterator it;
9785dd0baa8Skettenis 	int ret;
9795dd0baa8Skettenis 
9805dd0baa8Skettenis 	dcp_parse_foreach_in_array(handle, it) {
9815dd0baa8Skettenis 		save_handle = *it.handle;
9825dd0baa8Skettenis 		ret = parse_avep_element(it.handle, sieve, NULL);
9835dd0baa8Skettenis 
9845dd0baa8Skettenis 		if (!ret)
9855dd0baa8Skettenis 			continue;
9865dd0baa8Skettenis 
9875dd0baa8Skettenis 		if (ret < 0)
9885dd0baa8Skettenis 			return ret;
9895dd0baa8Skettenis 
9905dd0baa8Skettenis 		ret = parse_mode_in_avep_element(&save_handle, __ffs(sieve->nchans),
9915dd0baa8Skettenis 						 chmap, cookie);
9925dd0baa8Skettenis 		if (ret < 0)
9935dd0baa8Skettenis 			return ret;
9945dd0baa8Skettenis 		return 1;
9955dd0baa8Skettenis 	}
9965dd0baa8Skettenis 
9975dd0baa8Skettenis 	return 0;
9985dd0baa8Skettenis }
9995dd0baa8Skettenis EXPORT_SYMBOL_GPL(parse_sound_mode);
10005dd0baa8Skettenis 
parse_system_log_mnits(struct dcp_parse_ctx * handle,struct dcp_system_ev_mnits * entry)10015dd0baa8Skettenis int parse_system_log_mnits(struct dcp_parse_ctx *handle, struct dcp_system_ev_mnits *entry)
10025dd0baa8Skettenis {
10035dd0baa8Skettenis 	struct iterator it;
10045dd0baa8Skettenis 	int ret;
10055dd0baa8Skettenis 	s64 mnits = -1;
10065dd0baa8Skettenis 	s64 idac = -1;
10075dd0baa8Skettenis 	s64 timestamp = -1;
10085dd0baa8Skettenis 	bool type_match = false;
10095dd0baa8Skettenis 
10105dd0baa8Skettenis 	dcp_parse_foreach_in_dict(handle, it) {
10115dd0baa8Skettenis 		char *key = parse_string(it.handle);
10125dd0baa8Skettenis 		if (IS_ERR(key)) {
10135dd0baa8Skettenis 			ret = PTR_ERR(key);
10145dd0baa8Skettenis 		} else if (!strcmp(key, "mNits")) {
10155dd0baa8Skettenis 			ret = parse_int(it.handle, &mnits);
10165dd0baa8Skettenis 		} else if (!strcmp(key, "iDAC")) {
10175dd0baa8Skettenis 			ret = parse_int(it.handle, &idac);
10185dd0baa8Skettenis 		} else if (!strcmp(key, "logEvent")) {
10195dd0baa8Skettenis 			const char * value = parse_string(it.handle);
10205dd0baa8Skettenis 			if (!IS_ERR_OR_NULL(value)) {
10215dd0baa8Skettenis 				type_match = strcmp(value, "Display (Event Forward)") == 0;
10225dd0baa8Skettenis 				kfree(value);
10235dd0baa8Skettenis 			}
10245dd0baa8Skettenis 		} else if (!strcmp(key, "timestamp")) {
10255dd0baa8Skettenis 			ret = parse_int(it.handle, &timestamp);
10265dd0baa8Skettenis 		} else {
10275dd0baa8Skettenis 			skip(it.handle);
10285dd0baa8Skettenis 		}
10295dd0baa8Skettenis 
10305dd0baa8Skettenis 		if (!IS_ERR_OR_NULL(key))
10315dd0baa8Skettenis 			kfree(key);
10325dd0baa8Skettenis 
10335dd0baa8Skettenis 		if (ret) {
10345dd0baa8Skettenis 			pr_err("dcp parser: failed to parse mNits sys event\n");
10355dd0baa8Skettenis 			return ret;
10365dd0baa8Skettenis 		}
10375dd0baa8Skettenis 	}
10385dd0baa8Skettenis 
10395dd0baa8Skettenis 	if (!type_match ||  mnits < 0 || idac < 0 || timestamp < 0)
10405dd0baa8Skettenis 		return -EINVAL;
10415dd0baa8Skettenis 
10425dd0baa8Skettenis 	entry->millinits = mnits;
10435dd0baa8Skettenis 	entry->idac = idac;
10445dd0baa8Skettenis 	entry->timestamp = timestamp;
10455dd0baa8Skettenis 
10465dd0baa8Skettenis 	return 0;
10475dd0baa8Skettenis }
1048