diff options
Diffstat (limited to 'drivers/media/video/smiapp/smiapp-regs.c')
-rw-r--r-- | drivers/media/video/smiapp/smiapp-regs.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c new file mode 100644 index 000000000000..b1812b17a407 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-regs.c | |||
@@ -0,0 +1,273 @@ | |||
1 | /* | ||
2 | * drivers/media/video/smiapp/smiapp-regs.c | ||
3 | * | ||
4 | * Generic driver for SMIA/SMIA++ compliant camera modules | ||
5 | * | ||
6 | * Copyright (C) 2011--2012 Nokia Corporation | ||
7 | * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * version 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
21 | * 02110-1301 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include <linux/delay.h> | ||
26 | #include <linux/i2c.h> | ||
27 | |||
28 | #include "smiapp.h" | ||
29 | #include "smiapp-regs.h" | ||
30 | |||
31 | static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, | ||
32 | uint32_t phloat) | ||
33 | { | ||
34 | int32_t exp; | ||
35 | uint64_t man; | ||
36 | |||
37 | if (phloat >= 0x80000000) { | ||
38 | dev_err(&client->dev, "this is a negative number\n"); | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | if (phloat == 0x7f800000) | ||
43 | return ~0; /* Inf. */ | ||
44 | |||
45 | if ((phloat & 0x7f800000) == 0x7f800000) { | ||
46 | dev_err(&client->dev, "NaN or other special number\n"); | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | /* Valid cases begin here */ | ||
51 | if (phloat == 0) | ||
52 | return 0; /* Valid zero */ | ||
53 | |||
54 | if (phloat > 0x4f800000) | ||
55 | return ~0; /* larger than 4294967295 */ | ||
56 | |||
57 | /* | ||
58 | * Unbias exponent (note how phloat is now guaranteed to | ||
59 | * have 0 in the high bit) | ||
60 | */ | ||
61 | exp = ((int32_t)phloat >> 23) - 127; | ||
62 | |||
63 | /* Extract mantissa, add missing '1' bit and it's in MHz */ | ||
64 | man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; | ||
65 | |||
66 | if (exp < 0) | ||
67 | man >>= -exp; | ||
68 | else | ||
69 | man <<= exp; | ||
70 | |||
71 | man >>= 23; /* Remove mantissa bias */ | ||
72 | |||
73 | return man & 0xffffffff; | ||
74 | } | ||
75 | |||
76 | |||
77 | /* | ||
78 | * Read a 8/16/32-bit i2c register. The value is returned in 'val'. | ||
79 | * Returns zero if successful, or non-zero otherwise. | ||
80 | */ | ||
81 | static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, | ||
82 | u16 len, u32 *val) | ||
83 | { | ||
84 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); | ||
85 | struct i2c_msg msg; | ||
86 | unsigned char data[4]; | ||
87 | u16 offset = reg; | ||
88 | int r; | ||
89 | |||
90 | msg.addr = client->addr; | ||
91 | msg.flags = 0; | ||
92 | msg.len = 2; | ||
93 | msg.buf = data; | ||
94 | |||
95 | /* high byte goes out first */ | ||
96 | data[0] = (u8) (offset >> 8); | ||
97 | data[1] = (u8) offset; | ||
98 | r = i2c_transfer(client->adapter, &msg, 1); | ||
99 | if (r != 1) { | ||
100 | if (r >= 0) | ||
101 | r = -EBUSY; | ||
102 | goto err; | ||
103 | } | ||
104 | |||
105 | msg.len = len; | ||
106 | msg.flags = I2C_M_RD; | ||
107 | r = i2c_transfer(client->adapter, &msg, 1); | ||
108 | if (r != 1) { | ||
109 | if (r >= 0) | ||
110 | r = -EBUSY; | ||
111 | goto err; | ||
112 | } | ||
113 | |||
114 | *val = 0; | ||
115 | /* high byte comes first */ | ||
116 | switch (len) { | ||
117 | case SMIA_REG_32BIT: | ||
118 | *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + | ||
119 | data[3]; | ||
120 | break; | ||
121 | case SMIA_REG_16BIT: | ||
122 | *val = (data[0] << 8) + data[1]; | ||
123 | break; | ||
124 | case SMIA_REG_8BIT: | ||
125 | *val = data[0]; | ||
126 | break; | ||
127 | default: | ||
128 | BUG(); | ||
129 | } | ||
130 | |||
131 | return 0; | ||
132 | |||
133 | err: | ||
134 | dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); | ||
135 | |||
136 | return r; | ||
137 | } | ||
138 | |||
139 | /* Read a register using 8-bit access only. */ | ||
140 | static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg, | ||
141 | u16 len, u32 *val) | ||
142 | { | ||
143 | unsigned int i; | ||
144 | int rval; | ||
145 | |||
146 | *val = 0; | ||
147 | |||
148 | for (i = 0; i < len; i++) { | ||
149 | u32 val8; | ||
150 | |||
151 | rval = ____smiapp_read(sensor, reg + i, 1, &val8); | ||
152 | if (rval < 0) | ||
153 | return rval; | ||
154 | *val |= val8 << ((len - i - 1) << 3); | ||
155 | } | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * Read a 8/16/32-bit i2c register. The value is returned in 'val'. | ||
162 | * Returns zero if successful, or non-zero otherwise. | ||
163 | */ | ||
164 | static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, | ||
165 | bool only8) | ||
166 | { | ||
167 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); | ||
168 | unsigned int len = (u8)(reg >> 16); | ||
169 | int rval; | ||
170 | |||
171 | if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT | ||
172 | && len != SMIA_REG_32BIT) | ||
173 | return -EINVAL; | ||
174 | |||
175 | if (smiapp_quirk_reg(sensor, reg, val)) | ||
176 | goto found_quirk; | ||
177 | |||
178 | if (len == SMIA_REG_8BIT && !only8) | ||
179 | rval = ____smiapp_read(sensor, (u16)reg, len, val); | ||
180 | else | ||
181 | rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); | ||
182 | if (rval < 0) | ||
183 | return rval; | ||
184 | |||
185 | found_quirk: | ||
186 | if (reg & SMIA_REG_FLAG_FLOAT) | ||
187 | *val = float_to_u32_mul_1000000(client, *val); | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) | ||
193 | { | ||
194 | return __smiapp_read( | ||
195 | sensor, reg, val, | ||
196 | smiapp_needs_quirk(sensor, | ||
197 | SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); | ||
198 | } | ||
199 | |||
200 | int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) | ||
201 | { | ||
202 | return __smiapp_read(sensor, reg, val, true); | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * Write to a 8/16-bit register. | ||
207 | * Returns zero if successful, or non-zero otherwise. | ||
208 | */ | ||
209 | int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) | ||
210 | { | ||
211 | struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); | ||
212 | struct i2c_msg msg; | ||
213 | unsigned char data[6]; | ||
214 | unsigned int retries; | ||
215 | unsigned int flags = reg >> 24; | ||
216 | unsigned int len = (u8)(reg >> 16); | ||
217 | u16 offset = reg; | ||
218 | int r; | ||
219 | |||
220 | if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && | ||
221 | len != SMIA_REG_32BIT) || flags) | ||
222 | return -EINVAL; | ||
223 | |||
224 | msg.addr = client->addr; | ||
225 | msg.flags = 0; /* Write */ | ||
226 | msg.len = 2 + len; | ||
227 | msg.buf = data; | ||
228 | |||
229 | /* high byte goes out first */ | ||
230 | data[0] = (u8) (reg >> 8); | ||
231 | data[1] = (u8) (reg & 0xff); | ||
232 | |||
233 | switch (len) { | ||
234 | case SMIA_REG_8BIT: | ||
235 | data[2] = val; | ||
236 | break; | ||
237 | case SMIA_REG_16BIT: | ||
238 | data[2] = val >> 8; | ||
239 | data[3] = val; | ||
240 | break; | ||
241 | case SMIA_REG_32BIT: | ||
242 | data[2] = val >> 24; | ||
243 | data[3] = val >> 16; | ||
244 | data[4] = val >> 8; | ||
245 | data[5] = val; | ||
246 | break; | ||
247 | default: | ||
248 | BUG(); | ||
249 | } | ||
250 | |||
251 | for (retries = 0; retries < 5; retries++) { | ||
252 | /* | ||
253 | * Due to unknown reason sensor stops responding. This | ||
254 | * loop is a temporaty solution until the root cause | ||
255 | * is found. | ||
256 | */ | ||
257 | r = i2c_transfer(client->adapter, &msg, 1); | ||
258 | if (r == 1) { | ||
259 | if (retries) | ||
260 | dev_err(&client->dev, | ||
261 | "sensor i2c stall encountered. " | ||
262 | "retries: %d\n", retries); | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | usleep_range(2000, 2000); | ||
267 | } | ||
268 | |||
269 | dev_err(&client->dev, | ||
270 | "wrote 0x%x to offset 0x%x error %d\n", val, offset, r); | ||
271 | |||
272 | return r; | ||
273 | } | ||