forked from raspberrypi/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
media: Add MIPI CCI register access helper functions
The CSI2 specification specifies a standard method to access camera sensor registers called "Camera Control Interface (CCI)". This uses either 8 or 16 bit (big-endian wire order) register addresses and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths. Currently a lot of Linux camera sensor drivers all have their own custom helpers for this, often copy and pasted from other drivers. Add a set of generic helpers for this so that all sensor drivers can switch to a single common implementation. These helpers take an extra optional "int *err" function parameter, this can be used to chain a bunch of register accesses together with only a single error check at the end, rather than needing to error check each individual register access. The first failing call will set the contents of err to a non 0 value and all other calls will then become no-ops. Link: https://lore.kernel.org/linux-media/[email protected]/ Reviewed-by: Andy Shevchenko <[email protected]> Tested-by: Tommaso Merciai <[email protected]> Reviewed-by: Tommaso Merciai <[email protected]> Signed-off-by: Hans de Goede <[email protected]> Signed-off-by: Sakari Ailus <[email protected]> Signed-off-by: Mauro Carvalho Chehab <[email protected]>
- Loading branch information
1 parent
35a2991
commit 613cbb9
Showing
6 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.. SPDX-License-Identifier: GPL-2.0 | ||
V4L2 CCI kAPI | ||
^^^^^^^^^^^^^ | ||
.. kernel-doc:: include/media/v4l2-cci.h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ Video4Linux devices | |
v4l2-mem2mem | ||
v4l2-async | ||
v4l2-fwnode | ||
v4l2-cci | ||
v4l2-rect | ||
v4l2-tuner | ||
v4l2-common | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* MIPI Camera Control Interface (CCI) register access helpers. | ||
* | ||
* Copyright (C) 2023 Hans de Goede <[email protected]> | ||
*/ | ||
|
||
#include <linux/bitfield.h> | ||
#include <linux/delay.h> | ||
#include <linux/dev_printk.h> | ||
#include <linux/module.h> | ||
#include <linux/regmap.h> | ||
#include <linux/types.h> | ||
|
||
#include <asm/unaligned.h> | ||
|
||
#include <media/v4l2-cci.h> | ||
|
||
int cci_read(struct regmap *map, u32 reg, u64 *val, int *err) | ||
{ | ||
unsigned int len; | ||
u8 buf[8]; | ||
int ret; | ||
|
||
if (err && *err) | ||
return *err; | ||
|
||
len = FIELD_GET(CCI_REG_WIDTH_MASK, reg); | ||
reg = FIELD_GET(CCI_REG_ADDR_MASK, reg); | ||
|
||
ret = regmap_bulk_read(map, reg, buf, len); | ||
if (ret) { | ||
dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", | ||
reg, ret); | ||
goto out; | ||
} | ||
|
||
switch (len) { | ||
case 1: | ||
*val = buf[0]; | ||
break; | ||
case 2: | ||
*val = get_unaligned_be16(buf); | ||
break; | ||
case 3: | ||
*val = get_unaligned_be24(buf); | ||
break; | ||
case 4: | ||
*val = get_unaligned_be32(buf); | ||
break; | ||
case 8: | ||
*val = get_unaligned_be64(buf); | ||
break; | ||
default: | ||
dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n", | ||
len, reg); | ||
ret = -EINVAL; | ||
break; | ||
} | ||
|
||
out: | ||
if (ret && err) | ||
*err = ret; | ||
|
||
return ret; | ||
} | ||
EXPORT_SYMBOL_GPL(cci_read); | ||
|
||
int cci_write(struct regmap *map, u32 reg, u64 val, int *err) | ||
{ | ||
unsigned int len; | ||
u8 buf[8]; | ||
int ret; | ||
|
||
if (err && *err) | ||
return *err; | ||
|
||
len = FIELD_GET(CCI_REG_WIDTH_MASK, reg); | ||
reg = FIELD_GET(CCI_REG_ADDR_MASK, reg); | ||
|
||
switch (len) { | ||
case 1: | ||
buf[0] = val; | ||
break; | ||
case 2: | ||
put_unaligned_be16(val, buf); | ||
break; | ||
case 3: | ||
put_unaligned_be24(val, buf); | ||
break; | ||
case 4: | ||
put_unaligned_be32(val, buf); | ||
break; | ||
case 8: | ||
put_unaligned_be64(val, buf); | ||
break; | ||
default: | ||
dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n", | ||
len, reg); | ||
ret = -EINVAL; | ||
goto out; | ||
} | ||
|
||
ret = regmap_bulk_write(map, reg, buf, len); | ||
if (ret) | ||
dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", | ||
reg, ret); | ||
|
||
out: | ||
if (ret && err) | ||
*err = ret; | ||
|
||
return ret; | ||
} | ||
EXPORT_SYMBOL_GPL(cci_write); | ||
|
||
int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err) | ||
{ | ||
u64 readval; | ||
int ret; | ||
|
||
ret = cci_read(map, reg, &readval, err); | ||
if (ret) | ||
return ret; | ||
|
||
val = (readval & ~mask) | (val & mask); | ||
|
||
return cci_write(map, reg, val, err); | ||
} | ||
EXPORT_SYMBOL_GPL(cci_update_bits); | ||
|
||
int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs, | ||
unsigned int num_regs, int *err) | ||
{ | ||
unsigned int i; | ||
int ret; | ||
|
||
for (i = 0; i < num_regs; i++) { | ||
ret = cci_write(map, regs[i].reg, regs[i].val, err); | ||
if (ret) | ||
return ret; | ||
} | ||
|
||
return 0; | ||
} | ||
EXPORT_SYMBOL_GPL(cci_multi_reg_write); | ||
|
||
#if IS_ENABLED(CONFIG_V4L2_CCI_I2C) | ||
struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client, | ||
int reg_addr_bits) | ||
{ | ||
struct regmap_config config = { | ||
.reg_bits = reg_addr_bits, | ||
.val_bits = 8, | ||
.reg_format_endian = REGMAP_ENDIAN_BIG, | ||
.disable_locking = true, | ||
}; | ||
|
||
return devm_regmap_init_i2c(client, &config); | ||
} | ||
EXPORT_SYMBOL_GPL(devm_cci_regmap_init_i2c); | ||
#endif | ||
|
||
MODULE_LICENSE("GPL"); | ||
MODULE_AUTHOR("Hans de Goede <[email protected]>"); | ||
MODULE_DESCRIPTION("MIPI Camera Control Interface (CCI) support"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* MIPI Camera Control Interface (CCI) register access helpers. | ||
* | ||
* Copyright (C) 2023 Hans de Goede <[email protected]> | ||
*/ | ||
#ifndef _V4L2_CCI_H | ||
#define _V4L2_CCI_H | ||
|
||
#include <linux/types.h> | ||
|
||
struct i2c_client; | ||
struct regmap; | ||
|
||
/** | ||
* struct cci_reg_sequence - An individual write from a sequence of CCI writes | ||
* | ||
* @reg: Register address, use CCI_REG#() macros to encode reg width | ||
* @val: Register value | ||
* | ||
* Register/value pairs for sequences of writes. | ||
*/ | ||
struct cci_reg_sequence { | ||
u32 reg; | ||
u64 val; | ||
}; | ||
|
||
/* | ||
* Macros to define register address with the register width encoded | ||
* into the higher bits. | ||
*/ | ||
#define CCI_REG_ADDR_MASK GENMASK(15, 0) | ||
#define CCI_REG_WIDTH_SHIFT 16 | ||
#define CCI_REG_WIDTH_MASK GENMASK(19, 16) | ||
|
||
#define CCI_REG8(x) ((1 << CCI_REG_WIDTH_SHIFT) | (x)) | ||
#define CCI_REG16(x) ((2 << CCI_REG_WIDTH_SHIFT) | (x)) | ||
#define CCI_REG24(x) ((3 << CCI_REG_WIDTH_SHIFT) | (x)) | ||
#define CCI_REG32(x) ((4 << CCI_REG_WIDTH_SHIFT) | (x)) | ||
#define CCI_REG64(x) ((8 << CCI_REG_WIDTH_SHIFT) | (x)) | ||
|
||
/** | ||
* cci_read() - Read a value from a single CCI register | ||
* | ||
* @map: Register map to read from | ||
* @reg: Register address to read, use CCI_REG#() macros to encode reg width | ||
* @val: Pointer to store read value | ||
* @err: Optional pointer to store errors, if a previous error is set | ||
* then the read will be skipped | ||
* | ||
* Return: %0 on success or a negative error code on failure. | ||
*/ | ||
int cci_read(struct regmap *map, u32 reg, u64 *val, int *err); | ||
|
||
/** | ||
* cci_write() - Write a value to a single CCI register | ||
* | ||
* @map: Register map to write to | ||
* @reg: Register address to write, use CCI_REG#() macros to encode reg width | ||
* @val: Value to be written | ||
* @err: Optional pointer to store errors, if a previous error is set | ||
* then the write will be skipped | ||
* | ||
* Return: %0 on success or a negative error code on failure. | ||
*/ | ||
int cci_write(struct regmap *map, u32 reg, u64 val, int *err); | ||
|
||
/** | ||
* cci_update_bits() - Perform a read/modify/write cycle on | ||
* a single CCI register | ||
* | ||
* @map: Register map to update | ||
* @reg: Register address to update, use CCI_REG#() macros to encode reg width | ||
* @mask: Bitmask to change | ||
* @val: New value for bitmask | ||
* @err: Optional pointer to store errors, if a previous error is set | ||
* then the update will be skipped | ||
* | ||
* Note this uses read-modify-write to update the bits, atomicity with regards | ||
* to other cci_*() register access functions is NOT guaranteed. | ||
* | ||
* Return: %0 on success or a negative error code on failure. | ||
*/ | ||
int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err); | ||
|
||
/** | ||
* cci_multi_reg_write() - Write multiple registers to the device | ||
* | ||
* @map: Register map to write to | ||
* @regs: Array of structures containing register-address, -value pairs to be | ||
* written, register-addresses use CCI_REG#() macros to encode reg width | ||
* @num_regs: Number of registers to write | ||
* @err: Optional pointer to store errors, if a previous error is set | ||
* then the write will be skipped | ||
* | ||
* Write multiple registers to the device where the set of register, value | ||
* pairs are supplied in any order, possibly not all in a single range. | ||
* | ||
* Use of the CCI_REG#() macros to encode reg width is mandatory. | ||
* | ||
* For raw lists of register-address, -value pairs with only 8 bit | ||
* wide writes regmap_multi_reg_write() can be used instead. | ||
* | ||
* Return: %0 on success or a negative error code on failure. | ||
*/ | ||
int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs, | ||
unsigned int num_regs, int *err); | ||
|
||
#if IS_ENABLED(CONFIG_V4L2_CCI_I2C) | ||
/** | ||
* devm_cci_regmap_init_i2c() - Create regmap to use with cci_*() register | ||
* access functions | ||
* | ||
* @client: i2c_client to create the regmap for | ||
* @reg_addr_bits: register address width to use (8 or 16) | ||
* | ||
* Note the memory for the created regmap is devm() managed, tied to the client. | ||
* | ||
* Return: %0 on success or a negative error code on failure. | ||
*/ | ||
struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client, | ||
int reg_addr_bits); | ||
#endif | ||
|
||
#endif |