Merge tag 'vfio-v6.9-rc1' of https://github.com/awilliam/linux-vfio
[linux-2.6-microblaze.git] / drivers / media / v4l2-core / v4l2-cci.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 <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%04x: %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%04x: %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");