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 19 int cci_read(struct regmap *map, u32 reg, u64 *val, int *err) 20 { 21 unsigned int len; 22 u8 buf[8]; 23 int ret; 24 25 if (err && *err) 26 return *err; 27 28 len = FIELD_GET(CCI_REG_WIDTH_MASK, reg); 29 reg = FIELD_GET(CCI_REG_ADDR_MASK, reg); 30 31 ret = regmap_bulk_read(map, reg, buf, len); 32 if (ret) { 33 dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n", 34 reg, ret); 35 goto out; 36 } 37 38 switch (len) { 39 case 1: 40 *val = buf[0]; 41 break; 42 case 2: 43 *val = get_unaligned_be16(buf); 44 break; 45 case 3: 46 *val = get_unaligned_be24(buf); 47 break; 48 case 4: 49 *val = get_unaligned_be32(buf); 50 break; 51 case 8: 52 *val = get_unaligned_be64(buf); 53 break; 54 default: 55 dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n", 56 len, reg); 57 ret = -EINVAL; 58 break; 59 } 60 61 out: 62 if (ret && err) 63 *err = ret; 64 65 return ret; 66 } 67 EXPORT_SYMBOL_GPL(cci_read); 68 69 int cci_write(struct regmap *map, u32 reg, u64 val, int *err) 70 { 71 unsigned int len; 72 u8 buf[8]; 73 int ret; 74 75 if (err && *err) 76 return *err; 77 78 len = FIELD_GET(CCI_REG_WIDTH_MASK, reg); 79 reg = FIELD_GET(CCI_REG_ADDR_MASK, reg); 80 81 switch (len) { 82 case 1: 83 buf[0] = val; 84 break; 85 case 2: 86 put_unaligned_be16(val, buf); 87 break; 88 case 3: 89 put_unaligned_be24(val, buf); 90 break; 91 case 4: 92 put_unaligned_be32(val, buf); 93 break; 94 case 8: 95 put_unaligned_be64(val, buf); 96 break; 97 default: 98 dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n", 99 len, reg); 100 ret = -EINVAL; 101 goto out; 102 } 103 104 ret = regmap_bulk_write(map, reg, buf, len); 105 if (ret) 106 dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n", 107 reg, ret); 108 109 out: 110 if (ret && err) 111 *err = ret; 112 113 return ret; 114 } 115 EXPORT_SYMBOL_GPL(cci_write); 116 117 int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err) 118 { 119 u64 readval; 120 int ret; 121 122 ret = cci_read(map, reg, &readval, err); 123 if (ret) 124 return ret; 125 126 val = (readval & ~mask) | (val & mask); 127 128 return cci_write(map, reg, val, err); 129 } 130 EXPORT_SYMBOL_GPL(cci_update_bits); 131 132 int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs, 133 unsigned int num_regs, int *err) 134 { 135 unsigned int i; 136 int ret; 137 138 for (i = 0; i < num_regs; i++) { 139 ret = cci_write(map, regs[i].reg, regs[i].val, err); 140 if (ret) 141 return ret; 142 } 143 144 return 0; 145 } 146 EXPORT_SYMBOL_GPL(cci_multi_reg_write); 147 148 #if IS_ENABLED(CONFIG_V4L2_CCI_I2C) 149 struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client, 150 int reg_addr_bits) 151 { 152 struct regmap_config config = { 153 .reg_bits = reg_addr_bits, 154 .val_bits = 8, 155 .reg_format_endian = REGMAP_ENDIAN_BIG, 156 .disable_locking = true, 157 }; 158 159 return devm_regmap_init_i2c(client, &config); 160 } 161 EXPORT_SYMBOL_GPL(devm_cci_regmap_init_i2c); 162 #endif 163 164 MODULE_LICENSE("GPL"); 165 MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); 166 MODULE_DESCRIPTION("MIPI Camera Control Interface (CCI) support"); 167