xref: /openbmc/linux/drivers/media/i2c/ccs/ccs-data.c (revision 607bcc42)
1a6b396f4SSakari Ailus // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2a6b396f4SSakari Ailus /*
3a6b396f4SSakari Ailus  * CCS static data binary parser library
4a6b396f4SSakari Ailus  *
5a6b396f4SSakari Ailus  * Copyright 2019--2020 Intel Corporation
6a6b396f4SSakari Ailus  */
7a6b396f4SSakari Ailus 
8a6b396f4SSakari Ailus #include <linux/device.h>
9a6b396f4SSakari Ailus #include <linux/errno.h>
10a6b396f4SSakari Ailus #include <linux/limits.h>
11a6b396f4SSakari Ailus #include <linux/mm.h>
12a6b396f4SSakari Ailus #include <linux/slab.h>
13a6b396f4SSakari Ailus 
14a6b396f4SSakari Ailus #include "ccs-data-defs.h"
15a6b396f4SSakari Ailus 
16a6b396f4SSakari Ailus struct bin_container {
17a6b396f4SSakari Ailus 	void *base;
18a6b396f4SSakari Ailus 	void *now;
19a6b396f4SSakari Ailus 	void *end;
20a6b396f4SSakari Ailus 	size_t size;
21a6b396f4SSakari Ailus };
22a6b396f4SSakari Ailus 
bin_alloc(struct bin_container * bin,size_t len)23a6b396f4SSakari Ailus static void *bin_alloc(struct bin_container *bin, size_t len)
24a6b396f4SSakari Ailus {
25a6b396f4SSakari Ailus 	void *ptr;
26a6b396f4SSakari Ailus 
27a6b396f4SSakari Ailus 	len = ALIGN(len, 8);
28a6b396f4SSakari Ailus 
29a6b396f4SSakari Ailus 	if (bin->end - bin->now < len)
30a6b396f4SSakari Ailus 		return NULL;
31a6b396f4SSakari Ailus 
32a6b396f4SSakari Ailus 	ptr = bin->now;
33a6b396f4SSakari Ailus 	bin->now += len;
34a6b396f4SSakari Ailus 
35a6b396f4SSakari Ailus 	return ptr;
36a6b396f4SSakari Ailus }
37a6b396f4SSakari Ailus 
bin_reserve(struct bin_container * bin,size_t len)38a6b396f4SSakari Ailus static void bin_reserve(struct bin_container *bin, size_t len)
39a6b396f4SSakari Ailus {
40a6b396f4SSakari Ailus 	bin->size += ALIGN(len, 8);
41a6b396f4SSakari Ailus }
42a6b396f4SSakari Ailus 
bin_backing_alloc(struct bin_container * bin)43a6b396f4SSakari Ailus static int bin_backing_alloc(struct bin_container *bin)
44a6b396f4SSakari Ailus {
45a6b396f4SSakari Ailus 	bin->base = bin->now = kvzalloc(bin->size, GFP_KERNEL);
46a6b396f4SSakari Ailus 	if (!bin->base)
47a6b396f4SSakari Ailus 		return -ENOMEM;
48a6b396f4SSakari Ailus 
49a6b396f4SSakari Ailus 	bin->end = bin->base + bin->size;
50a6b396f4SSakari Ailus 
51a6b396f4SSakari Ailus 	return 0;
52a6b396f4SSakari Ailus }
53a6b396f4SSakari Ailus 
54a6b396f4SSakari Ailus #define is_contained(var, endp)				\
55a6b396f4SSakari Ailus 	(sizeof(*var) <= (endp) - (void *)(var))
56a6b396f4SSakari Ailus #define has_headroom(ptr, headroom, endp)	\
57a6b396f4SSakari Ailus 	((headroom) <= (endp) - (void *)(ptr))
58a6b396f4SSakari Ailus #define is_contained_with_headroom(var, headroom, endp)		\
59a6b396f4SSakari Ailus 	(sizeof(*var) + (headroom) <= (endp) - (void *)(var))
60a6b396f4SSakari Ailus 
61a6b396f4SSakari Ailus static int
ccs_data_parse_length_specifier(const struct __ccs_data_length_specifier * __len,size_t * __hlen,size_t * __plen,const void * endp)62a6b396f4SSakari Ailus ccs_data_parse_length_specifier(const struct __ccs_data_length_specifier *__len,
63a6b396f4SSakari Ailus 				size_t *__hlen, size_t *__plen,
64a6b396f4SSakari Ailus 				const void *endp)
65a6b396f4SSakari Ailus {
66a6b396f4SSakari Ailus 	size_t hlen, plen;
67a6b396f4SSakari Ailus 
68a6b396f4SSakari Ailus 	if (!is_contained(__len, endp))
69a6b396f4SSakari Ailus 		return -ENODATA;
70a6b396f4SSakari Ailus 
71a6b396f4SSakari Ailus 	switch (__len->length >> CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) {
72a6b396f4SSakari Ailus 	case CCS_DATA_LENGTH_SPECIFIER_1:
73a6b396f4SSakari Ailus 		hlen = sizeof(*__len);
74a6b396f4SSakari Ailus 		plen = __len->length &
75a6b396f4SSakari Ailus 			((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1);
76a6b396f4SSakari Ailus 		break;
77a6b396f4SSakari Ailus 	case CCS_DATA_LENGTH_SPECIFIER_2: {
78a6b396f4SSakari Ailus 		struct __ccs_data_length_specifier2 *__len2 = (void *)__len;
79a6b396f4SSakari Ailus 
80a6b396f4SSakari Ailus 		if (!is_contained(__len2, endp))
81a6b396f4SSakari Ailus 			return -ENODATA;
82a6b396f4SSakari Ailus 
83a6b396f4SSakari Ailus 		hlen = sizeof(*__len2);
84a6b396f4SSakari Ailus 		plen = ((size_t)
85a6b396f4SSakari Ailus 			(__len2->length[0] &
86a6b396f4SSakari Ailus 			 ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1))
87a6b396f4SSakari Ailus 			<< 8) + __len2->length[1];
88a6b396f4SSakari Ailus 		break;
89a6b396f4SSakari Ailus 	}
90a6b396f4SSakari Ailus 	case CCS_DATA_LENGTH_SPECIFIER_3: {
91a6b396f4SSakari Ailus 		struct __ccs_data_length_specifier3 *__len3 = (void *)__len;
92a6b396f4SSakari Ailus 
93a6b396f4SSakari Ailus 		if (!is_contained(__len3, endp))
94a6b396f4SSakari Ailus 			return -ENODATA;
95a6b396f4SSakari Ailus 
96a6b396f4SSakari Ailus 		hlen = sizeof(*__len3);
97a6b396f4SSakari Ailus 		plen = ((size_t)
98a6b396f4SSakari Ailus 			(__len3->length[0] &
99a6b396f4SSakari Ailus 			 ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1))
100a6b396f4SSakari Ailus 			<< 16) + (__len3->length[0] << 8) + __len3->length[1];
101a6b396f4SSakari Ailus 		break;
102a6b396f4SSakari Ailus 	}
103a6b396f4SSakari Ailus 	default:
104a6b396f4SSakari Ailus 		return -EINVAL;
105a6b396f4SSakari Ailus 	}
106a6b396f4SSakari Ailus 
107a6b396f4SSakari Ailus 	if (!has_headroom(__len, hlen + plen, endp))
108a6b396f4SSakari Ailus 		return -ENODATA;
109a6b396f4SSakari Ailus 
110a6b396f4SSakari Ailus 	*__hlen = hlen;
111a6b396f4SSakari Ailus 	*__plen = plen;
112a6b396f4SSakari Ailus 
113a6b396f4SSakari Ailus 	return 0;
114a6b396f4SSakari Ailus }
115a6b396f4SSakari Ailus 
116a6b396f4SSakari Ailus static u8
ccs_data_parse_format_version(const struct __ccs_data_block * block)117a6b396f4SSakari Ailus ccs_data_parse_format_version(const struct __ccs_data_block *block)
118a6b396f4SSakari Ailus {
119a6b396f4SSakari Ailus 	return block->id >> CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT;
120a6b396f4SSakari Ailus }
121a6b396f4SSakari Ailus 
ccs_data_parse_block_id(const struct __ccs_data_block * block,bool is_first)122a6b396f4SSakari Ailus static u8 ccs_data_parse_block_id(const struct __ccs_data_block *block,
123a6b396f4SSakari Ailus 				       bool is_first)
124a6b396f4SSakari Ailus {
125a6b396f4SSakari Ailus 	if (!is_first)
126a6b396f4SSakari Ailus 		return block->id;
127a6b396f4SSakari Ailus 
128a6b396f4SSakari Ailus 	return block->id & ((1 << CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT) - 1);
129a6b396f4SSakari Ailus }
130a6b396f4SSakari Ailus 
ccs_data_parse_version(struct bin_container * bin,struct ccs_data_container * ccsdata,const void * payload,const void * endp)131a6b396f4SSakari Ailus static int ccs_data_parse_version(struct bin_container *bin,
132a6b396f4SSakari Ailus 				  struct ccs_data_container *ccsdata,
133a6b396f4SSakari Ailus 				  const void *payload, const void *endp)
134a6b396f4SSakari Ailus {
135a6b396f4SSakari Ailus 	const struct __ccs_data_block_version *v = payload;
136a6b396f4SSakari Ailus 	struct ccs_data_block_version *vv;
137a6b396f4SSakari Ailus 
138a6b396f4SSakari Ailus 	if (v + 1 != endp)
139a6b396f4SSakari Ailus 		return -ENODATA;
140a6b396f4SSakari Ailus 
141a6b396f4SSakari Ailus 	if (!bin->base) {
142a6b396f4SSakari Ailus 		bin_reserve(bin, sizeof(*ccsdata->version));
143a6b396f4SSakari Ailus 		return 0;
144a6b396f4SSakari Ailus 	}
145a6b396f4SSakari Ailus 
146a6b396f4SSakari Ailus 	ccsdata->version = bin_alloc(bin, sizeof(*ccsdata->version));
147a6b396f4SSakari Ailus 	if (!ccsdata->version)
148a6b396f4SSakari Ailus 		return -ENOMEM;
149a6b396f4SSakari Ailus 
150a6b396f4SSakari Ailus 	vv = ccsdata->version;
151a6b396f4SSakari Ailus 	vv->version_major = ((u16)v->static_data_version_major[0] << 8) +
152a6b396f4SSakari Ailus 		v->static_data_version_major[1];
153a6b396f4SSakari Ailus 	vv->version_minor = ((u16)v->static_data_version_minor[0] << 8) +
1541bc0b1baSSakari Ailus 		v->static_data_version_minor[1];
155a6b396f4SSakari Ailus 	vv->date_year =  ((u16)v->year[0] << 8) + v->year[1];
156a6b396f4SSakari Ailus 	vv->date_month = v->month;
157a6b396f4SSakari Ailus 	vv->date_day = v->day;
158a6b396f4SSakari Ailus 
159a6b396f4SSakari Ailus 	return 0;
160a6b396f4SSakari Ailus }
161a6b396f4SSakari Ailus 
print_ccs_data_version(struct device * dev,struct ccs_data_block_version * v)162a6b396f4SSakari Ailus static void print_ccs_data_version(struct device *dev,
163a6b396f4SSakari Ailus 				   struct ccs_data_block_version *v)
164a6b396f4SSakari Ailus {
165a6b396f4SSakari Ailus 	dev_dbg(dev,
166a6b396f4SSakari Ailus 		"static data version %4.4x.%4.4x, date %4.4u-%2.2u-%2.2u\n",
167a6b396f4SSakari Ailus 		v->version_major, v->version_minor,
168a6b396f4SSakari Ailus 		v->date_year, v->date_month, v->date_day);
169a6b396f4SSakari Ailus }
170a6b396f4SSakari Ailus 
ccs_data_block_parse_header(const struct __ccs_data_block * block,bool is_first,unsigned int * __block_id,const void ** payload,const struct __ccs_data_block ** next_block,const void * endp,struct device * dev,bool verbose)171a6b396f4SSakari Ailus static int ccs_data_block_parse_header(const struct __ccs_data_block *block,
172a6b396f4SSakari Ailus 				       bool is_first, unsigned int *__block_id,
173a6b396f4SSakari Ailus 				       const void **payload,
174a6b396f4SSakari Ailus 				       const struct __ccs_data_block **next_block,
175a6b396f4SSakari Ailus 				       const void *endp, struct device *dev,
176a6b396f4SSakari Ailus 				       bool verbose)
177a6b396f4SSakari Ailus {
178a6b396f4SSakari Ailus 	size_t plen, hlen;
179a6b396f4SSakari Ailus 	u8 block_id;
180a6b396f4SSakari Ailus 	int rval;
181a6b396f4SSakari Ailus 
182a6b396f4SSakari Ailus 	if (!is_contained(block, endp))
183a6b396f4SSakari Ailus 		return -ENODATA;
184a6b396f4SSakari Ailus 
185a6b396f4SSakari Ailus 	rval = ccs_data_parse_length_specifier(&block->length, &hlen, &plen,
186a6b396f4SSakari Ailus 					       endp);
187a6b396f4SSakari Ailus 	if (rval < 0)
188a6b396f4SSakari Ailus 		return rval;
189a6b396f4SSakari Ailus 
190a6b396f4SSakari Ailus 	block_id = ccs_data_parse_block_id(block, is_first);
191a6b396f4SSakari Ailus 
192a6b396f4SSakari Ailus 	if (verbose)
193a6b396f4SSakari Ailus 		dev_dbg(dev,
194a6b396f4SSakari Ailus 			"Block ID 0x%2.2x, header length %zu, payload length %zu\n",
195a6b396f4SSakari Ailus 			block_id, hlen, plen);
196a6b396f4SSakari Ailus 
197a6b396f4SSakari Ailus 	if (!has_headroom(&block->length, hlen + plen, endp))
198a6b396f4SSakari Ailus 		return -ENODATA;
199a6b396f4SSakari Ailus 
200a6b396f4SSakari Ailus 	if (__block_id)
201a6b396f4SSakari Ailus 		*__block_id = block_id;
202a6b396f4SSakari Ailus 
203a6b396f4SSakari Ailus 	if (payload)
204a6b396f4SSakari Ailus 		*payload = (void *)&block->length + hlen;
205a6b396f4SSakari Ailus 
206a6b396f4SSakari Ailus 	if (next_block)
207a6b396f4SSakari Ailus 		*next_block = (void *)&block->length + hlen + plen;
208a6b396f4SSakari Ailus 
209a6b396f4SSakari Ailus 	return 0;
210a6b396f4SSakari Ailus }
211a6b396f4SSakari Ailus 
ccs_data_parse_regs(struct bin_container * bin,struct ccs_reg ** __regs,size_t * __num_regs,const void * payload,const void * endp,struct device * dev)212a6b396f4SSakari Ailus static int ccs_data_parse_regs(struct bin_container *bin,
213a6b396f4SSakari Ailus 			       struct ccs_reg **__regs,
214a6b396f4SSakari Ailus 			       size_t *__num_regs, const void *payload,
215a6b396f4SSakari Ailus 			       const void *endp, struct device *dev)
216a6b396f4SSakari Ailus {
217ae56e038SSakari Ailus 	struct ccs_reg *regs_base = NULL, *regs = NULL;
218a6b396f4SSakari Ailus 	size_t num_regs = 0;
219a6b396f4SSakari Ailus 	u16 addr = 0;
220a6b396f4SSakari Ailus 
221a6b396f4SSakari Ailus 	if (bin->base && __regs) {
222a6b396f4SSakari Ailus 		regs = regs_base = bin_alloc(bin, sizeof(*regs) * *__num_regs);
223a6b396f4SSakari Ailus 		if (!regs)
224a6b396f4SSakari Ailus 			return -ENOMEM;
225a6b396f4SSakari Ailus 	}
226a6b396f4SSakari Ailus 
227a6b396f4SSakari Ailus 	while (payload < endp && num_regs < INT_MAX) {
228a6b396f4SSakari Ailus 		const struct __ccs_data_block_regs *r = payload;
229a6b396f4SSakari Ailus 		size_t len;
230a6b396f4SSakari Ailus 		const void *data;
231a6b396f4SSakari Ailus 
232a6b396f4SSakari Ailus 		if (!is_contained(r, endp))
233a6b396f4SSakari Ailus 			return -ENODATA;
234a6b396f4SSakari Ailus 
235a6b396f4SSakari Ailus 		switch (r->reg_len >> CCS_DATA_BLOCK_REGS_SEL_SHIFT) {
236a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_REGS_SEL_REGS:
237a6b396f4SSakari Ailus 			addr += r->reg_len & CCS_DATA_BLOCK_REGS_ADDR_MASK;
238a6b396f4SSakari Ailus 			len = ((r->reg_len & CCS_DATA_BLOCK_REGS_LEN_MASK)
239a6b396f4SSakari Ailus 			       >> CCS_DATA_BLOCK_REGS_LEN_SHIFT) + 1;
240a6b396f4SSakari Ailus 
241a6b396f4SSakari Ailus 			if (!is_contained_with_headroom(r, len, endp))
242a6b396f4SSakari Ailus 				return -ENODATA;
243a6b396f4SSakari Ailus 
244a6b396f4SSakari Ailus 			data = r + 1;
245a6b396f4SSakari Ailus 			break;
246a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_REGS_SEL_REGS2: {
247a6b396f4SSakari Ailus 			const struct __ccs_data_block_regs2 *r2 = payload;
248a6b396f4SSakari Ailus 
249a6b396f4SSakari Ailus 			if (!is_contained(r2, endp))
250a6b396f4SSakari Ailus 				return -ENODATA;
251a6b396f4SSakari Ailus 
252a6b396f4SSakari Ailus 			addr += ((u16)(r2->reg_len &
253a6b396f4SSakari Ailus 				       CCS_DATA_BLOCK_REGS_2_ADDR_MASK) << 8)
254a6b396f4SSakari Ailus 				+ r2->addr;
255a6b396f4SSakari Ailus 			len = ((r2->reg_len & CCS_DATA_BLOCK_REGS_2_LEN_MASK)
256a6b396f4SSakari Ailus 			       >> CCS_DATA_BLOCK_REGS_2_LEN_SHIFT) + 1;
257a6b396f4SSakari Ailus 
258a6b396f4SSakari Ailus 			if (!is_contained_with_headroom(r2, len, endp))
259a6b396f4SSakari Ailus 				return -ENODATA;
260a6b396f4SSakari Ailus 
261a6b396f4SSakari Ailus 			data = r2 + 1;
262a6b396f4SSakari Ailus 			break;
263a6b396f4SSakari Ailus 		}
264a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_REGS_SEL_REGS3: {
265a6b396f4SSakari Ailus 			const struct __ccs_data_block_regs3 *r3 = payload;
266a6b396f4SSakari Ailus 
267a6b396f4SSakari Ailus 			if (!is_contained(r3, endp))
268a6b396f4SSakari Ailus 				return -ENODATA;
269a6b396f4SSakari Ailus 
270a6b396f4SSakari Ailus 			addr = ((u16)r3->addr[0] << 8) + r3->addr[1];
271a6b396f4SSakari Ailus 			len = (r3->reg_len & CCS_DATA_BLOCK_REGS_3_LEN_MASK) + 1;
272a6b396f4SSakari Ailus 
273a6b396f4SSakari Ailus 			if (!is_contained_with_headroom(r3, len, endp))
274a6b396f4SSakari Ailus 				return -ENODATA;
275a6b396f4SSakari Ailus 
276a6b396f4SSakari Ailus 			data = r3 + 1;
277a6b396f4SSakari Ailus 			break;
278a6b396f4SSakari Ailus 		}
279a6b396f4SSakari Ailus 		default:
280a6b396f4SSakari Ailus 			return -EINVAL;
281a6b396f4SSakari Ailus 		}
282a6b396f4SSakari Ailus 
283a6b396f4SSakari Ailus 		num_regs++;
284a6b396f4SSakari Ailus 
285a6b396f4SSakari Ailus 		if (!bin->base) {
286a6b396f4SSakari Ailus 			bin_reserve(bin, len);
287a6b396f4SSakari Ailus 		} else if (__regs) {
288ae56e038SSakari Ailus 			if (!regs)
289ae56e038SSakari Ailus 				return -EIO;
290ae56e038SSakari Ailus 
291a6b396f4SSakari Ailus 			regs->addr = addr;
292a6b396f4SSakari Ailus 			regs->len = len;
293a6b396f4SSakari Ailus 			regs->value = bin_alloc(bin, len);
294a6b396f4SSakari Ailus 			if (!regs->value)
295a6b396f4SSakari Ailus 				return -ENOMEM;
296a6b396f4SSakari Ailus 
297a6b396f4SSakari Ailus 			memcpy(regs->value, data, len);
298a6b396f4SSakari Ailus 			regs++;
299a6b396f4SSakari Ailus 		}
300a6b396f4SSakari Ailus 
301a6b396f4SSakari Ailus 		addr += len;
302a6b396f4SSakari Ailus 		payload = data + len;
303a6b396f4SSakari Ailus 	}
304a6b396f4SSakari Ailus 
305a6b396f4SSakari Ailus 	if (!bin->base)
306a6b396f4SSakari Ailus 		bin_reserve(bin, sizeof(*regs) * num_regs);
307a6b396f4SSakari Ailus 
308a6b396f4SSakari Ailus 	if (__num_regs)
309a6b396f4SSakari Ailus 		*__num_regs = num_regs;
310a6b396f4SSakari Ailus 
311ae56e038SSakari Ailus 	if (bin->base && __regs) {
312ae56e038SSakari Ailus 		if (!regs_base)
313ae56e038SSakari Ailus 			return -EIO;
314ae56e038SSakari Ailus 
315a6b396f4SSakari Ailus 		*__regs = regs_base;
316ae56e038SSakari Ailus 	}
317a6b396f4SSakari Ailus 
318a6b396f4SSakari Ailus 	return 0;
319a6b396f4SSakari Ailus }
320a6b396f4SSakari Ailus 
ccs_data_parse_reg_rules(struct bin_container * bin,struct ccs_reg ** __regs,size_t * __num_regs,const void * payload,const void * endp,struct device * dev)321a6b396f4SSakari Ailus static int ccs_data_parse_reg_rules(struct bin_container *bin,
322a6b396f4SSakari Ailus 				    struct ccs_reg **__regs,
323a6b396f4SSakari Ailus 				    size_t *__num_regs,
324a6b396f4SSakari Ailus 				    const void *payload,
325a6b396f4SSakari Ailus 				    const void *endp, struct device *dev)
326a6b396f4SSakari Ailus {
327a6b396f4SSakari Ailus 	int rval;
328a6b396f4SSakari Ailus 
329a6b396f4SSakari Ailus 	if (!bin->base)
330a6b396f4SSakari Ailus 		return ccs_data_parse_regs(bin, NULL, NULL, payload, endp, dev);
331a6b396f4SSakari Ailus 
332a6b396f4SSakari Ailus 	rval = ccs_data_parse_regs(bin, NULL, __num_regs, payload, endp, dev);
333a6b396f4SSakari Ailus 	if (rval)
334a6b396f4SSakari Ailus 		return rval;
335a6b396f4SSakari Ailus 
336a6b396f4SSakari Ailus 	return ccs_data_parse_regs(bin, __regs, __num_regs, payload, endp,
337a6b396f4SSakari Ailus 				   dev);
338a6b396f4SSakari Ailus }
339a6b396f4SSakari Ailus 
assign_ffd_entry(struct ccs_frame_format_desc * desc,const struct __ccs_data_block_ffd_entry * ent)340a6b396f4SSakari Ailus static void assign_ffd_entry(struct ccs_frame_format_desc *desc,
341a6b396f4SSakari Ailus 			     const struct __ccs_data_block_ffd_entry *ent)
342a6b396f4SSakari Ailus {
343a6b396f4SSakari Ailus 	desc->pixelcode = ent->pixelcode;
344a6b396f4SSakari Ailus 	desc->value = ((u16)ent->value[0] << 8) + ent->value[1];
345a6b396f4SSakari Ailus }
346a6b396f4SSakari Ailus 
ccs_data_parse_ffd(struct bin_container * bin,struct ccs_frame_format_descs ** ffd,const void * payload,const void * endp,struct device * dev)347a6b396f4SSakari Ailus static int ccs_data_parse_ffd(struct bin_container *bin,
348a6b396f4SSakari Ailus 			      struct ccs_frame_format_descs **ffd,
349a6b396f4SSakari Ailus 			      const void *payload,
350a6b396f4SSakari Ailus 			      const void *endp, struct device *dev)
351a6b396f4SSakari Ailus {
352a6b396f4SSakari Ailus 	const struct __ccs_data_block_ffd *__ffd = payload;
353a6b396f4SSakari Ailus 	const struct __ccs_data_block_ffd_entry *__entry;
354a6b396f4SSakari Ailus 	unsigned int i;
355a6b396f4SSakari Ailus 
356a6b396f4SSakari Ailus 	if (!is_contained(__ffd, endp))
357a6b396f4SSakari Ailus 		return -ENODATA;
358a6b396f4SSakari Ailus 
359a6b396f4SSakari Ailus 	if ((void *)__ffd + sizeof(*__ffd) +
360a6b396f4SSakari Ailus 	    ((u32)__ffd->num_column_descs +
361a6b396f4SSakari Ailus 	     (u32)__ffd->num_row_descs) *
362a6b396f4SSakari Ailus 	    sizeof(struct __ccs_data_block_ffd_entry) != endp)
363a6b396f4SSakari Ailus 		return -ENODATA;
364a6b396f4SSakari Ailus 
365a6b396f4SSakari Ailus 	if (!bin->base) {
366a6b396f4SSakari Ailus 		bin_reserve(bin, sizeof(**ffd));
367a6b396f4SSakari Ailus 		bin_reserve(bin, __ffd->num_column_descs *
368a6b396f4SSakari Ailus 			    sizeof(struct ccs_frame_format_desc));
369a6b396f4SSakari Ailus 		bin_reserve(bin, __ffd->num_row_descs *
370a6b396f4SSakari Ailus 			    sizeof(struct ccs_frame_format_desc));
371a6b396f4SSakari Ailus 
372a6b396f4SSakari Ailus 		return 0;
373a6b396f4SSakari Ailus 	}
374a6b396f4SSakari Ailus 
375a6b396f4SSakari Ailus 	*ffd = bin_alloc(bin, sizeof(**ffd));
376a6b396f4SSakari Ailus 	if (!*ffd)
377a6b396f4SSakari Ailus 		return -ENOMEM;
378a6b396f4SSakari Ailus 
379a6b396f4SSakari Ailus 	(*ffd)->num_column_descs = __ffd->num_column_descs;
380a6b396f4SSakari Ailus 	(*ffd)->num_row_descs = __ffd->num_row_descs;
381a6b396f4SSakari Ailus 	__entry = (void *)(__ffd + 1);
382a6b396f4SSakari Ailus 
383a6b396f4SSakari Ailus 	(*ffd)->column_descs = bin_alloc(bin, __ffd->num_column_descs *
384a6b396f4SSakari Ailus 					 sizeof(*(*ffd)->column_descs));
385a6b396f4SSakari Ailus 	if (!(*ffd)->column_descs)
386a6b396f4SSakari Ailus 		return -ENOMEM;
387a6b396f4SSakari Ailus 
388a6b396f4SSakari Ailus 	for (i = 0; i < __ffd->num_column_descs; i++, __entry++)
389a6b396f4SSakari Ailus 		assign_ffd_entry(&(*ffd)->column_descs[i], __entry);
390a6b396f4SSakari Ailus 
391a6b396f4SSakari Ailus 	(*ffd)->row_descs = bin_alloc(bin, __ffd->num_row_descs *
392a6b396f4SSakari Ailus 				      sizeof(*(*ffd)->row_descs));
393a6b396f4SSakari Ailus 	if (!(*ffd)->row_descs)
394a6b396f4SSakari Ailus 		return -ENOMEM;
395a6b396f4SSakari Ailus 
396a6b396f4SSakari Ailus 	for (i = 0; i < __ffd->num_row_descs; i++, __entry++)
397a6b396f4SSakari Ailus 		assign_ffd_entry(&(*ffd)->row_descs[i], __entry);
398a6b396f4SSakari Ailus 
399a6b396f4SSakari Ailus 	if (__entry != endp)
400a6b396f4SSakari Ailus 		return -EPROTO;
401a6b396f4SSakari Ailus 
402a6b396f4SSakari Ailus 	return 0;
403a6b396f4SSakari Ailus }
404a6b396f4SSakari Ailus 
ccs_data_parse_pdaf_readout(struct bin_container * bin,struct ccs_pdaf_readout ** pdaf_readout,const void * payload,const void * endp,struct device * dev)405a6b396f4SSakari Ailus static int ccs_data_parse_pdaf_readout(struct bin_container *bin,
406a6b396f4SSakari Ailus 				       struct ccs_pdaf_readout **pdaf_readout,
407a6b396f4SSakari Ailus 				       const void *payload,
408a6b396f4SSakari Ailus 				       const void *endp, struct device *dev)
409a6b396f4SSakari Ailus {
410a6b396f4SSakari Ailus 	const struct __ccs_data_block_pdaf_readout *__pdaf = payload;
411a6b396f4SSakari Ailus 
412a6b396f4SSakari Ailus 	if (!is_contained(__pdaf, endp))
413a6b396f4SSakari Ailus 		return -ENODATA;
414a6b396f4SSakari Ailus 
415a6b396f4SSakari Ailus 	if (!bin->base) {
416a6b396f4SSakari Ailus 		bin_reserve(bin, sizeof(**pdaf_readout));
417a6b396f4SSakari Ailus 	} else {
418a6b396f4SSakari Ailus 		*pdaf_readout = bin_alloc(bin, sizeof(**pdaf_readout));
419a6b396f4SSakari Ailus 		if (!*pdaf_readout)
420a6b396f4SSakari Ailus 			return -ENOMEM;
421a6b396f4SSakari Ailus 
422a6b396f4SSakari Ailus 		(*pdaf_readout)->pdaf_readout_info_order =
423a6b396f4SSakari Ailus 			__pdaf->pdaf_readout_info_order;
424a6b396f4SSakari Ailus 	}
425a6b396f4SSakari Ailus 
426a6b396f4SSakari Ailus 	return ccs_data_parse_ffd(bin, !bin->base ? NULL : &(*pdaf_readout)->ffd,
427a6b396f4SSakari Ailus 				  __pdaf + 1, endp, dev);
428a6b396f4SSakari Ailus }
429a6b396f4SSakari Ailus 
ccs_data_parse_rules(struct bin_container * bin,struct ccs_rule ** __rules,size_t * __num_rules,const void * payload,const void * endp,struct device * dev)430a6b396f4SSakari Ailus static int ccs_data_parse_rules(struct bin_container *bin,
431a6b396f4SSakari Ailus 				struct ccs_rule **__rules,
432a6b396f4SSakari Ailus 				size_t *__num_rules, const void *payload,
433a6b396f4SSakari Ailus 				const void *endp, struct device *dev)
434a6b396f4SSakari Ailus {
435ae56e038SSakari Ailus 	struct ccs_rule *rules_base = NULL, *rules = NULL, *next_rule = NULL;
436a6b396f4SSakari Ailus 	size_t num_rules = 0;
437a6b396f4SSakari Ailus 	const void *__next_rule = payload;
438a6b396f4SSakari Ailus 	int rval;
439a6b396f4SSakari Ailus 
440a6b396f4SSakari Ailus 	if (bin->base) {
441a6b396f4SSakari Ailus 		rules_base = next_rule =
442a6b396f4SSakari Ailus 			bin_alloc(bin, sizeof(*rules) * *__num_rules);
443a6b396f4SSakari Ailus 		if (!rules_base)
444a6b396f4SSakari Ailus 			return -ENOMEM;
445a6b396f4SSakari Ailus 	}
446a6b396f4SSakari Ailus 
447a6b396f4SSakari Ailus 	while (__next_rule < endp) {
448a6b396f4SSakari Ailus 		size_t rule_hlen, rule_plen, rule_plen2;
449a6b396f4SSakari Ailus 		const u8 *__rule_type;
450a6b396f4SSakari Ailus 		const void *rule_payload;
451a6b396f4SSakari Ailus 
452a6b396f4SSakari Ailus 		/* Size of a single rule */
453a6b396f4SSakari Ailus 		rval = ccs_data_parse_length_specifier(__next_rule, &rule_hlen,
454a6b396f4SSakari Ailus 						       &rule_plen, endp);
455a6b396f4SSakari Ailus 
456a6b396f4SSakari Ailus 		if (rval < 0)
457a6b396f4SSakari Ailus 			return rval;
458a6b396f4SSakari Ailus 
459a6b396f4SSakari Ailus 		__rule_type = __next_rule + rule_hlen;
460a6b396f4SSakari Ailus 
461a6b396f4SSakari Ailus 		if (!is_contained(__rule_type, endp))
462a6b396f4SSakari Ailus 			return -ENODATA;
463a6b396f4SSakari Ailus 
464a6b396f4SSakari Ailus 		rule_payload = __rule_type + 1;
465a6b396f4SSakari Ailus 		rule_plen2 = rule_plen - sizeof(*__rule_type);
466a6b396f4SSakari Ailus 
467*607bcc42SSakari Ailus 		if (*__rule_type == CCS_DATA_BLOCK_RULE_ID_IF) {
468a6b396f4SSakari Ailus 			const struct __ccs_data_block_rule_if *__if_rules =
469a6b396f4SSakari Ailus 				rule_payload;
470a6b396f4SSakari Ailus 			const size_t __num_if_rules =
471a6b396f4SSakari Ailus 				rule_plen2 / sizeof(*__if_rules);
472a6b396f4SSakari Ailus 			struct ccs_if_rule *if_rule;
473a6b396f4SSakari Ailus 
474a6b396f4SSakari Ailus 			if (!has_headroom(__if_rules,
475a6b396f4SSakari Ailus 					  sizeof(*__if_rules) * __num_if_rules,
476a6b396f4SSakari Ailus 					  rule_payload + rule_plen2))
477a6b396f4SSakari Ailus 				return -ENODATA;
478a6b396f4SSakari Ailus 
479a6b396f4SSakari Ailus 			/* Also check there is no extra data */
480a6b396f4SSakari Ailus 			if (__if_rules + __num_if_rules !=
481a6b396f4SSakari Ailus 			    rule_payload + rule_plen2)
482a6b396f4SSakari Ailus 				return -EINVAL;
483a6b396f4SSakari Ailus 
484a6b396f4SSakari Ailus 			if (!bin->base) {
485a6b396f4SSakari Ailus 				bin_reserve(bin,
486a6b396f4SSakari Ailus 					    sizeof(*if_rule) *
487a6b396f4SSakari Ailus 					    __num_if_rules);
488a6b396f4SSakari Ailus 				num_rules++;
489a6b396f4SSakari Ailus 			} else {
490a6b396f4SSakari Ailus 				unsigned int i;
491a6b396f4SSakari Ailus 
492ae56e038SSakari Ailus 				if (!next_rule)
493ae56e038SSakari Ailus 					return -EIO;
494ae56e038SSakari Ailus 
495a6b396f4SSakari Ailus 				rules = next_rule;
496a6b396f4SSakari Ailus 				next_rule++;
497a6b396f4SSakari Ailus 
498a6b396f4SSakari Ailus 				if_rule = bin_alloc(bin,
499a6b396f4SSakari Ailus 						    sizeof(*if_rule) *
500a6b396f4SSakari Ailus 						    __num_if_rules);
501a6b396f4SSakari Ailus 				if (!if_rule)
502a6b396f4SSakari Ailus 					return -ENOMEM;
503a6b396f4SSakari Ailus 
504a6b396f4SSakari Ailus 				for (i = 0; i < __num_if_rules; i++) {
505a6b396f4SSakari Ailus 					if_rule[i].addr =
506a6b396f4SSakari Ailus 						((u16)__if_rules[i].addr[0]
507a6b396f4SSakari Ailus 						 << 8) +
508a6b396f4SSakari Ailus 						__if_rules[i].addr[1];
509a6b396f4SSakari Ailus 					if_rule[i].value = __if_rules[i].value;
510a6b396f4SSakari Ailus 					if_rule[i].mask = __if_rules[i].mask;
511a6b396f4SSakari Ailus 				}
512a6b396f4SSakari Ailus 
513a6b396f4SSakari Ailus 				rules->if_rules = if_rule;
514a6b396f4SSakari Ailus 				rules->num_if_rules = __num_if_rules;
515a6b396f4SSakari Ailus 			}
516*607bcc42SSakari Ailus 		} else {
517*607bcc42SSakari Ailus 			/* Check there was an if rule before any other rules */
518*607bcc42SSakari Ailus 			if (bin->base && !rules)
519*607bcc42SSakari Ailus 				return -EINVAL;
520*607bcc42SSakari Ailus 
521*607bcc42SSakari Ailus 			switch (*__rule_type) {
522a6b396f4SSakari Ailus 			case CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS:
523*607bcc42SSakari Ailus 				rval = ccs_data_parse_reg_rules(bin,
524*607bcc42SSakari Ailus 								rules ?
525*607bcc42SSakari Ailus 								&rules->read_only_regs : NULL,
526*607bcc42SSakari Ailus 								rules ?
527*607bcc42SSakari Ailus 								&rules->num_read_only_regs : NULL,
528a6b396f4SSakari Ailus 								rule_payload,
529a6b396f4SSakari Ailus 								rule_payload + rule_plen2,
530a6b396f4SSakari Ailus 								dev);
531a6b396f4SSakari Ailus 				if (rval)
532a6b396f4SSakari Ailus 					return rval;
533a6b396f4SSakari Ailus 				break;
534a6b396f4SSakari Ailus 			case CCS_DATA_BLOCK_RULE_ID_FFD:
535*607bcc42SSakari Ailus 				rval = ccs_data_parse_ffd(bin, rules ?
536*607bcc42SSakari Ailus 							  &rules->frame_format : NULL,
537a6b396f4SSakari Ailus 							  rule_payload,
538a6b396f4SSakari Ailus 							  rule_payload + rule_plen2,
539a6b396f4SSakari Ailus 							  dev);
540a6b396f4SSakari Ailus 				if (rval)
541a6b396f4SSakari Ailus 					return rval;
542a6b396f4SSakari Ailus 				break;
543a6b396f4SSakari Ailus 			case CCS_DATA_BLOCK_RULE_ID_MSR:
544a6b396f4SSakari Ailus 				rval = ccs_data_parse_reg_rules(bin,
545*607bcc42SSakari Ailus 								rules ?
546*607bcc42SSakari Ailus 								&rules->manufacturer_regs : NULL,
547*607bcc42SSakari Ailus 								rules ?
548*607bcc42SSakari Ailus 								&rules->num_manufacturer_regs : NULL,
549a6b396f4SSakari Ailus 								rule_payload,
550a6b396f4SSakari Ailus 								rule_payload + rule_plen2,
551a6b396f4SSakari Ailus 								dev);
552a6b396f4SSakari Ailus 				if (rval)
553a6b396f4SSakari Ailus 					return rval;
554a6b396f4SSakari Ailus 				break;
555a6b396f4SSakari Ailus 			case CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT:
556a6b396f4SSakari Ailus 				rval = ccs_data_parse_pdaf_readout(bin,
557*607bcc42SSakari Ailus 								   rules ?
558*607bcc42SSakari Ailus 								   &rules->pdaf_readout : NULL,
559a6b396f4SSakari Ailus 								   rule_payload,
560a6b396f4SSakari Ailus 								   rule_payload + rule_plen2,
561a6b396f4SSakari Ailus 								   dev);
562a6b396f4SSakari Ailus 				if (rval)
563a6b396f4SSakari Ailus 					return rval;
564a6b396f4SSakari Ailus 				break;
565a6b396f4SSakari Ailus 			default:
566a6b396f4SSakari Ailus 				dev_dbg(dev,
567a6b396f4SSakari Ailus 					"Don't know how to handle rule type %u!\n",
568a6b396f4SSakari Ailus 					*__rule_type);
569a6b396f4SSakari Ailus 				return -EINVAL;
570a6b396f4SSakari Ailus 			}
571*607bcc42SSakari Ailus 		}
572a6b396f4SSakari Ailus 		__next_rule = __next_rule + rule_hlen + rule_plen;
573a6b396f4SSakari Ailus 	}
574a6b396f4SSakari Ailus 
575a6b396f4SSakari Ailus 	if (!bin->base) {
576a6b396f4SSakari Ailus 		bin_reserve(bin, sizeof(*rules) * num_rules);
577a6b396f4SSakari Ailus 		*__num_rules = num_rules;
578a6b396f4SSakari Ailus 	} else {
579ae56e038SSakari Ailus 		if (!rules_base)
580ae56e038SSakari Ailus 			return -EIO;
581ae56e038SSakari Ailus 
582a6b396f4SSakari Ailus 		*__rules = rules_base;
583a6b396f4SSakari Ailus 	}
584a6b396f4SSakari Ailus 
585a6b396f4SSakari Ailus 	return 0;
586a6b396f4SSakari Ailus }
587a6b396f4SSakari Ailus 
ccs_data_parse_pdaf(struct bin_container * bin,struct ccs_pdaf_pix_loc ** pdaf,const void * payload,const void * endp,struct device * dev)588a6b396f4SSakari Ailus static int ccs_data_parse_pdaf(struct bin_container *bin, struct ccs_pdaf_pix_loc **pdaf,
589a6b396f4SSakari Ailus 			       const void *payload, const void *endp,
590a6b396f4SSakari Ailus 			       struct device *dev)
591a6b396f4SSakari Ailus {
592a6b396f4SSakari Ailus 	const struct __ccs_data_block_pdaf_pix_loc *__pdaf = payload;
593a6b396f4SSakari Ailus 	const struct __ccs_data_block_pdaf_pix_loc_block_desc_group *__bdesc_group;
594a6b396f4SSakari Ailus 	const struct __ccs_data_block_pdaf_pix_loc_pixel_desc *__pixel_desc;
595a6b396f4SSakari Ailus 	unsigned int i;
596a6b396f4SSakari Ailus 	u16 num_block_desc_groups;
597a6b396f4SSakari Ailus 	u8 max_block_type_id = 0;
598a6b396f4SSakari Ailus 	const u8 *__num_pixel_descs;
599a6b396f4SSakari Ailus 
600a6b396f4SSakari Ailus 	if (!is_contained(__pdaf, endp))
601a6b396f4SSakari Ailus 		return -ENODATA;
602a6b396f4SSakari Ailus 
603a6b396f4SSakari Ailus 	if (bin->base) {
604a6b396f4SSakari Ailus 		*pdaf = bin_alloc(bin, sizeof(**pdaf));
605a6b396f4SSakari Ailus 		if (!*pdaf)
606a6b396f4SSakari Ailus 			return -ENOMEM;
607a6b396f4SSakari Ailus 	} else {
608a6b396f4SSakari Ailus 		bin_reserve(bin, sizeof(**pdaf));
609a6b396f4SSakari Ailus 	}
610a6b396f4SSakari Ailus 
611a6b396f4SSakari Ailus 	num_block_desc_groups =
612a6b396f4SSakari Ailus 		((u16)__pdaf->num_block_desc_groups[0] << 8) +
613a6b396f4SSakari Ailus 		__pdaf->num_block_desc_groups[1];
614a6b396f4SSakari Ailus 
615a6b396f4SSakari Ailus 	if (bin->base) {
616a6b396f4SSakari Ailus 		(*pdaf)->main_offset_x =
617a6b396f4SSakari Ailus 			((u16)__pdaf->main_offset_x[0] << 8) +
618a6b396f4SSakari Ailus 			__pdaf->main_offset_x[1];
619a6b396f4SSakari Ailus 		(*pdaf)->main_offset_y =
620a6b396f4SSakari Ailus 			((u16)__pdaf->main_offset_y[0] << 8) +
621a6b396f4SSakari Ailus 			__pdaf->main_offset_y[1];
622a6b396f4SSakari Ailus 		(*pdaf)->global_pdaf_type = __pdaf->global_pdaf_type;
623a6b396f4SSakari Ailus 		(*pdaf)->block_width = __pdaf->block_width;
624a6b396f4SSakari Ailus 		(*pdaf)->block_height = __pdaf->block_height;
625a6b396f4SSakari Ailus 		(*pdaf)->num_block_desc_groups = num_block_desc_groups;
626a6b396f4SSakari Ailus 	}
627a6b396f4SSakari Ailus 
628a6b396f4SSakari Ailus 	__bdesc_group = (const void *)(__pdaf + 1);
629a6b396f4SSakari Ailus 
630a6b396f4SSakari Ailus 	if (bin->base) {
631a6b396f4SSakari Ailus 		(*pdaf)->block_desc_groups =
632a6b396f4SSakari Ailus 			bin_alloc(bin,
633a6b396f4SSakari Ailus 				  sizeof(struct ccs_pdaf_pix_loc_block_desc_group) *
634a6b396f4SSakari Ailus 				  num_block_desc_groups);
635a6b396f4SSakari Ailus 		if (!(*pdaf)->block_desc_groups)
636a6b396f4SSakari Ailus 			return -ENOMEM;
637a6b396f4SSakari Ailus 	} else {
638a6b396f4SSakari Ailus 		bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_block_desc_group) *
639a6b396f4SSakari Ailus 			    num_block_desc_groups);
640a6b396f4SSakari Ailus 	}
641a6b396f4SSakari Ailus 
642a6b396f4SSakari Ailus 	for (i = 0; i < num_block_desc_groups; i++) {
643a6b396f4SSakari Ailus 		const struct __ccs_data_block_pdaf_pix_loc_block_desc *__bdesc;
644a6b396f4SSakari Ailus 		u16 num_block_descs;
645a6b396f4SSakari Ailus 		unsigned int j;
646a6b396f4SSakari Ailus 
647a6b396f4SSakari Ailus 		if (!is_contained(__bdesc_group, endp))
648a6b396f4SSakari Ailus 			return -ENODATA;
649a6b396f4SSakari Ailus 
650a6b396f4SSakari Ailus 		num_block_descs =
651a6b396f4SSakari Ailus 			((u16)__bdesc_group->num_block_descs[0] << 8) +
652a6b396f4SSakari Ailus 			__bdesc_group->num_block_descs[1];
653a6b396f4SSakari Ailus 
654a6b396f4SSakari Ailus 		if (bin->base) {
655a6b396f4SSakari Ailus 			(*pdaf)->block_desc_groups[i].repeat_y =
656a6b396f4SSakari Ailus 				__bdesc_group->repeat_y;
657a6b396f4SSakari Ailus 			(*pdaf)->block_desc_groups[i].num_block_descs =
658a6b396f4SSakari Ailus 				num_block_descs;
659a6b396f4SSakari Ailus 		}
660a6b396f4SSakari Ailus 
661a6b396f4SSakari Ailus 		__bdesc = (const void *)(__bdesc_group + 1);
662a6b396f4SSakari Ailus 
663a6b396f4SSakari Ailus 		if (bin->base) {
664a6b396f4SSakari Ailus 			(*pdaf)->block_desc_groups[i].block_descs =
665a6b396f4SSakari Ailus 				bin_alloc(bin,
666a6b396f4SSakari Ailus 					  sizeof(struct ccs_pdaf_pix_loc_block_desc) *
667a6b396f4SSakari Ailus 					  num_block_descs);
668a6b396f4SSakari Ailus 			if (!(*pdaf)->block_desc_groups[i].block_descs)
669a6b396f4SSakari Ailus 				return -ENOMEM;
670a6b396f4SSakari Ailus 		} else {
671a6b396f4SSakari Ailus 			bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_block_desc) *
672a6b396f4SSakari Ailus 				    num_block_descs);
673a6b396f4SSakari Ailus 		}
674a6b396f4SSakari Ailus 
675a6b396f4SSakari Ailus 		for (j = 0; j < num_block_descs; j++, __bdesc++) {
676a6b396f4SSakari Ailus 			struct ccs_pdaf_pix_loc_block_desc *bdesc;
677a6b396f4SSakari Ailus 
678a6b396f4SSakari Ailus 			if (!is_contained(__bdesc, endp))
679a6b396f4SSakari Ailus 				return -ENODATA;
680a6b396f4SSakari Ailus 
681a6b396f4SSakari Ailus 			if (max_block_type_id <= __bdesc->block_type_id)
682a6b396f4SSakari Ailus 				max_block_type_id = __bdesc->block_type_id + 1;
683a6b396f4SSakari Ailus 
684a6b396f4SSakari Ailus 			if (!bin->base)
685a6b396f4SSakari Ailus 				continue;
686a6b396f4SSakari Ailus 
687a6b396f4SSakari Ailus 			bdesc = &(*pdaf)->block_desc_groups[i].block_descs[j];
688a6b396f4SSakari Ailus 
689a6b396f4SSakari Ailus 			bdesc->repeat_x = ((u16)__bdesc->repeat_x[0] << 8)
690a6b396f4SSakari Ailus 				+ __bdesc->repeat_x[1];
691a6b396f4SSakari Ailus 
692a6b396f4SSakari Ailus 			if (__bdesc->block_type_id >= num_block_descs)
693a6b396f4SSakari Ailus 				return -EINVAL;
694a6b396f4SSakari Ailus 
695a6b396f4SSakari Ailus 			bdesc->block_type_id = __bdesc->block_type_id;
696a6b396f4SSakari Ailus 		}
697a6b396f4SSakari Ailus 
698a6b396f4SSakari Ailus 		__bdesc_group = (const void *)__bdesc;
699a6b396f4SSakari Ailus 	}
700a6b396f4SSakari Ailus 
701a6b396f4SSakari Ailus 	__num_pixel_descs = (const void *)__bdesc_group;
702a6b396f4SSakari Ailus 
703a6b396f4SSakari Ailus 	if (bin->base) {
704a6b396f4SSakari Ailus 		(*pdaf)->pixel_desc_groups =
705a6b396f4SSakari Ailus 			bin_alloc(bin,
706a6b396f4SSakari Ailus 				  sizeof(struct ccs_pdaf_pix_loc_pixel_desc_group) *
707a6b396f4SSakari Ailus 				  max_block_type_id);
708a6b396f4SSakari Ailus 		if (!(*pdaf)->pixel_desc_groups)
709a6b396f4SSakari Ailus 			return -ENOMEM;
710a6b396f4SSakari Ailus 		(*pdaf)->num_pixel_desc_grups = max_block_type_id;
711a6b396f4SSakari Ailus 	} else {
712a6b396f4SSakari Ailus 		bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_pixel_desc_group) *
713a6b396f4SSakari Ailus 			    max_block_type_id);
714a6b396f4SSakari Ailus 	}
715a6b396f4SSakari Ailus 
716a6b396f4SSakari Ailus 	for (i = 0; i < max_block_type_id; i++) {
717ae56e038SSakari Ailus 		struct ccs_pdaf_pix_loc_pixel_desc_group *pdgroup = NULL;
718a6b396f4SSakari Ailus 		unsigned int j;
719a6b396f4SSakari Ailus 
720a6b396f4SSakari Ailus 		if (!is_contained(__num_pixel_descs, endp))
721a6b396f4SSakari Ailus 			return -ENODATA;
722a6b396f4SSakari Ailus 
723a6b396f4SSakari Ailus 		if (bin->base) {
724a6b396f4SSakari Ailus 			pdgroup = &(*pdaf)->pixel_desc_groups[i];
725a6b396f4SSakari Ailus 			pdgroup->descs =
726a6b396f4SSakari Ailus 				bin_alloc(bin,
727a6b396f4SSakari Ailus 					  sizeof(struct ccs_pdaf_pix_loc_pixel_desc) *
728a6b396f4SSakari Ailus 					  *__num_pixel_descs);
729a6b396f4SSakari Ailus 			if (!pdgroup->descs)
730a6b396f4SSakari Ailus 				return -ENOMEM;
731a6b396f4SSakari Ailus 			pdgroup->num_descs = *__num_pixel_descs;
732a6b396f4SSakari Ailus 		} else {
733a6b396f4SSakari Ailus 			bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_pixel_desc) *
734a6b396f4SSakari Ailus 				    *__num_pixel_descs);
735a6b396f4SSakari Ailus 		}
736a6b396f4SSakari Ailus 
737a6b396f4SSakari Ailus 		__pixel_desc = (const void *)(__num_pixel_descs + 1);
738a6b396f4SSakari Ailus 
739a6b396f4SSakari Ailus 		for (j = 0; j < *__num_pixel_descs; j++, __pixel_desc++) {
740a6b396f4SSakari Ailus 			struct ccs_pdaf_pix_loc_pixel_desc *pdesc;
741a6b396f4SSakari Ailus 
742a6b396f4SSakari Ailus 			if (!is_contained(__pixel_desc, endp))
743a6b396f4SSakari Ailus 				return -ENODATA;
744a6b396f4SSakari Ailus 
745a6b396f4SSakari Ailus 			if (!bin->base)
746a6b396f4SSakari Ailus 				continue;
747a6b396f4SSakari Ailus 
748ae56e038SSakari Ailus 			if (!pdgroup)
749ae56e038SSakari Ailus 				return -EIO;
750ae56e038SSakari Ailus 
751a6b396f4SSakari Ailus 			pdesc = &pdgroup->descs[j];
752a6b396f4SSakari Ailus 			pdesc->pixel_type = __pixel_desc->pixel_type;
753a6b396f4SSakari Ailus 			pdesc->small_offset_x = __pixel_desc->small_offset_x;
754a6b396f4SSakari Ailus 			pdesc->small_offset_y = __pixel_desc->small_offset_y;
755a6b396f4SSakari Ailus 		}
756a6b396f4SSakari Ailus 
757a6b396f4SSakari Ailus 		__num_pixel_descs = (const void *)(__pixel_desc + 1);
758a6b396f4SSakari Ailus 	}
759a6b396f4SSakari Ailus 
760a6b396f4SSakari Ailus 	return 0;
761a6b396f4SSakari Ailus }
762a6b396f4SSakari Ailus 
ccs_data_parse_license(struct bin_container * bin,char ** __license,size_t * __license_length,const void * payload,const void * endp)763a6b396f4SSakari Ailus static int ccs_data_parse_license(struct bin_container *bin,
764a6b396f4SSakari Ailus 				  char **__license,
765a6b396f4SSakari Ailus 				  size_t *__license_length,
766a6b396f4SSakari Ailus 				  const void *payload, const void *endp)
767a6b396f4SSakari Ailus {
768a6b396f4SSakari Ailus 	size_t size = endp - payload;
769a6b396f4SSakari Ailus 	char *license;
770a6b396f4SSakari Ailus 
771a6b396f4SSakari Ailus 	if (!bin->base) {
772a6b396f4SSakari Ailus 		bin_reserve(bin, size);
773a6b396f4SSakari Ailus 		return 0;
774a6b396f4SSakari Ailus 	}
775a6b396f4SSakari Ailus 
776a6b396f4SSakari Ailus 	license = bin_alloc(bin, size);
777a6b396f4SSakari Ailus 	if (!license)
778a6b396f4SSakari Ailus 		return -ENOMEM;
779a6b396f4SSakari Ailus 
780a6b396f4SSakari Ailus 	memcpy(license, payload, size);
781a6b396f4SSakari Ailus 
782a6b396f4SSakari Ailus 	*__license = license;
783a6b396f4SSakari Ailus 	*__license_length = size;
784a6b396f4SSakari Ailus 
785a6b396f4SSakari Ailus 	return 0;
786a6b396f4SSakari Ailus }
787a6b396f4SSakari Ailus 
ccs_data_parse_end(bool * end,const void * payload,const void * endp,struct device * dev)788a6b396f4SSakari Ailus static int ccs_data_parse_end(bool *end, const void *payload, const void *endp,
789a6b396f4SSakari Ailus 			      struct device *dev)
790a6b396f4SSakari Ailus {
791a6b396f4SSakari Ailus 	const struct __ccs_data_block_end *__end = payload;
792a6b396f4SSakari Ailus 
793a6b396f4SSakari Ailus 	if (__end + 1 != endp) {
794a6b396f4SSakari Ailus 		dev_dbg(dev, "Invalid end block length %u\n",
795a6b396f4SSakari Ailus 			(unsigned int)(endp - payload));
796a6b396f4SSakari Ailus 		return -ENODATA;
797a6b396f4SSakari Ailus 	}
798a6b396f4SSakari Ailus 
799a6b396f4SSakari Ailus 	*end = true;
800a6b396f4SSakari Ailus 
801a6b396f4SSakari Ailus 	return 0;
802a6b396f4SSakari Ailus }
803a6b396f4SSakari Ailus 
__ccs_data_parse(struct bin_container * bin,struct ccs_data_container * ccsdata,const void * data,size_t len,struct device * dev,bool verbose)804a6b396f4SSakari Ailus static int __ccs_data_parse(struct bin_container *bin,
805a6b396f4SSakari Ailus 			    struct ccs_data_container *ccsdata,
806a6b396f4SSakari Ailus 			    const void *data, size_t len, struct device *dev,
807a6b396f4SSakari Ailus 			    bool verbose)
808a6b396f4SSakari Ailus {
809a6b396f4SSakari Ailus 	const struct __ccs_data_block *block = data;
810a6b396f4SSakari Ailus 	const struct __ccs_data_block *endp = data + len;
811a6b396f4SSakari Ailus 	unsigned int version;
812a6b396f4SSakari Ailus 	bool is_first = true;
813a6b396f4SSakari Ailus 	int rval;
814a6b396f4SSakari Ailus 
815a6b396f4SSakari Ailus 	version = ccs_data_parse_format_version(block);
816a6b396f4SSakari Ailus 	if (version != CCS_STATIC_DATA_VERSION) {
817a6b396f4SSakari Ailus 		dev_dbg(dev, "Don't know how to handle version %u\n", version);
818a6b396f4SSakari Ailus 		return -EINVAL;
819a6b396f4SSakari Ailus 	}
820a6b396f4SSakari Ailus 
821a6b396f4SSakari Ailus 	if (verbose)
822a6b396f4SSakari Ailus 		dev_dbg(dev, "Parsing CCS static data version %u\n", version);
823a6b396f4SSakari Ailus 
824a6b396f4SSakari Ailus 	if (!bin->base)
825a6b396f4SSakari Ailus 		*ccsdata = (struct ccs_data_container){ 0 };
826a6b396f4SSakari Ailus 
827a6b396f4SSakari Ailus 	while (block < endp) {
828a6b396f4SSakari Ailus 		const struct __ccs_data_block *next_block;
829a6b396f4SSakari Ailus 		unsigned int block_id;
830a6b396f4SSakari Ailus 		const void *payload;
831a6b396f4SSakari Ailus 
832a6b396f4SSakari Ailus 		rval = ccs_data_block_parse_header(block, is_first, &block_id,
833a6b396f4SSakari Ailus 						   &payload, &next_block, endp,
834a6b396f4SSakari Ailus 						   dev,
835a6b396f4SSakari Ailus 						   bin->base ? false : verbose);
836a6b396f4SSakari Ailus 
837a6b396f4SSakari Ailus 		if (rval < 0)
838a6b396f4SSakari Ailus 			return rval;
839a6b396f4SSakari Ailus 
840a6b396f4SSakari Ailus 		switch (block_id) {
841a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_DUMMY:
842a6b396f4SSakari Ailus 			break;
843a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_DATA_VERSION:
844a6b396f4SSakari Ailus 			rval = ccs_data_parse_version(bin, ccsdata, payload,
845a6b396f4SSakari Ailus 						      next_block);
846a6b396f4SSakari Ailus 			if (rval < 0)
847a6b396f4SSakari Ailus 				return rval;
848a6b396f4SSakari Ailus 			break;
849a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_SENSOR_READ_ONLY_REGS:
850a6b396f4SSakari Ailus 			rval = ccs_data_parse_regs(
851a6b396f4SSakari Ailus 				bin, &ccsdata->sensor_read_only_regs,
852a6b396f4SSakari Ailus 				&ccsdata->num_sensor_read_only_regs, payload,
853a6b396f4SSakari Ailus 				next_block, dev);
854a6b396f4SSakari Ailus 			if (rval < 0)
855a6b396f4SSakari Ailus 				return rval;
856a6b396f4SSakari Ailus 			break;
857a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_SENSOR_MANUFACTURER_REGS:
858a6b396f4SSakari Ailus 			rval = ccs_data_parse_regs(
859a6b396f4SSakari Ailus 				bin, &ccsdata->sensor_manufacturer_regs,
860a6b396f4SSakari Ailus 				&ccsdata->num_sensor_manufacturer_regs, payload,
861a6b396f4SSakari Ailus 				next_block, dev);
862a6b396f4SSakari Ailus 			if (rval < 0)
863a6b396f4SSakari Ailus 				return rval;
864a6b396f4SSakari Ailus 			break;
865a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_MODULE_READ_ONLY_REGS:
866a6b396f4SSakari Ailus 			rval = ccs_data_parse_regs(
867a6b396f4SSakari Ailus 				bin, &ccsdata->module_read_only_regs,
868a6b396f4SSakari Ailus 				&ccsdata->num_module_read_only_regs, payload,
869a6b396f4SSakari Ailus 				next_block, dev);
870a6b396f4SSakari Ailus 			if (rval < 0)
871a6b396f4SSakari Ailus 				return rval;
872a6b396f4SSakari Ailus 			break;
873a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_MODULE_MANUFACTURER_REGS:
874a6b396f4SSakari Ailus 			rval = ccs_data_parse_regs(
875a6b396f4SSakari Ailus 				bin, &ccsdata->module_manufacturer_regs,
876a6b396f4SSakari Ailus 				&ccsdata->num_module_manufacturer_regs, payload,
877a6b396f4SSakari Ailus 				next_block, dev);
878a6b396f4SSakari Ailus 			if (rval < 0)
879a6b396f4SSakari Ailus 				return rval;
880a6b396f4SSakari Ailus 			break;
881a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_SENSOR_PDAF_PIXEL_LOCATION:
882a6b396f4SSakari Ailus 			rval = ccs_data_parse_pdaf(bin, &ccsdata->sensor_pdaf,
883a6b396f4SSakari Ailus 						   payload, next_block, dev);
884a6b396f4SSakari Ailus 			if (rval < 0)
885a6b396f4SSakari Ailus 				return rval;
886a6b396f4SSakari Ailus 			break;
887a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_MODULE_PDAF_PIXEL_LOCATION:
888a6b396f4SSakari Ailus 			rval = ccs_data_parse_pdaf(bin, &ccsdata->module_pdaf,
889a6b396f4SSakari Ailus 						   payload, next_block, dev);
890a6b396f4SSakari Ailus 			if (rval < 0)
891a6b396f4SSakari Ailus 				return rval;
892a6b396f4SSakari Ailus 			break;
893a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_SENSOR_RULE_BASED_BLOCK:
894a6b396f4SSakari Ailus 			rval = ccs_data_parse_rules(
895a6b396f4SSakari Ailus 				bin, &ccsdata->sensor_rules,
896a6b396f4SSakari Ailus 				&ccsdata->num_sensor_rules, payload, next_block,
897a6b396f4SSakari Ailus 				dev);
898a6b396f4SSakari Ailus 			if (rval < 0)
899a6b396f4SSakari Ailus 				return rval;
900a6b396f4SSakari Ailus 			break;
901a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_MODULE_RULE_BASED_BLOCK:
902a6b396f4SSakari Ailus 			rval = ccs_data_parse_rules(
903a6b396f4SSakari Ailus 				bin, &ccsdata->module_rules,
904a6b396f4SSakari Ailus 				&ccsdata->num_module_rules, payload, next_block,
905a6b396f4SSakari Ailus 				dev);
906a6b396f4SSakari Ailus 			if (rval < 0)
907a6b396f4SSakari Ailus 				return rval;
908a6b396f4SSakari Ailus 			break;
909a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_LICENSE:
910a6b396f4SSakari Ailus 			rval = ccs_data_parse_license(bin, &ccsdata->license,
911a6b396f4SSakari Ailus 						      &ccsdata->license_length,
912a6b396f4SSakari Ailus 						      payload, next_block);
913a6b396f4SSakari Ailus 			if (rval < 0)
914a6b396f4SSakari Ailus 				return rval;
915a6b396f4SSakari Ailus 			break;
916a6b396f4SSakari Ailus 		case CCS_DATA_BLOCK_ID_END:
917a6b396f4SSakari Ailus 			rval = ccs_data_parse_end(&ccsdata->end, payload,
918a6b396f4SSakari Ailus 						  next_block, dev);
919a6b396f4SSakari Ailus 			if (rval < 0)
920a6b396f4SSakari Ailus 				return rval;
921a6b396f4SSakari Ailus 			break;
922a6b396f4SSakari Ailus 		default:
923a6b396f4SSakari Ailus 			dev_dbg(dev, "WARNING: not handling block ID 0x%2.2x\n",
924a6b396f4SSakari Ailus 				block_id);
925a6b396f4SSakari Ailus 		}
926a6b396f4SSakari Ailus 
927a6b396f4SSakari Ailus 		block = next_block;
928a6b396f4SSakari Ailus 		is_first = false;
929a6b396f4SSakari Ailus 	}
930a6b396f4SSakari Ailus 
931a6b396f4SSakari Ailus 	return 0;
932a6b396f4SSakari Ailus }
933a6b396f4SSakari Ailus 
934a6b396f4SSakari Ailus /**
935a6b396f4SSakari Ailus  * ccs_data_parse - Parse a CCS static data file into a usable in-memory
936a6b396f4SSakari Ailus  *		    data structure
937a6b396f4SSakari Ailus  * @ccsdata:	CCS static data in-memory data structure
938a6b396f4SSakari Ailus  * @data:	CCS static data binary
939a6b396f4SSakari Ailus  * @len:	Length of @data
940a6b396f4SSakari Ailus  * @dev:	Device the data is related to (used for printing debug messages)
941a6b396f4SSakari Ailus  * @verbose:	Whether to be verbose or not
942a6b396f4SSakari Ailus  */
ccs_data_parse(struct ccs_data_container * ccsdata,const void * data,size_t len,struct device * dev,bool verbose)943a6b396f4SSakari Ailus int ccs_data_parse(struct ccs_data_container *ccsdata, const void *data,
944a6b396f4SSakari Ailus 		   size_t len, struct device *dev, bool verbose)
945a6b396f4SSakari Ailus {
946a6b396f4SSakari Ailus 	struct bin_container bin = { 0 };
947a6b396f4SSakari Ailus 	int rval;
948a6b396f4SSakari Ailus 
949a6b396f4SSakari Ailus 	rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, verbose);
950a6b396f4SSakari Ailus 	if (rval)
951a6b396f4SSakari Ailus 		return rval;
952a6b396f4SSakari Ailus 
953a6b396f4SSakari Ailus 	rval = bin_backing_alloc(&bin);
954a6b396f4SSakari Ailus 	if (rval)
955a6b396f4SSakari Ailus 		return rval;
956a6b396f4SSakari Ailus 
957a6b396f4SSakari Ailus 	rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, false);
958a6b396f4SSakari Ailus 	if (rval)
959a6b396f4SSakari Ailus 		goto out_free;
960a6b396f4SSakari Ailus 
961a6b396f4SSakari Ailus 	if (verbose && ccsdata->version)
962a6b396f4SSakari Ailus 		print_ccs_data_version(dev, ccsdata->version);
963a6b396f4SSakari Ailus 
964a6b396f4SSakari Ailus 	if (bin.now != bin.end) {
965a6b396f4SSakari Ailus 		rval = -EPROTO;
966a6b396f4SSakari Ailus 		dev_dbg(dev, "parsing mismatch; base %p; now %p; end %p\n",
967a6b396f4SSakari Ailus 			bin.base, bin.now, bin.end);
968a6b396f4SSakari Ailus 		goto out_free;
969a6b396f4SSakari Ailus 	}
970a6b396f4SSakari Ailus 
971a6b396f4SSakari Ailus 	ccsdata->backing = bin.base;
972a6b396f4SSakari Ailus 
973a6b396f4SSakari Ailus 	return 0;
974a6b396f4SSakari Ailus 
975a6b396f4SSakari Ailus out_free:
976a6b396f4SSakari Ailus 	kvfree(bin.base);
977a6b396f4SSakari Ailus 
978a6b396f4SSakari Ailus 	return rval;
979a6b396f4SSakari Ailus }
980