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, ×tamp);
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