162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CCS static data binary parser library
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2019--2020 Intel Corporation
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/device.h>
962306a36Sopenharmony_ci#include <linux/errno.h>
1062306a36Sopenharmony_ci#include <linux/limits.h>
1162306a36Sopenharmony_ci#include <linux/mm.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "ccs-data-defs.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct bin_container {
1762306a36Sopenharmony_ci	void *base;
1862306a36Sopenharmony_ci	void *now;
1962306a36Sopenharmony_ci	void *end;
2062306a36Sopenharmony_ci	size_t size;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void *bin_alloc(struct bin_container *bin, size_t len)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	void *ptr;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	len = ALIGN(len, 8);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (bin->end - bin->now < len)
3062306a36Sopenharmony_ci		return NULL;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	ptr = bin->now;
3362306a36Sopenharmony_ci	bin->now += len;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return ptr;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void bin_reserve(struct bin_container *bin, size_t len)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	bin->size += ALIGN(len, 8);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int bin_backing_alloc(struct bin_container *bin)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	bin->base = bin->now = kvzalloc(bin->size, GFP_KERNEL);
4662306a36Sopenharmony_ci	if (!bin->base)
4762306a36Sopenharmony_ci		return -ENOMEM;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	bin->end = bin->base + bin->size;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	return 0;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define is_contained(var, endp)				\
5562306a36Sopenharmony_ci	(sizeof(*var) <= (endp) - (void *)(var))
5662306a36Sopenharmony_ci#define has_headroom(ptr, headroom, endp)	\
5762306a36Sopenharmony_ci	((headroom) <= (endp) - (void *)(ptr))
5862306a36Sopenharmony_ci#define is_contained_with_headroom(var, headroom, endp)		\
5962306a36Sopenharmony_ci	(sizeof(*var) + (headroom) <= (endp) - (void *)(var))
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int
6262306a36Sopenharmony_ciccs_data_parse_length_specifier(const struct __ccs_data_length_specifier *__len,
6362306a36Sopenharmony_ci				size_t *__hlen, size_t *__plen,
6462306a36Sopenharmony_ci				const void *endp)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	size_t hlen, plen;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (!is_contained(__len, endp))
6962306a36Sopenharmony_ci		return -ENODATA;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	switch (__len->length >> CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) {
7262306a36Sopenharmony_ci	case CCS_DATA_LENGTH_SPECIFIER_1:
7362306a36Sopenharmony_ci		hlen = sizeof(*__len);
7462306a36Sopenharmony_ci		plen = __len->length &
7562306a36Sopenharmony_ci			((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1);
7662306a36Sopenharmony_ci		break;
7762306a36Sopenharmony_ci	case CCS_DATA_LENGTH_SPECIFIER_2: {
7862306a36Sopenharmony_ci		struct __ccs_data_length_specifier2 *__len2 = (void *)__len;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		if (!is_contained(__len2, endp))
8162306a36Sopenharmony_ci			return -ENODATA;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		hlen = sizeof(*__len2);
8462306a36Sopenharmony_ci		plen = ((size_t)
8562306a36Sopenharmony_ci			(__len2->length[0] &
8662306a36Sopenharmony_ci			 ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1))
8762306a36Sopenharmony_ci			<< 8) + __len2->length[1];
8862306a36Sopenharmony_ci		break;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci	case CCS_DATA_LENGTH_SPECIFIER_3: {
9162306a36Sopenharmony_ci		struct __ccs_data_length_specifier3 *__len3 = (void *)__len;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		if (!is_contained(__len3, endp))
9462306a36Sopenharmony_ci			return -ENODATA;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		hlen = sizeof(*__len3);
9762306a36Sopenharmony_ci		plen = ((size_t)
9862306a36Sopenharmony_ci			(__len3->length[0] &
9962306a36Sopenharmony_ci			 ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1))
10062306a36Sopenharmony_ci			<< 16) + (__len3->length[0] << 8) + __len3->length[1];
10162306a36Sopenharmony_ci		break;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci	default:
10462306a36Sopenharmony_ci		return -EINVAL;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (!has_headroom(__len, hlen + plen, endp))
10862306a36Sopenharmony_ci		return -ENODATA;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	*__hlen = hlen;
11162306a36Sopenharmony_ci	*__plen = plen;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic u8
11762306a36Sopenharmony_ciccs_data_parse_format_version(const struct __ccs_data_block *block)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	return block->id >> CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic u8 ccs_data_parse_block_id(const struct __ccs_data_block *block,
12362306a36Sopenharmony_ci				       bool is_first)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	if (!is_first)
12662306a36Sopenharmony_ci		return block->id;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return block->id & ((1 << CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT) - 1);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int ccs_data_parse_version(struct bin_container *bin,
13262306a36Sopenharmony_ci				  struct ccs_data_container *ccsdata,
13362306a36Sopenharmony_ci				  const void *payload, const void *endp)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	const struct __ccs_data_block_version *v = payload;
13662306a36Sopenharmony_ci	struct ccs_data_block_version *vv;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (v + 1 != endp)
13962306a36Sopenharmony_ci		return -ENODATA;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (!bin->base) {
14262306a36Sopenharmony_ci		bin_reserve(bin, sizeof(*ccsdata->version));
14362306a36Sopenharmony_ci		return 0;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	ccsdata->version = bin_alloc(bin, sizeof(*ccsdata->version));
14762306a36Sopenharmony_ci	if (!ccsdata->version)
14862306a36Sopenharmony_ci		return -ENOMEM;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	vv = ccsdata->version;
15162306a36Sopenharmony_ci	vv->version_major = ((u16)v->static_data_version_major[0] << 8) +
15262306a36Sopenharmony_ci		v->static_data_version_major[1];
15362306a36Sopenharmony_ci	vv->version_minor = ((u16)v->static_data_version_minor[0] << 8) +
15462306a36Sopenharmony_ci		v->static_data_version_minor[1];
15562306a36Sopenharmony_ci	vv->date_year =  ((u16)v->year[0] << 8) + v->year[1];
15662306a36Sopenharmony_ci	vv->date_month = v->month;
15762306a36Sopenharmony_ci	vv->date_day = v->day;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic void print_ccs_data_version(struct device *dev,
16362306a36Sopenharmony_ci				   struct ccs_data_block_version *v)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	dev_dbg(dev,
16662306a36Sopenharmony_ci		"static data version %4.4x.%4.4x, date %4.4u-%2.2u-%2.2u\n",
16762306a36Sopenharmony_ci		v->version_major, v->version_minor,
16862306a36Sopenharmony_ci		v->date_year, v->date_month, v->date_day);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int ccs_data_block_parse_header(const struct __ccs_data_block *block,
17262306a36Sopenharmony_ci				       bool is_first, unsigned int *__block_id,
17362306a36Sopenharmony_ci				       const void **payload,
17462306a36Sopenharmony_ci				       const struct __ccs_data_block **next_block,
17562306a36Sopenharmony_ci				       const void *endp, struct device *dev,
17662306a36Sopenharmony_ci				       bool verbose)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	size_t plen, hlen;
17962306a36Sopenharmony_ci	u8 block_id;
18062306a36Sopenharmony_ci	int rval;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (!is_contained(block, endp))
18362306a36Sopenharmony_ci		return -ENODATA;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	rval = ccs_data_parse_length_specifier(&block->length, &hlen, &plen,
18662306a36Sopenharmony_ci					       endp);
18762306a36Sopenharmony_ci	if (rval < 0)
18862306a36Sopenharmony_ci		return rval;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	block_id = ccs_data_parse_block_id(block, is_first);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (verbose)
19362306a36Sopenharmony_ci		dev_dbg(dev,
19462306a36Sopenharmony_ci			"Block ID 0x%2.2x, header length %zu, payload length %zu\n",
19562306a36Sopenharmony_ci			block_id, hlen, plen);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (!has_headroom(&block->length, hlen + plen, endp))
19862306a36Sopenharmony_ci		return -ENODATA;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (__block_id)
20162306a36Sopenharmony_ci		*__block_id = block_id;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (payload)
20462306a36Sopenharmony_ci		*payload = (void *)&block->length + hlen;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (next_block)
20762306a36Sopenharmony_ci		*next_block = (void *)&block->length + hlen + plen;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int ccs_data_parse_regs(struct bin_container *bin,
21362306a36Sopenharmony_ci			       struct ccs_reg **__regs,
21462306a36Sopenharmony_ci			       size_t *__num_regs, const void *payload,
21562306a36Sopenharmony_ci			       const void *endp, struct device *dev)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct ccs_reg *regs_base = NULL, *regs = NULL;
21862306a36Sopenharmony_ci	size_t num_regs = 0;
21962306a36Sopenharmony_ci	u16 addr = 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (bin->base && __regs) {
22262306a36Sopenharmony_ci		regs = regs_base = bin_alloc(bin, sizeof(*regs) * *__num_regs);
22362306a36Sopenharmony_ci		if (!regs)
22462306a36Sopenharmony_ci			return -ENOMEM;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	while (payload < endp && num_regs < INT_MAX) {
22862306a36Sopenharmony_ci		const struct __ccs_data_block_regs *r = payload;
22962306a36Sopenharmony_ci		size_t len;
23062306a36Sopenharmony_ci		const void *data;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		if (!is_contained(r, endp))
23362306a36Sopenharmony_ci			return -ENODATA;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		switch (r->reg_len >> CCS_DATA_BLOCK_REGS_SEL_SHIFT) {
23662306a36Sopenharmony_ci		case CCS_DATA_BLOCK_REGS_SEL_REGS:
23762306a36Sopenharmony_ci			addr += r->reg_len & CCS_DATA_BLOCK_REGS_ADDR_MASK;
23862306a36Sopenharmony_ci			len = ((r->reg_len & CCS_DATA_BLOCK_REGS_LEN_MASK)
23962306a36Sopenharmony_ci			       >> CCS_DATA_BLOCK_REGS_LEN_SHIFT) + 1;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci			if (!is_contained_with_headroom(r, len, endp))
24262306a36Sopenharmony_ci				return -ENODATA;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci			data = r + 1;
24562306a36Sopenharmony_ci			break;
24662306a36Sopenharmony_ci		case CCS_DATA_BLOCK_REGS_SEL_REGS2: {
24762306a36Sopenharmony_ci			const struct __ccs_data_block_regs2 *r2 = payload;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci			if (!is_contained(r2, endp))
25062306a36Sopenharmony_ci				return -ENODATA;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci			addr += ((u16)(r2->reg_len &
25362306a36Sopenharmony_ci				       CCS_DATA_BLOCK_REGS_2_ADDR_MASK) << 8)
25462306a36Sopenharmony_ci				+ r2->addr;
25562306a36Sopenharmony_ci			len = ((r2->reg_len & CCS_DATA_BLOCK_REGS_2_LEN_MASK)
25662306a36Sopenharmony_ci			       >> CCS_DATA_BLOCK_REGS_2_LEN_SHIFT) + 1;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci			if (!is_contained_with_headroom(r2, len, endp))
25962306a36Sopenharmony_ci				return -ENODATA;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci			data = r2 + 1;
26262306a36Sopenharmony_ci			break;
26362306a36Sopenharmony_ci		}
26462306a36Sopenharmony_ci		case CCS_DATA_BLOCK_REGS_SEL_REGS3: {
26562306a36Sopenharmony_ci			const struct __ccs_data_block_regs3 *r3 = payload;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci			if (!is_contained(r3, endp))
26862306a36Sopenharmony_ci				return -ENODATA;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci			addr = ((u16)r3->addr[0] << 8) + r3->addr[1];
27162306a36Sopenharmony_ci			len = (r3->reg_len & CCS_DATA_BLOCK_REGS_3_LEN_MASK) + 1;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci			if (!is_contained_with_headroom(r3, len, endp))
27462306a36Sopenharmony_ci				return -ENODATA;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci			data = r3 + 1;
27762306a36Sopenharmony_ci			break;
27862306a36Sopenharmony_ci		}
27962306a36Sopenharmony_ci		default:
28062306a36Sopenharmony_ci			return -EINVAL;
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		num_regs++;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		if (!bin->base) {
28662306a36Sopenharmony_ci			bin_reserve(bin, len);
28762306a36Sopenharmony_ci		} else if (__regs) {
28862306a36Sopenharmony_ci			if (!regs)
28962306a36Sopenharmony_ci				return -EIO;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci			regs->addr = addr;
29262306a36Sopenharmony_ci			regs->len = len;
29362306a36Sopenharmony_ci			regs->value = bin_alloc(bin, len);
29462306a36Sopenharmony_ci			if (!regs->value)
29562306a36Sopenharmony_ci				return -ENOMEM;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci			memcpy(regs->value, data, len);
29862306a36Sopenharmony_ci			regs++;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		addr += len;
30262306a36Sopenharmony_ci		payload = data + len;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (!bin->base)
30662306a36Sopenharmony_ci		bin_reserve(bin, sizeof(*regs) * num_regs);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (__num_regs)
30962306a36Sopenharmony_ci		*__num_regs = num_regs;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (bin->base && __regs) {
31262306a36Sopenharmony_ci		if (!regs_base)
31362306a36Sopenharmony_ci			return -EIO;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		*__regs = regs_base;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return 0;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int ccs_data_parse_reg_rules(struct bin_container *bin,
32262306a36Sopenharmony_ci				    struct ccs_reg **__regs,
32362306a36Sopenharmony_ci				    size_t *__num_regs,
32462306a36Sopenharmony_ci				    const void *payload,
32562306a36Sopenharmony_ci				    const void *endp, struct device *dev)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	int rval;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (!bin->base)
33062306a36Sopenharmony_ci		return ccs_data_parse_regs(bin, NULL, NULL, payload, endp, dev);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	rval = ccs_data_parse_regs(bin, NULL, __num_regs, payload, endp, dev);
33362306a36Sopenharmony_ci	if (rval)
33462306a36Sopenharmony_ci		return rval;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return ccs_data_parse_regs(bin, __regs, __num_regs, payload, endp,
33762306a36Sopenharmony_ci				   dev);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic void assign_ffd_entry(struct ccs_frame_format_desc *desc,
34162306a36Sopenharmony_ci			     const struct __ccs_data_block_ffd_entry *ent)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	desc->pixelcode = ent->pixelcode;
34462306a36Sopenharmony_ci	desc->value = ((u16)ent->value[0] << 8) + ent->value[1];
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int ccs_data_parse_ffd(struct bin_container *bin,
34862306a36Sopenharmony_ci			      struct ccs_frame_format_descs **ffd,
34962306a36Sopenharmony_ci			      const void *payload,
35062306a36Sopenharmony_ci			      const void *endp, struct device *dev)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	const struct __ccs_data_block_ffd *__ffd = payload;
35362306a36Sopenharmony_ci	const struct __ccs_data_block_ffd_entry *__entry;
35462306a36Sopenharmony_ci	unsigned int i;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (!is_contained(__ffd, endp))
35762306a36Sopenharmony_ci		return -ENODATA;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if ((void *)__ffd + sizeof(*__ffd) +
36062306a36Sopenharmony_ci	    ((u32)__ffd->num_column_descs +
36162306a36Sopenharmony_ci	     (u32)__ffd->num_row_descs) *
36262306a36Sopenharmony_ci	    sizeof(struct __ccs_data_block_ffd_entry) != endp)
36362306a36Sopenharmony_ci		return -ENODATA;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (!bin->base) {
36662306a36Sopenharmony_ci		bin_reserve(bin, sizeof(**ffd));
36762306a36Sopenharmony_ci		bin_reserve(bin, __ffd->num_column_descs *
36862306a36Sopenharmony_ci			    sizeof(struct ccs_frame_format_desc));
36962306a36Sopenharmony_ci		bin_reserve(bin, __ffd->num_row_descs *
37062306a36Sopenharmony_ci			    sizeof(struct ccs_frame_format_desc));
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		return 0;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	*ffd = bin_alloc(bin, sizeof(**ffd));
37662306a36Sopenharmony_ci	if (!*ffd)
37762306a36Sopenharmony_ci		return -ENOMEM;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	(*ffd)->num_column_descs = __ffd->num_column_descs;
38062306a36Sopenharmony_ci	(*ffd)->num_row_descs = __ffd->num_row_descs;
38162306a36Sopenharmony_ci	__entry = (void *)(__ffd + 1);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	(*ffd)->column_descs = bin_alloc(bin, __ffd->num_column_descs *
38462306a36Sopenharmony_ci					 sizeof(*(*ffd)->column_descs));
38562306a36Sopenharmony_ci	if (!(*ffd)->column_descs)
38662306a36Sopenharmony_ci		return -ENOMEM;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	for (i = 0; i < __ffd->num_column_descs; i++, __entry++)
38962306a36Sopenharmony_ci		assign_ffd_entry(&(*ffd)->column_descs[i], __entry);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	(*ffd)->row_descs = bin_alloc(bin, __ffd->num_row_descs *
39262306a36Sopenharmony_ci				      sizeof(*(*ffd)->row_descs));
39362306a36Sopenharmony_ci	if (!(*ffd)->row_descs)
39462306a36Sopenharmony_ci		return -ENOMEM;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	for (i = 0; i < __ffd->num_row_descs; i++, __entry++)
39762306a36Sopenharmony_ci		assign_ffd_entry(&(*ffd)->row_descs[i], __entry);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (__entry != endp)
40062306a36Sopenharmony_ci		return -EPROTO;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return 0;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic int ccs_data_parse_pdaf_readout(struct bin_container *bin,
40662306a36Sopenharmony_ci				       struct ccs_pdaf_readout **pdaf_readout,
40762306a36Sopenharmony_ci				       const void *payload,
40862306a36Sopenharmony_ci				       const void *endp, struct device *dev)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	const struct __ccs_data_block_pdaf_readout *__pdaf = payload;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (!is_contained(__pdaf, endp))
41362306a36Sopenharmony_ci		return -ENODATA;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (!bin->base) {
41662306a36Sopenharmony_ci		bin_reserve(bin, sizeof(**pdaf_readout));
41762306a36Sopenharmony_ci	} else {
41862306a36Sopenharmony_ci		*pdaf_readout = bin_alloc(bin, sizeof(**pdaf_readout));
41962306a36Sopenharmony_ci		if (!*pdaf_readout)
42062306a36Sopenharmony_ci			return -ENOMEM;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		(*pdaf_readout)->pdaf_readout_info_order =
42362306a36Sopenharmony_ci			__pdaf->pdaf_readout_info_order;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	return ccs_data_parse_ffd(bin, !bin->base ? NULL : &(*pdaf_readout)->ffd,
42762306a36Sopenharmony_ci				  __pdaf + 1, endp, dev);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic int ccs_data_parse_rules(struct bin_container *bin,
43162306a36Sopenharmony_ci				struct ccs_rule **__rules,
43262306a36Sopenharmony_ci				size_t *__num_rules, const void *payload,
43362306a36Sopenharmony_ci				const void *endp, struct device *dev)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct ccs_rule *rules_base = NULL, *rules = NULL, *next_rule = NULL;
43662306a36Sopenharmony_ci	size_t num_rules = 0;
43762306a36Sopenharmony_ci	const void *__next_rule = payload;
43862306a36Sopenharmony_ci	int rval;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (bin->base) {
44162306a36Sopenharmony_ci		rules_base = next_rule =
44262306a36Sopenharmony_ci			bin_alloc(bin, sizeof(*rules) * *__num_rules);
44362306a36Sopenharmony_ci		if (!rules_base)
44462306a36Sopenharmony_ci			return -ENOMEM;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	while (__next_rule < endp) {
44862306a36Sopenharmony_ci		size_t rule_hlen, rule_plen, rule_plen2;
44962306a36Sopenharmony_ci		const u8 *__rule_type;
45062306a36Sopenharmony_ci		const void *rule_payload;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		/* Size of a single rule */
45362306a36Sopenharmony_ci		rval = ccs_data_parse_length_specifier(__next_rule, &rule_hlen,
45462306a36Sopenharmony_ci						       &rule_plen, endp);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		if (rval < 0)
45762306a36Sopenharmony_ci			return rval;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		__rule_type = __next_rule + rule_hlen;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		if (!is_contained(__rule_type, endp))
46262306a36Sopenharmony_ci			return -ENODATA;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		rule_payload = __rule_type + 1;
46562306a36Sopenharmony_ci		rule_plen2 = rule_plen - sizeof(*__rule_type);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		if (*__rule_type == CCS_DATA_BLOCK_RULE_ID_IF) {
46862306a36Sopenharmony_ci			const struct __ccs_data_block_rule_if *__if_rules =
46962306a36Sopenharmony_ci				rule_payload;
47062306a36Sopenharmony_ci			const size_t __num_if_rules =
47162306a36Sopenharmony_ci				rule_plen2 / sizeof(*__if_rules);
47262306a36Sopenharmony_ci			struct ccs_if_rule *if_rule;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci			if (!has_headroom(__if_rules,
47562306a36Sopenharmony_ci					  sizeof(*__if_rules) * __num_if_rules,
47662306a36Sopenharmony_ci					  rule_payload + rule_plen2))
47762306a36Sopenharmony_ci				return -ENODATA;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci			/* Also check there is no extra data */
48062306a36Sopenharmony_ci			if (__if_rules + __num_if_rules !=
48162306a36Sopenharmony_ci			    rule_payload + rule_plen2)
48262306a36Sopenharmony_ci				return -EINVAL;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci			if (!bin->base) {
48562306a36Sopenharmony_ci				bin_reserve(bin,
48662306a36Sopenharmony_ci					    sizeof(*if_rule) *
48762306a36Sopenharmony_ci					    __num_if_rules);
48862306a36Sopenharmony_ci				num_rules++;
48962306a36Sopenharmony_ci			} else {
49062306a36Sopenharmony_ci				unsigned int i;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci				if (!next_rule)
49362306a36Sopenharmony_ci					return -EIO;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci				rules = next_rule;
49662306a36Sopenharmony_ci				next_rule++;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci				if_rule = bin_alloc(bin,
49962306a36Sopenharmony_ci						    sizeof(*if_rule) *
50062306a36Sopenharmony_ci						    __num_if_rules);
50162306a36Sopenharmony_ci				if (!if_rule)
50262306a36Sopenharmony_ci					return -ENOMEM;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci				for (i = 0; i < __num_if_rules; i++) {
50562306a36Sopenharmony_ci					if_rule[i].addr =
50662306a36Sopenharmony_ci						((u16)__if_rules[i].addr[0]
50762306a36Sopenharmony_ci						 << 8) +
50862306a36Sopenharmony_ci						__if_rules[i].addr[1];
50962306a36Sopenharmony_ci					if_rule[i].value = __if_rules[i].value;
51062306a36Sopenharmony_ci					if_rule[i].mask = __if_rules[i].mask;
51162306a36Sopenharmony_ci				}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci				rules->if_rules = if_rule;
51462306a36Sopenharmony_ci				rules->num_if_rules = __num_if_rules;
51562306a36Sopenharmony_ci			}
51662306a36Sopenharmony_ci		} else {
51762306a36Sopenharmony_ci			/* Check there was an if rule before any other rules */
51862306a36Sopenharmony_ci			if (bin->base && !rules)
51962306a36Sopenharmony_ci				return -EINVAL;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci			switch (*__rule_type) {
52262306a36Sopenharmony_ci			case CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS:
52362306a36Sopenharmony_ci				rval = ccs_data_parse_reg_rules(bin,
52462306a36Sopenharmony_ci								rules ?
52562306a36Sopenharmony_ci								&rules->read_only_regs : NULL,
52662306a36Sopenharmony_ci								rules ?
52762306a36Sopenharmony_ci								&rules->num_read_only_regs : NULL,
52862306a36Sopenharmony_ci								rule_payload,
52962306a36Sopenharmony_ci								rule_payload + rule_plen2,
53062306a36Sopenharmony_ci								dev);
53162306a36Sopenharmony_ci				if (rval)
53262306a36Sopenharmony_ci					return rval;
53362306a36Sopenharmony_ci				break;
53462306a36Sopenharmony_ci			case CCS_DATA_BLOCK_RULE_ID_FFD:
53562306a36Sopenharmony_ci				rval = ccs_data_parse_ffd(bin, rules ?
53662306a36Sopenharmony_ci							  &rules->frame_format : NULL,
53762306a36Sopenharmony_ci							  rule_payload,
53862306a36Sopenharmony_ci							  rule_payload + rule_plen2,
53962306a36Sopenharmony_ci							  dev);
54062306a36Sopenharmony_ci				if (rval)
54162306a36Sopenharmony_ci					return rval;
54262306a36Sopenharmony_ci				break;
54362306a36Sopenharmony_ci			case CCS_DATA_BLOCK_RULE_ID_MSR:
54462306a36Sopenharmony_ci				rval = ccs_data_parse_reg_rules(bin,
54562306a36Sopenharmony_ci								rules ?
54662306a36Sopenharmony_ci								&rules->manufacturer_regs : NULL,
54762306a36Sopenharmony_ci								rules ?
54862306a36Sopenharmony_ci								&rules->num_manufacturer_regs : NULL,
54962306a36Sopenharmony_ci								rule_payload,
55062306a36Sopenharmony_ci								rule_payload + rule_plen2,
55162306a36Sopenharmony_ci								dev);
55262306a36Sopenharmony_ci				if (rval)
55362306a36Sopenharmony_ci					return rval;
55462306a36Sopenharmony_ci				break;
55562306a36Sopenharmony_ci			case CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT:
55662306a36Sopenharmony_ci				rval = ccs_data_parse_pdaf_readout(bin,
55762306a36Sopenharmony_ci								   rules ?
55862306a36Sopenharmony_ci								   &rules->pdaf_readout : NULL,
55962306a36Sopenharmony_ci								   rule_payload,
56062306a36Sopenharmony_ci								   rule_payload + rule_plen2,
56162306a36Sopenharmony_ci								   dev);
56262306a36Sopenharmony_ci				if (rval)
56362306a36Sopenharmony_ci					return rval;
56462306a36Sopenharmony_ci				break;
56562306a36Sopenharmony_ci			default:
56662306a36Sopenharmony_ci				dev_dbg(dev,
56762306a36Sopenharmony_ci					"Don't know how to handle rule type %u!\n",
56862306a36Sopenharmony_ci					*__rule_type);
56962306a36Sopenharmony_ci				return -EINVAL;
57062306a36Sopenharmony_ci			}
57162306a36Sopenharmony_ci		}
57262306a36Sopenharmony_ci		__next_rule = __next_rule + rule_hlen + rule_plen;
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (!bin->base) {
57662306a36Sopenharmony_ci		bin_reserve(bin, sizeof(*rules) * num_rules);
57762306a36Sopenharmony_ci		*__num_rules = num_rules;
57862306a36Sopenharmony_ci	} else {
57962306a36Sopenharmony_ci		if (!rules_base)
58062306a36Sopenharmony_ci			return -EIO;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		*__rules = rules_base;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	return 0;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic int ccs_data_parse_pdaf(struct bin_container *bin, struct ccs_pdaf_pix_loc **pdaf,
58962306a36Sopenharmony_ci			       const void *payload, const void *endp,
59062306a36Sopenharmony_ci			       struct device *dev)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	const struct __ccs_data_block_pdaf_pix_loc *__pdaf = payload;
59362306a36Sopenharmony_ci	const struct __ccs_data_block_pdaf_pix_loc_block_desc_group *__bdesc_group;
59462306a36Sopenharmony_ci	const struct __ccs_data_block_pdaf_pix_loc_pixel_desc *__pixel_desc;
59562306a36Sopenharmony_ci	unsigned int i;
59662306a36Sopenharmony_ci	u16 num_block_desc_groups;
59762306a36Sopenharmony_ci	u8 max_block_type_id = 0;
59862306a36Sopenharmony_ci	const u8 *__num_pixel_descs;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (!is_contained(__pdaf, endp))
60162306a36Sopenharmony_ci		return -ENODATA;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	if (bin->base) {
60462306a36Sopenharmony_ci		*pdaf = bin_alloc(bin, sizeof(**pdaf));
60562306a36Sopenharmony_ci		if (!*pdaf)
60662306a36Sopenharmony_ci			return -ENOMEM;
60762306a36Sopenharmony_ci	} else {
60862306a36Sopenharmony_ci		bin_reserve(bin, sizeof(**pdaf));
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	num_block_desc_groups =
61262306a36Sopenharmony_ci		((u16)__pdaf->num_block_desc_groups[0] << 8) +
61362306a36Sopenharmony_ci		__pdaf->num_block_desc_groups[1];
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (bin->base) {
61662306a36Sopenharmony_ci		(*pdaf)->main_offset_x =
61762306a36Sopenharmony_ci			((u16)__pdaf->main_offset_x[0] << 8) +
61862306a36Sopenharmony_ci			__pdaf->main_offset_x[1];
61962306a36Sopenharmony_ci		(*pdaf)->main_offset_y =
62062306a36Sopenharmony_ci			((u16)__pdaf->main_offset_y[0] << 8) +
62162306a36Sopenharmony_ci			__pdaf->main_offset_y[1];
62262306a36Sopenharmony_ci		(*pdaf)->global_pdaf_type = __pdaf->global_pdaf_type;
62362306a36Sopenharmony_ci		(*pdaf)->block_width = __pdaf->block_width;
62462306a36Sopenharmony_ci		(*pdaf)->block_height = __pdaf->block_height;
62562306a36Sopenharmony_ci		(*pdaf)->num_block_desc_groups = num_block_desc_groups;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	__bdesc_group = (const void *)(__pdaf + 1);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (bin->base) {
63162306a36Sopenharmony_ci		(*pdaf)->block_desc_groups =
63262306a36Sopenharmony_ci			bin_alloc(bin,
63362306a36Sopenharmony_ci				  sizeof(struct ccs_pdaf_pix_loc_block_desc_group) *
63462306a36Sopenharmony_ci				  num_block_desc_groups);
63562306a36Sopenharmony_ci		if (!(*pdaf)->block_desc_groups)
63662306a36Sopenharmony_ci			return -ENOMEM;
63762306a36Sopenharmony_ci	} else {
63862306a36Sopenharmony_ci		bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_block_desc_group) *
63962306a36Sopenharmony_ci			    num_block_desc_groups);
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	for (i = 0; i < num_block_desc_groups; i++) {
64362306a36Sopenharmony_ci		const struct __ccs_data_block_pdaf_pix_loc_block_desc *__bdesc;
64462306a36Sopenharmony_ci		u16 num_block_descs;
64562306a36Sopenharmony_ci		unsigned int j;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		if (!is_contained(__bdesc_group, endp))
64862306a36Sopenharmony_ci			return -ENODATA;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		num_block_descs =
65162306a36Sopenharmony_ci			((u16)__bdesc_group->num_block_descs[0] << 8) +
65262306a36Sopenharmony_ci			__bdesc_group->num_block_descs[1];
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		if (bin->base) {
65562306a36Sopenharmony_ci			(*pdaf)->block_desc_groups[i].repeat_y =
65662306a36Sopenharmony_ci				__bdesc_group->repeat_y;
65762306a36Sopenharmony_ci			(*pdaf)->block_desc_groups[i].num_block_descs =
65862306a36Sopenharmony_ci				num_block_descs;
65962306a36Sopenharmony_ci		}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci		__bdesc = (const void *)(__bdesc_group + 1);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		if (bin->base) {
66462306a36Sopenharmony_ci			(*pdaf)->block_desc_groups[i].block_descs =
66562306a36Sopenharmony_ci				bin_alloc(bin,
66662306a36Sopenharmony_ci					  sizeof(struct ccs_pdaf_pix_loc_block_desc) *
66762306a36Sopenharmony_ci					  num_block_descs);
66862306a36Sopenharmony_ci			if (!(*pdaf)->block_desc_groups[i].block_descs)
66962306a36Sopenharmony_ci				return -ENOMEM;
67062306a36Sopenharmony_ci		} else {
67162306a36Sopenharmony_ci			bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_block_desc) *
67262306a36Sopenharmony_ci				    num_block_descs);
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		for (j = 0; j < num_block_descs; j++, __bdesc++) {
67662306a36Sopenharmony_ci			struct ccs_pdaf_pix_loc_block_desc *bdesc;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci			if (!is_contained(__bdesc, endp))
67962306a36Sopenharmony_ci				return -ENODATA;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci			if (max_block_type_id <= __bdesc->block_type_id)
68262306a36Sopenharmony_ci				max_block_type_id = __bdesc->block_type_id + 1;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci			if (!bin->base)
68562306a36Sopenharmony_ci				continue;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci			bdesc = &(*pdaf)->block_desc_groups[i].block_descs[j];
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci			bdesc->repeat_x = ((u16)__bdesc->repeat_x[0] << 8)
69062306a36Sopenharmony_ci				+ __bdesc->repeat_x[1];
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci			if (__bdesc->block_type_id >= num_block_descs)
69362306a36Sopenharmony_ci				return -EINVAL;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci			bdesc->block_type_id = __bdesc->block_type_id;
69662306a36Sopenharmony_ci		}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		__bdesc_group = (const void *)__bdesc;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	__num_pixel_descs = (const void *)__bdesc_group;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (bin->base) {
70462306a36Sopenharmony_ci		(*pdaf)->pixel_desc_groups =
70562306a36Sopenharmony_ci			bin_alloc(bin,
70662306a36Sopenharmony_ci				  sizeof(struct ccs_pdaf_pix_loc_pixel_desc_group) *
70762306a36Sopenharmony_ci				  max_block_type_id);
70862306a36Sopenharmony_ci		if (!(*pdaf)->pixel_desc_groups)
70962306a36Sopenharmony_ci			return -ENOMEM;
71062306a36Sopenharmony_ci		(*pdaf)->num_pixel_desc_grups = max_block_type_id;
71162306a36Sopenharmony_ci	} else {
71262306a36Sopenharmony_ci		bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_pixel_desc_group) *
71362306a36Sopenharmony_ci			    max_block_type_id);
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	for (i = 0; i < max_block_type_id; i++) {
71762306a36Sopenharmony_ci		struct ccs_pdaf_pix_loc_pixel_desc_group *pdgroup = NULL;
71862306a36Sopenharmony_ci		unsigned int j;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci		if (!is_contained(__num_pixel_descs, endp))
72162306a36Sopenharmony_ci			return -ENODATA;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		if (bin->base) {
72462306a36Sopenharmony_ci			pdgroup = &(*pdaf)->pixel_desc_groups[i];
72562306a36Sopenharmony_ci			pdgroup->descs =
72662306a36Sopenharmony_ci				bin_alloc(bin,
72762306a36Sopenharmony_ci					  sizeof(struct ccs_pdaf_pix_loc_pixel_desc) *
72862306a36Sopenharmony_ci					  *__num_pixel_descs);
72962306a36Sopenharmony_ci			if (!pdgroup->descs)
73062306a36Sopenharmony_ci				return -ENOMEM;
73162306a36Sopenharmony_ci			pdgroup->num_descs = *__num_pixel_descs;
73262306a36Sopenharmony_ci		} else {
73362306a36Sopenharmony_ci			bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_pixel_desc) *
73462306a36Sopenharmony_ci				    *__num_pixel_descs);
73562306a36Sopenharmony_ci		}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		__pixel_desc = (const void *)(__num_pixel_descs + 1);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		for (j = 0; j < *__num_pixel_descs; j++, __pixel_desc++) {
74062306a36Sopenharmony_ci			struct ccs_pdaf_pix_loc_pixel_desc *pdesc;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci			if (!is_contained(__pixel_desc, endp))
74362306a36Sopenharmony_ci				return -ENODATA;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci			if (!bin->base)
74662306a36Sopenharmony_ci				continue;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci			if (!pdgroup)
74962306a36Sopenharmony_ci				return -EIO;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci			pdesc = &pdgroup->descs[j];
75262306a36Sopenharmony_ci			pdesc->pixel_type = __pixel_desc->pixel_type;
75362306a36Sopenharmony_ci			pdesc->small_offset_x = __pixel_desc->small_offset_x;
75462306a36Sopenharmony_ci			pdesc->small_offset_y = __pixel_desc->small_offset_y;
75562306a36Sopenharmony_ci		}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		__num_pixel_descs = (const void *)(__pixel_desc + 1);
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	return 0;
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic int ccs_data_parse_license(struct bin_container *bin,
76462306a36Sopenharmony_ci				  char **__license,
76562306a36Sopenharmony_ci				  size_t *__license_length,
76662306a36Sopenharmony_ci				  const void *payload, const void *endp)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	size_t size = endp - payload;
76962306a36Sopenharmony_ci	char *license;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (!bin->base) {
77262306a36Sopenharmony_ci		bin_reserve(bin, size);
77362306a36Sopenharmony_ci		return 0;
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	license = bin_alloc(bin, size);
77762306a36Sopenharmony_ci	if (!license)
77862306a36Sopenharmony_ci		return -ENOMEM;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	memcpy(license, payload, size);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	*__license = license;
78362306a36Sopenharmony_ci	*__license_length = size;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	return 0;
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic int ccs_data_parse_end(bool *end, const void *payload, const void *endp,
78962306a36Sopenharmony_ci			      struct device *dev)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	const struct __ccs_data_block_end *__end = payload;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	if (__end + 1 != endp) {
79462306a36Sopenharmony_ci		dev_dbg(dev, "Invalid end block length %u\n",
79562306a36Sopenharmony_ci			(unsigned int)(endp - payload));
79662306a36Sopenharmony_ci		return -ENODATA;
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	*end = true;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	return 0;
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic int __ccs_data_parse(struct bin_container *bin,
80562306a36Sopenharmony_ci			    struct ccs_data_container *ccsdata,
80662306a36Sopenharmony_ci			    const void *data, size_t len, struct device *dev,
80762306a36Sopenharmony_ci			    bool verbose)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	const struct __ccs_data_block *block = data;
81062306a36Sopenharmony_ci	const struct __ccs_data_block *endp = data + len;
81162306a36Sopenharmony_ci	unsigned int version;
81262306a36Sopenharmony_ci	bool is_first = true;
81362306a36Sopenharmony_ci	int rval;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	version = ccs_data_parse_format_version(block);
81662306a36Sopenharmony_ci	if (version != CCS_STATIC_DATA_VERSION) {
81762306a36Sopenharmony_ci		dev_dbg(dev, "Don't know how to handle version %u\n", version);
81862306a36Sopenharmony_ci		return -EINVAL;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (verbose)
82262306a36Sopenharmony_ci		dev_dbg(dev, "Parsing CCS static data version %u\n", version);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (!bin->base)
82562306a36Sopenharmony_ci		*ccsdata = (struct ccs_data_container){ 0 };
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	while (block < endp) {
82862306a36Sopenharmony_ci		const struct __ccs_data_block *next_block;
82962306a36Sopenharmony_ci		unsigned int block_id;
83062306a36Sopenharmony_ci		const void *payload;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci		rval = ccs_data_block_parse_header(block, is_first, &block_id,
83362306a36Sopenharmony_ci						   &payload, &next_block, endp,
83462306a36Sopenharmony_ci						   dev,
83562306a36Sopenharmony_ci						   bin->base ? false : verbose);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		if (rval < 0)
83862306a36Sopenharmony_ci			return rval;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		switch (block_id) {
84162306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_DUMMY:
84262306a36Sopenharmony_ci			break;
84362306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_DATA_VERSION:
84462306a36Sopenharmony_ci			rval = ccs_data_parse_version(bin, ccsdata, payload,
84562306a36Sopenharmony_ci						      next_block);
84662306a36Sopenharmony_ci			if (rval < 0)
84762306a36Sopenharmony_ci				return rval;
84862306a36Sopenharmony_ci			break;
84962306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_SENSOR_READ_ONLY_REGS:
85062306a36Sopenharmony_ci			rval = ccs_data_parse_regs(
85162306a36Sopenharmony_ci				bin, &ccsdata->sensor_read_only_regs,
85262306a36Sopenharmony_ci				&ccsdata->num_sensor_read_only_regs, payload,
85362306a36Sopenharmony_ci				next_block, dev);
85462306a36Sopenharmony_ci			if (rval < 0)
85562306a36Sopenharmony_ci				return rval;
85662306a36Sopenharmony_ci			break;
85762306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_SENSOR_MANUFACTURER_REGS:
85862306a36Sopenharmony_ci			rval = ccs_data_parse_regs(
85962306a36Sopenharmony_ci				bin, &ccsdata->sensor_manufacturer_regs,
86062306a36Sopenharmony_ci				&ccsdata->num_sensor_manufacturer_regs, payload,
86162306a36Sopenharmony_ci				next_block, dev);
86262306a36Sopenharmony_ci			if (rval < 0)
86362306a36Sopenharmony_ci				return rval;
86462306a36Sopenharmony_ci			break;
86562306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_MODULE_READ_ONLY_REGS:
86662306a36Sopenharmony_ci			rval = ccs_data_parse_regs(
86762306a36Sopenharmony_ci				bin, &ccsdata->module_read_only_regs,
86862306a36Sopenharmony_ci				&ccsdata->num_module_read_only_regs, payload,
86962306a36Sopenharmony_ci				next_block, dev);
87062306a36Sopenharmony_ci			if (rval < 0)
87162306a36Sopenharmony_ci				return rval;
87262306a36Sopenharmony_ci			break;
87362306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_MODULE_MANUFACTURER_REGS:
87462306a36Sopenharmony_ci			rval = ccs_data_parse_regs(
87562306a36Sopenharmony_ci				bin, &ccsdata->module_manufacturer_regs,
87662306a36Sopenharmony_ci				&ccsdata->num_module_manufacturer_regs, payload,
87762306a36Sopenharmony_ci				next_block, dev);
87862306a36Sopenharmony_ci			if (rval < 0)
87962306a36Sopenharmony_ci				return rval;
88062306a36Sopenharmony_ci			break;
88162306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_SENSOR_PDAF_PIXEL_LOCATION:
88262306a36Sopenharmony_ci			rval = ccs_data_parse_pdaf(bin, &ccsdata->sensor_pdaf,
88362306a36Sopenharmony_ci						   payload, next_block, dev);
88462306a36Sopenharmony_ci			if (rval < 0)
88562306a36Sopenharmony_ci				return rval;
88662306a36Sopenharmony_ci			break;
88762306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_MODULE_PDAF_PIXEL_LOCATION:
88862306a36Sopenharmony_ci			rval = ccs_data_parse_pdaf(bin, &ccsdata->module_pdaf,
88962306a36Sopenharmony_ci						   payload, next_block, dev);
89062306a36Sopenharmony_ci			if (rval < 0)
89162306a36Sopenharmony_ci				return rval;
89262306a36Sopenharmony_ci			break;
89362306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_SENSOR_RULE_BASED_BLOCK:
89462306a36Sopenharmony_ci			rval = ccs_data_parse_rules(
89562306a36Sopenharmony_ci				bin, &ccsdata->sensor_rules,
89662306a36Sopenharmony_ci				&ccsdata->num_sensor_rules, payload, next_block,
89762306a36Sopenharmony_ci				dev);
89862306a36Sopenharmony_ci			if (rval < 0)
89962306a36Sopenharmony_ci				return rval;
90062306a36Sopenharmony_ci			break;
90162306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_MODULE_RULE_BASED_BLOCK:
90262306a36Sopenharmony_ci			rval = ccs_data_parse_rules(
90362306a36Sopenharmony_ci				bin, &ccsdata->module_rules,
90462306a36Sopenharmony_ci				&ccsdata->num_module_rules, payload, next_block,
90562306a36Sopenharmony_ci				dev);
90662306a36Sopenharmony_ci			if (rval < 0)
90762306a36Sopenharmony_ci				return rval;
90862306a36Sopenharmony_ci			break;
90962306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_LICENSE:
91062306a36Sopenharmony_ci			rval = ccs_data_parse_license(bin, &ccsdata->license,
91162306a36Sopenharmony_ci						      &ccsdata->license_length,
91262306a36Sopenharmony_ci						      payload, next_block);
91362306a36Sopenharmony_ci			if (rval < 0)
91462306a36Sopenharmony_ci				return rval;
91562306a36Sopenharmony_ci			break;
91662306a36Sopenharmony_ci		case CCS_DATA_BLOCK_ID_END:
91762306a36Sopenharmony_ci			rval = ccs_data_parse_end(&ccsdata->end, payload,
91862306a36Sopenharmony_ci						  next_block, dev);
91962306a36Sopenharmony_ci			if (rval < 0)
92062306a36Sopenharmony_ci				return rval;
92162306a36Sopenharmony_ci			break;
92262306a36Sopenharmony_ci		default:
92362306a36Sopenharmony_ci			dev_dbg(dev, "WARNING: not handling block ID 0x%2.2x\n",
92462306a36Sopenharmony_ci				block_id);
92562306a36Sopenharmony_ci		}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		block = next_block;
92862306a36Sopenharmony_ci		is_first = false;
92962306a36Sopenharmony_ci	}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	return 0;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci/**
93562306a36Sopenharmony_ci * ccs_data_parse - Parse a CCS static data file into a usable in-memory
93662306a36Sopenharmony_ci *		    data structure
93762306a36Sopenharmony_ci * @ccsdata:	CCS static data in-memory data structure
93862306a36Sopenharmony_ci * @data:	CCS static data binary
93962306a36Sopenharmony_ci * @len:	Length of @data
94062306a36Sopenharmony_ci * @dev:	Device the data is related to (used for printing debug messages)
94162306a36Sopenharmony_ci * @verbose:	Whether to be verbose or not
94262306a36Sopenharmony_ci */
94362306a36Sopenharmony_ciint ccs_data_parse(struct ccs_data_container *ccsdata, const void *data,
94462306a36Sopenharmony_ci		   size_t len, struct device *dev, bool verbose)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	struct bin_container bin = { 0 };
94762306a36Sopenharmony_ci	int rval;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, verbose);
95062306a36Sopenharmony_ci	if (rval)
95162306a36Sopenharmony_ci		return rval;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	rval = bin_backing_alloc(&bin);
95462306a36Sopenharmony_ci	if (rval)
95562306a36Sopenharmony_ci		return rval;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, false);
95862306a36Sopenharmony_ci	if (rval)
95962306a36Sopenharmony_ci		goto out_free;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	if (verbose && ccsdata->version)
96262306a36Sopenharmony_ci		print_ccs_data_version(dev, ccsdata->version);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (bin.now != bin.end) {
96562306a36Sopenharmony_ci		rval = -EPROTO;
96662306a36Sopenharmony_ci		dev_dbg(dev, "parsing mismatch; base %p; now %p; end %p\n",
96762306a36Sopenharmony_ci			bin.base, bin.now, bin.end);
96862306a36Sopenharmony_ci		goto out_free;
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	ccsdata->backing = bin.base;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	return 0;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ciout_free:
97662306a36Sopenharmony_ci	kvfree(bin.base);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	return rval;
97962306a36Sopenharmony_ci}
980