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 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%4x: %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 75 out: 76 if (ret && err) 77 *err = ret; 78 79 return ret; 80 } 81 EXPORT_SYMBOL_GPL(cci_read); 82 83 int 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%4x: %d\n", 135 reg, ret); 136 137 out: 138 if (ret && err) 139 *err = ret; 140 141 return ret; 142 } 143 EXPORT_SYMBOL_GPL(cci_write); 144 145 int 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 } 158 EXPORT_SYMBOL_GPL(cci_update_bits); 159 160 int 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 } 174 EXPORT_SYMBOL_GPL(cci_multi_reg_write); 175 176 #if IS_ENABLED(CONFIG_V4L2_CCI_I2C) 177 struct 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 } 189 EXPORT_SYMBOL_GPL(devm_cci_regmap_init_i2c); 190 #endif 191 192 MODULE_LICENSE("GPL"); 193 MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); 194 MODULE_DESCRIPTION("MIPI Camera Control Interface (CCI) support"); 195