File Index
Files and directories
Sign in
FILE

v4l2-cci.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/v4l2-core/v4l2-cci.c C
  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 <linux/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	/*
 27	 * TODO: Fix smatch. Assign *val to 0 here in order to avoid
 28	 * failing a smatch check on caller when the caller proceeds to
 29	 * read *val without initialising it on caller's side. *val is set
 30	 * to a valid value whenever this function returns 0 but smatch
 31	 * can't figure that out currently.
 32	 */
 33	*val = 0;
 34
 35	if (err && *err)
 36		return *err;
 37
 38	little_endian = reg & CCI_REG_LE;
 39	len = CCI_REG_WIDTH_BYTES(reg);
 40	reg = CCI_REG_ADDR(reg);
 41
 42	ret = regmap_bulk_read(map, reg, buf, len);
 43	if (ret) {
 44		dev_err(regmap_get_device(map), "Error reading reg 0x%04x: %d\n",
 45			reg, ret);
 46		goto out;
 47	}
 48
 49	switch (len) {
 50	case 1:
 51		*val = buf[0];
 52		break;
 53	case 2:
 54		if (little_endian)
 55			*val = get_unaligned_le16(buf);
 56		else
 57			*val = get_unaligned_be16(buf);
 58		break;
 59	case 3:
 60		if (little_endian)
 61			*val = get_unaligned_le24(buf);
 62		else
 63			*val = get_unaligned_be24(buf);
 64		break;
 65	case 4:
 66		if (little_endian)
 67			*val = get_unaligned_le32(buf);
 68		else
 69			*val = get_unaligned_be32(buf);
 70		break;
 71	case 8:
 72		if (little_endian)
 73			*val = get_unaligned_le64(buf);
 74		else
 75			*val = get_unaligned_be64(buf);
 76		break;
 77	default:
 78		dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n",
 79			len, reg);
 80		ret = -EINVAL;
 81		break;
 82	}
 83
 84out:
 85	if (ret && err)
 86		*err = ret;
 87
 88	return ret;
 89}
 90EXPORT_SYMBOL_GPL(cci_read);
 91
 92int cci_write(struct regmap *map, u32 reg, u64 val, int *err)
 93{
 94	bool little_endian;
 95	unsigned int len;
 96	u8 buf[8];
 97	int ret;
 98
 99	if (err && *err)
100		return *err;
101
102	little_endian = reg & CCI_REG_LE;
103	len = CCI_REG_WIDTH_BYTES(reg);
104	reg = CCI_REG_ADDR(reg);
105
106	switch (len) {
107	case 1:
108		buf[0] = val;
109		break;
110	case 2:
111		if (little_endian)
112			put_unaligned_le16(val, buf);
113		else
114			put_unaligned_be16(val, buf);
115		break;
116	case 3:
117		if (little_endian)
118			put_unaligned_le24(val, buf);
119		else
120			put_unaligned_be24(val, buf);
121		break;
122	case 4:
123		if (little_endian)
124			put_unaligned_le32(val, buf);
125		else
126			put_unaligned_be32(val, buf);
127		break;
128	case 8:
129		if (little_endian)
130			put_unaligned_le64(val, buf);
131		else
132			put_unaligned_be64(val, buf);
133		break;
134	default:
135		dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n",
136			len, reg);
137		ret = -EINVAL;
138		goto out;
139	}
140
141	ret = regmap_bulk_write(map, reg, buf, len);
142	if (ret)
143		dev_err(regmap_get_device(map), "Error writing reg 0x%04x: %d\n",
144			reg, ret);
145
146out:
147	if (ret && err)
148		*err = ret;
149
150	return ret;
151}
152EXPORT_SYMBOL_GPL(cci_write);
153
154int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err)
155{
156	u64 readval;
157	int ret;
158
159	ret = cci_read(map, reg, &readval, err);
160	if (ret)
161		return ret;
162
163	val = (readval & ~mask) | (val & mask);
164
165	return cci_write(map, reg, val, err);
166}
167EXPORT_SYMBOL_GPL(cci_update_bits);
168
169int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs,
170			unsigned int num_regs, int *err)
171{
172	unsigned int i;
173	int ret;
174
175	for (i = 0; i < num_regs; i++) {
176		ret = cci_write(map, regs[i].reg, regs[i].val, err);
177		if (ret)
178			return ret;
179	}
180
181	return 0;
182}
183EXPORT_SYMBOL_GPL(cci_multi_reg_write);
184
185#if IS_ENABLED(CONFIG_V4L2_CCI_I2C)
186struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client,
187					int reg_addr_bits)
188{
189	struct regmap_config config = {
190		.reg_bits = reg_addr_bits,
191		.val_bits = 8,
192		.reg_format_endian = REGMAP_ENDIAN_BIG,
193		.disable_locking = true,
194	};
195
196	return devm_regmap_init_i2c(client, &config);
197}
198EXPORT_SYMBOL_GPL(devm_cci_regmap_init_i2c);
199#endif
200
201MODULE_LICENSE("GPL");
202MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
203MODULE_DESCRIPTION("MIPI Camera Control Interface (CCI) support");
~/ Read-only file preview.