204 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 avoid28 * failing a smatch check on caller when the caller proceeds to29 * read *val without initialising it on caller's side. *val is set30 * to a valid value whenever this function returns 0 but smatch31 * 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 else57 *val = get_unaligned_be16(buf);58 break;59 case 3:60 if (little_endian)61 *val = get_unaligned_le24(buf);62 else63 *val = get_unaligned_be24(buf);64 break;65 case 4:66 if (little_endian)67 *val = get_unaligned_le32(buf);68 else69 *val = get_unaligned_be32(buf);70 break;71 case 8:72 if (little_endian)73 *val = get_unaligned_le64(buf);74 else75 *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 else114 put_unaligned_be16(val, buf);115 break;116 case 3:117 if (little_endian)118 put_unaligned_le24(val, buf);119 else120 put_unaligned_be24(val, buf);121 break;122 case 4:123 if (little_endian)124 put_unaligned_le32(val, buf);125 else126 put_unaligned_be32(val, buf);127 break;128 case 8:129 if (little_endian)130 put_unaligned_le64(val, buf);131 else132 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#endif200 201MODULE_LICENSE("GPL");202MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");203MODULE_DESCRIPTION("MIPI Camera Control Interface (CCI) support");204