1// SPDX-License-Identifier: GPL-2.0
2/*
3 * MIPI Camera Control Interface (CCI) register access helpers.
4 *
5 * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
6 */
7
8#include <linux/bitfield.h>
9#include <linux/delay.h>
10#include <linux/dev_printk.h>
11#include <linux/module.h>
12#include <linux/regmap.h>
13#include <linux/types.h>
14
15#include <asm/unaligned.h>
16
17#include <media/v4l2-cci.h>
18
19int cci_read(struct regmap *map, u32 reg, u64 *val, int *err)
20{
21	bool little_endian;
22	unsigned int len;
23	u8 buf[8];
24	int ret;
25
26	if (err && *err)
27		return *err;
28
29	little_endian = reg & CCI_REG_LE;
30	len = CCI_REG_WIDTH_BYTES(reg);
31	reg = CCI_REG_ADDR(reg);
32
33	ret = regmap_bulk_read(map, reg, buf, len);
34	if (ret) {
35		dev_err(regmap_get_device(map), "Error reading reg 0x%04x: %d\n",
36			reg, ret);
37		goto out;
38	}
39
40	switch (len) {
41	case 1:
42		*val = buf[0];
43		break;
44	case 2:
45		if (little_endian)
46			*val = get_unaligned_le16(buf);
47		else
48			*val = get_unaligned_be16(buf);
49		break;
50	case 3:
51		if (little_endian)
52			*val = get_unaligned_le24(buf);
53		else
54			*val = get_unaligned_be24(buf);
55		break;
56	case 4:
57		if (little_endian)
58			*val = get_unaligned_le32(buf);
59		else
60			*val = get_unaligned_be32(buf);
61		break;
62	case 8:
63		if (little_endian)
64			*val = get_unaligned_le64(buf);
65		else
66			*val = get_unaligned_be64(buf);
67		break;
68	default:
69		dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n",
70			len, reg);
71		ret = -EINVAL;
72		break;
73	}
74
75out:
76	if (ret && err)
77		*err = ret;
78
79	return ret;
80}
81EXPORT_SYMBOL_GPL(cci_read);
82
83int cci_write(struct regmap *map, u32 reg, u64 val, int *err)
84{
85	bool little_endian;
86	unsigned int len;
87	u8 buf[8];
88	int ret;
89
90	if (err && *err)
91		return *err;
92
93	little_endian = reg & CCI_REG_LE;
94	len = CCI_REG_WIDTH_BYTES(reg);
95	reg = CCI_REG_ADDR(reg);
96
97	switch (len) {
98	case 1:
99		buf[0] = val;
100		break;
101	case 2:
102		if (little_endian)
103			put_unaligned_le16(val, buf);
104		else
105			put_unaligned_be16(val, buf);
106		break;
107	case 3:
108		if (little_endian)
109			put_unaligned_le24(val, buf);
110		else
111			put_unaligned_be24(val, buf);
112		break;
113	case 4:
114		if (little_endian)
115			put_unaligned_le32(val, buf);
116		else
117			put_unaligned_be32(val, buf);
118		break;
119	case 8:
120		if (little_endian)
121			put_unaligned_le64(val, buf);
122		else
123			put_unaligned_be64(val, buf);
124		break;
125	default:
126		dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n",
127			len, reg);
128		ret = -EINVAL;
129		goto out;
130	}
131
132	ret = regmap_bulk_write(map, reg, buf, len);
133	if (ret)
134		dev_err(regmap_get_device(map), "Error writing reg 0x%04x: %d\n",
135			reg, ret);
136
137out:
138	if (ret && err)
139		*err = ret;
140
141	return ret;
142}
143EXPORT_SYMBOL_GPL(cci_write);
144
145int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err)
146{
147	u64 readval;
148	int ret;
149
150	ret = cci_read(map, reg, &readval, err);
151	if (ret)
152		return ret;
153
154	val = (readval & ~mask) | (val & mask);
155
156	return cci_write(map, reg, val, err);
157}
158EXPORT_SYMBOL_GPL(cci_update_bits);
159
160int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs,
161			unsigned int num_regs, int *err)
162{
163	unsigned int i;
164	int ret;
165
166	for (i = 0; i < num_regs; i++) {
167		ret = cci_write(map, regs[i].reg, regs[i].val, err);
168		if (ret)
169			return ret;
170	}
171
172	return 0;
173}
174EXPORT_SYMBOL_GPL(cci_multi_reg_write);
175
176#if IS_ENABLED(CONFIG_V4L2_CCI_I2C)
177struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client,
178					int reg_addr_bits)
179{
180	struct regmap_config config = {
181		.reg_bits = reg_addr_bits,
182		.val_bits = 8,
183		.reg_format_endian = REGMAP_ENDIAN_BIG,
184		.disable_locking = true,
185	};
186
187	return devm_regmap_init_i2c(client, &config);
188}
189EXPORT_SYMBOL_GPL(devm_cci_regmap_init_i2c);
190#endif
191
192MODULE_LICENSE("GPL");
193MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
194MODULE_DESCRIPTION("MIPI Camera Control Interface (CCI) support");
195