]>
Commit | Line | Data |
---|---|---|
c109f816 EA |
1 | /* |
2 | * Driver for the s5k83a sensor | |
3 | * | |
0c505e68 | 4 | * Copyright (C) 2008 Erik Andrén |
c109f816 EA |
5 | * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. |
6 | * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> | |
7 | * | |
8 | * Portions of code to USB interface and ALi driver software, | |
9 | * Copyright (c) 2006 Willem Duinker | |
10 | * v4l2 interface modeled after the V4L2 driver | |
11 | * for SN9C10x PC Camera Controllers | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or | |
14 | * modify it under the terms of the GNU General Public License as | |
15 | * published by the Free Software Foundation, version 2. | |
16 | * | |
17 | */ | |
18 | ||
ac3d5bfe | 19 | #include <linux/kthread.h> |
5a0e3ad6 | 20 | #include <linux/slab.h> |
c109f816 EA |
21 | #include "m5602_s5k83a.h" |
22 | ||
cf811d50 EA |
23 | static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val); |
24 | static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val); | |
25 | static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val); | |
26 | static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); | |
27 | static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val); | |
28 | static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); | |
29 | static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); | |
30 | static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val); | |
31 | static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); | |
32 | static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val); | |
33 | ||
74cadfe1 EA |
34 | static struct v4l2_pix_format s5k83a_modes[] = { |
35 | { | |
36 | 640, | |
37 | 480, | |
38 | V4L2_PIX_FMT_SBGGR8, | |
39 | V4L2_FIELD_NONE, | |
40 | .sizeimage = | |
41 | 640 * 480, | |
42 | .bytesperline = 640, | |
43 | .colorspace = V4L2_COLORSPACE_SRGB, | |
44 | .priv = 0 | |
45 | } | |
46 | }; | |
47 | ||
2e03669d | 48 | static const struct ctrl s5k83a_ctrls[] = { |
ac3d5bfe | 49 | #define GAIN_IDX 0 |
e17cc08c EA |
50 | { |
51 | { | |
ac3d5bfe | 52 | .id = V4L2_CID_GAIN, |
e17cc08c | 53 | .type = V4L2_CTRL_TYPE_INTEGER, |
ac3d5bfe | 54 | .name = "gain", |
e17cc08c EA |
55 | .minimum = 0x00, |
56 | .maximum = 0xff, | |
57 | .step = 0x01, | |
ac3d5bfe | 58 | .default_value = S5K83A_DEFAULT_GAIN, |
e17cc08c EA |
59 | .flags = V4L2_CTRL_FLAG_SLIDER |
60 | }, | |
ac3d5bfe LK |
61 | .set = s5k83a_set_gain, |
62 | .get = s5k83a_get_gain | |
e17cc08c | 63 | |
ac3d5bfe LK |
64 | }, |
65 | #define BRIGHTNESS_IDX 1 | |
66 | { | |
e17cc08c | 67 | { |
ac3d5bfe | 68 | .id = V4L2_CID_BRIGHTNESS, |
e17cc08c | 69 | .type = V4L2_CTRL_TYPE_INTEGER, |
ac3d5bfe | 70 | .name = "brightness", |
e17cc08c EA |
71 | .minimum = 0x00, |
72 | .maximum = 0xff, | |
73 | .step = 0x01, | |
ac3d5bfe | 74 | .default_value = S5K83A_DEFAULT_BRIGHTNESS, |
e17cc08c EA |
75 | .flags = V4L2_CTRL_FLAG_SLIDER |
76 | }, | |
ac3d5bfe LK |
77 | .set = s5k83a_set_brightness, |
78 | .get = s5k83a_get_brightness, | |
79 | }, | |
80 | #define EXPOSURE_IDX 2 | |
81 | { | |
e17cc08c | 82 | { |
ac3d5bfe | 83 | .id = V4L2_CID_EXPOSURE, |
e17cc08c | 84 | .type = V4L2_CTRL_TYPE_INTEGER, |
ac3d5bfe | 85 | .name = "exposure", |
e17cc08c | 86 | .minimum = 0x00, |
ac3d5bfe | 87 | .maximum = S5K83A_MAXIMUM_EXPOSURE, |
e17cc08c | 88 | .step = 0x01, |
ac3d5bfe | 89 | .default_value = S5K83A_DEFAULT_EXPOSURE, |
e17cc08c EA |
90 | .flags = V4L2_CTRL_FLAG_SLIDER |
91 | }, | |
ac3d5bfe LK |
92 | .set = s5k83a_set_exposure, |
93 | .get = s5k83a_get_exposure | |
94 | }, | |
95 | #define HFLIP_IDX 3 | |
96 | { | |
e17cc08c | 97 | { |
00e02567 EA |
98 | .id = V4L2_CID_HFLIP, |
99 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
100 | .name = "horizontal flip", | |
101 | .minimum = 0, | |
102 | .maximum = 1, | |
103 | .step = 1, | |
104 | .default_value = 0 | |
e17cc08c EA |
105 | }, |
106 | .set = s5k83a_set_hflip, | |
107 | .get = s5k83a_get_hflip | |
ac3d5bfe LK |
108 | }, |
109 | #define VFLIP_IDX 4 | |
110 | { | |
e17cc08c | 111 | { |
00e02567 EA |
112 | .id = V4L2_CID_VFLIP, |
113 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
114 | .name = "vertical flip", | |
115 | .minimum = 0, | |
116 | .maximum = 1, | |
117 | .step = 1, | |
118 | .default_value = 0 | |
e17cc08c EA |
119 | }, |
120 | .set = s5k83a_set_vflip, | |
121 | .get = s5k83a_get_vflip | |
122 | } | |
123 | }; | |
124 | ||
658efb63 | 125 | static void s5k83a_dump_registers(struct sd *sd); |
ac3d5bfe LK |
126 | static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data); |
127 | static int s5k83a_set_led_indication(struct sd *sd, u8 val); | |
57851d0c EA |
128 | static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, |
129 | __s32 vflip, __s32 hflip); | |
658efb63 | 130 | |
c109f816 EA |
131 | int s5k83a_probe(struct sd *sd) |
132 | { | |
ac3d5bfe | 133 | struct s5k83a_priv *sens_priv; |
c109f816 EA |
134 | u8 prod_id = 0, ver_id = 0; |
135 | int i, err = 0; | |
136 | ||
137 | if (force_sensor) { | |
138 | if (force_sensor == S5K83A_SENSOR) { | |
139 | info("Forcing a %s sensor", s5k83a.name); | |
140 | goto sensor_found; | |
141 | } | |
142 | /* If we want to force another sensor, don't try to probe this | |
143 | * one */ | |
144 | return -ENODEV; | |
145 | } | |
146 | ||
969cc926 | 147 | PDEBUG(D_PROBE, "Probing for a s5k83a sensor"); |
c109f816 EA |
148 | |
149 | /* Preinit the sensor */ | |
150 | for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) { | |
151 | u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]}; | |
152 | if (preinit_s5k83a[i][0] == SENSOR) | |
6dc4cff0 | 153 | err = m5602_write_sensor(sd, preinit_s5k83a[i][1], |
c109f816 EA |
154 | data, 2); |
155 | else | |
156 | err = m5602_write_bridge(sd, preinit_s5k83a[i][1], | |
157 | data[0]); | |
158 | } | |
159 | ||
160 | /* We don't know what register (if any) that contain the product id | |
161 | * Just pick the first addresses that seem to produce the same results | |
162 | * on multiple machines */ | |
6b9c0a2a | 163 | if (m5602_read_sensor(sd, 0x00, &prod_id, 1)) |
c109f816 EA |
164 | return -ENODEV; |
165 | ||
6b9c0a2a | 166 | if (m5602_read_sensor(sd, 0x01, &ver_id, 1)) |
c109f816 EA |
167 | return -ENODEV; |
168 | ||
169 | if ((prod_id == 0xff) || (ver_id == 0xff)) | |
170 | return -ENODEV; | |
171 | else | |
172 | info("Detected a s5k83a sensor"); | |
173 | ||
174 | sensor_found: | |
ac3d5bfe LK |
175 | sens_priv = kmalloc( |
176 | sizeof(struct s5k83a_priv), GFP_KERNEL); | |
177 | if (!sens_priv) | |
178 | return -ENOMEM; | |
179 | ||
180 | sens_priv->settings = | |
181 | kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL); | |
b807d175 JL |
182 | if (!sens_priv->settings) { |
183 | kfree(sens_priv); | |
ac3d5bfe | 184 | return -ENOMEM; |
b807d175 | 185 | } |
ac3d5bfe | 186 | |
74cadfe1 EA |
187 | sd->gspca_dev.cam.cam_mode = s5k83a_modes; |
188 | sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes); | |
e17cc08c | 189 | sd->desc->ctrls = s5k83a_ctrls; |
e4cc4fcc | 190 | sd->desc->nctrls = ARRAY_SIZE(s5k83a_ctrls); |
ac3d5bfe LK |
191 | |
192 | /* null the pointer! thread is't running now */ | |
193 | sens_priv->rotation_thread = NULL; | |
194 | ||
195 | for (i = 0; i < ARRAY_SIZE(s5k83a_ctrls); i++) | |
196 | sens_priv->settings[i] = s5k83a_ctrls[i].qctrl.default_value; | |
197 | ||
198 | sd->sensor_priv = sens_priv; | |
c109f816 EA |
199 | return 0; |
200 | } | |
201 | ||
c109f816 EA |
202 | int s5k83a_init(struct sd *sd) |
203 | { | |
204 | int i, err = 0; | |
c41507ba LK |
205 | s32 *sensor_settings = |
206 | ((struct s5k83a_priv *) sd->sensor_priv)->settings; | |
c109f816 EA |
207 | |
208 | for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) { | |
209 | u8 data[2] = {0x00, 0x00}; | |
210 | ||
211 | switch (init_s5k83a[i][0]) { | |
212 | case BRIDGE: | |
213 | err = m5602_write_bridge(sd, | |
214 | init_s5k83a[i][1], | |
215 | init_s5k83a[i][2]); | |
216 | break; | |
217 | ||
218 | case SENSOR: | |
219 | data[0] = init_s5k83a[i][2]; | |
6dc4cff0 | 220 | err = m5602_write_sensor(sd, |
c109f816 EA |
221 | init_s5k83a[i][1], data, 1); |
222 | break; | |
223 | ||
224 | case SENSOR_LONG: | |
225 | data[0] = init_s5k83a[i][2]; | |
226 | data[1] = init_s5k83a[i][3]; | |
6dc4cff0 | 227 | err = m5602_write_sensor(sd, |
c109f816 EA |
228 | init_s5k83a[i][1], data, 2); |
229 | break; | |
230 | default: | |
231 | info("Invalid stream command, exiting init"); | |
232 | return -EINVAL; | |
233 | } | |
234 | } | |
235 | ||
236 | if (dump_sensor) | |
237 | s5k83a_dump_registers(sd); | |
238 | ||
57851d0c EA |
239 | err = s5k83a_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); |
240 | if (err < 0) | |
241 | return err; | |
242 | ||
243 | err = s5k83a_set_brightness(&sd->gspca_dev, | |
244 | sensor_settings[BRIGHTNESS_IDX]); | |
245 | if (err < 0) | |
246 | return err; | |
247 | ||
248 | err = s5k83a_set_exposure(&sd->gspca_dev, | |
249 | sensor_settings[EXPOSURE_IDX]); | |
250 | if (err < 0) | |
251 | return err; | |
252 | ||
253 | err = s5k83a_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); | |
254 | if (err < 0) | |
255 | return err; | |
256 | ||
257 | err = s5k83a_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); | |
258 | ||
259 | return err; | |
c109f816 EA |
260 | } |
261 | ||
ac3d5bfe LK |
262 | static int rotation_thread_function(void *data) |
263 | { | |
264 | struct sd *sd = (struct sd *) data; | |
265 | struct s5k83a_priv *sens_priv = sd->sensor_priv; | |
266 | u8 reg, previous_rotation = 0; | |
267 | __s32 vflip, hflip; | |
268 | ||
269 | set_current_state(TASK_INTERRUPTIBLE); | |
270 | while (!schedule_timeout(100)) { | |
271 | if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock)) | |
272 | break; | |
273 | ||
274 | s5k83a_get_rotation(sd, ®); | |
275 | if (previous_rotation != reg) { | |
276 | previous_rotation = reg; | |
277 | info("Camera was flipped"); | |
278 | ||
279 | s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); | |
280 | s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); | |
281 | ||
282 | if (reg) { | |
283 | vflip = !vflip; | |
284 | hflip = !hflip; | |
285 | } | |
1b844b53 EA |
286 | s5k83a_set_flip_real((struct gspca_dev *) sd, |
287 | vflip, hflip); | |
ac3d5bfe LK |
288 | } |
289 | ||
290 | mutex_unlock(&sd->gspca_dev.usb_lock); | |
291 | set_current_state(TASK_INTERRUPTIBLE); | |
292 | } | |
293 | ||
294 | /* return to "front" flip */ | |
295 | if (previous_rotation) { | |
296 | s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); | |
297 | s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); | |
298 | s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip); | |
299 | } | |
300 | ||
301 | sens_priv->rotation_thread = NULL; | |
302 | return 0; | |
303 | } | |
304 | ||
4a7581f0 LK |
305 | int s5k83a_start(struct sd *sd) |
306 | { | |
cde41bb2 | 307 | int i, err = 0; |
ac3d5bfe LK |
308 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
309 | ||
310 | /* Create another thread, polling the GPIO ports of the camera to check | |
311 | if it got rotated. This is how the windows driver does it so we have | |
312 | to assume that there is no better way of accomplishing this */ | |
57851d0c | 313 | sens_priv->rotation_thread = kthread_create(rotation_thread_function, |
cde41bb2 | 314 | sd, "rotation thread"); |
ac3d5bfe LK |
315 | wake_up_process(sens_priv->rotation_thread); |
316 | ||
cde41bb2 EA |
317 | /* Preinit the sensor */ |
318 | for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) { | |
319 | u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]}; | |
320 | if (start_s5k83a[i][0] == SENSOR) | |
321 | err = m5602_write_sensor(sd, start_s5k83a[i][1], | |
322 | data, 2); | |
323 | else | |
324 | err = m5602_write_bridge(sd, start_s5k83a[i][1], | |
325 | data[0]); | |
326 | } | |
327 | if (err < 0) | |
328 | return err; | |
329 | ||
4a7581f0 LK |
330 | return s5k83a_set_led_indication(sd, 1); |
331 | } | |
332 | ||
333 | int s5k83a_stop(struct sd *sd) | |
334 | { | |
ac3d5bfe LK |
335 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
336 | ||
337 | if (sens_priv->rotation_thread) | |
338 | kthread_stop(sens_priv->rotation_thread); | |
339 | ||
4a7581f0 LK |
340 | return s5k83a_set_led_indication(sd, 0); |
341 | } | |
342 | ||
ac3d5bfe | 343 | void s5k83a_disconnect(struct sd *sd) |
c109f816 | 344 | { |
ac3d5bfe | 345 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
c109f816 | 346 | |
ac3d5bfe | 347 | s5k83a_stop(sd); |
c109f816 | 348 | |
ac3d5bfe LK |
349 | sd->sensor = NULL; |
350 | kfree(sens_priv->settings); | |
351 | kfree(sens_priv); | |
352 | } | |
c109f816 | 353 | |
cf811d50 | 354 | static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) |
c109f816 | 355 | { |
c109f816 | 356 | struct sd *sd = (struct sd *) gspca_dev; |
ac3d5bfe | 357 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
c109f816 | 358 | |
ac3d5bfe LK |
359 | *val = sens_priv->settings[GAIN_IDX]; |
360 | return 0; | |
c109f816 EA |
361 | } |
362 | ||
cf811d50 | 363 | static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) |
c109f816 EA |
364 | { |
365 | int err; | |
366 | u8 data[2]; | |
367 | struct sd *sd = (struct sd *) gspca_dev; | |
ac3d5bfe LK |
368 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
369 | ||
370 | sens_priv->settings[GAIN_IDX] = val; | |
c109f816 EA |
371 | |
372 | data[0] = 0x00; | |
373 | data[1] = 0x20; | |
6dc4cff0 | 374 | err = m5602_write_sensor(sd, 0x14, data, 2); |
c109f816 | 375 | if (err < 0) |
051781b3 | 376 | return err; |
c109f816 EA |
377 | |
378 | data[0] = 0x01; | |
379 | data[1] = 0x00; | |
6dc4cff0 | 380 | err = m5602_write_sensor(sd, 0x0d, data, 2); |
c109f816 | 381 | if (err < 0) |
051781b3 | 382 | return err; |
c109f816 EA |
383 | |
384 | /* FIXME: This is not sane, we need to figure out the composition | |
385 | of these registers */ | |
ac3d5bfe LK |
386 | data[0] = val >> 3; /* gain, high 5 bits */ |
387 | data[1] = val >> 1; /* gain, high 7 bits */ | |
388 | err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2); | |
c109f816 | 389 | |
7b2cd079 | 390 | return err; |
c109f816 EA |
391 | } |
392 | ||
cf811d50 | 393 | static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) |
c109f816 | 394 | { |
c109f816 | 395 | struct sd *sd = (struct sd *) gspca_dev; |
ac3d5bfe | 396 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
c109f816 | 397 | |
ac3d5bfe LK |
398 | *val = sens_priv->settings[BRIGHTNESS_IDX]; |
399 | return 0; | |
c109f816 EA |
400 | } |
401 | ||
cf811d50 | 402 | static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) |
c109f816 EA |
403 | { |
404 | int err; | |
405 | u8 data[1]; | |
406 | struct sd *sd = (struct sd *) gspca_dev; | |
ac3d5bfe | 407 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
c109f816 | 408 | |
ac3d5bfe | 409 | sens_priv->settings[BRIGHTNESS_IDX] = val; |
c109f816 | 410 | data[0] = val; |
ac3d5bfe | 411 | err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1); |
7b2cd079 | 412 | return err; |
c109f816 EA |
413 | } |
414 | ||
cf811d50 | 415 | static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) |
c109f816 | 416 | { |
c109f816 | 417 | struct sd *sd = (struct sd *) gspca_dev; |
ac3d5bfe | 418 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
c109f816 | 419 | |
ac3d5bfe LK |
420 | *val = sens_priv->settings[EXPOSURE_IDX]; |
421 | return 0; | |
c109f816 EA |
422 | } |
423 | ||
cf811d50 | 424 | static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val) |
c109f816 | 425 | { |
18f8fae1 | 426 | int err; |
c109f816 EA |
427 | u8 data[2]; |
428 | struct sd *sd = (struct sd *) gspca_dev; | |
ac3d5bfe | 429 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
c109f816 | 430 | |
ac3d5bfe | 431 | sens_priv->settings[EXPOSURE_IDX] = val; |
c109f816 EA |
432 | data[0] = 0; |
433 | data[1] = val; | |
ac3d5bfe | 434 | err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2); |
7b2cd079 | 435 | return err; |
c109f816 | 436 | } |
18f8fae1 | 437 | |
cf811d50 | 438 | static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) |
18f8fae1 | 439 | { |
18f8fae1 | 440 | struct sd *sd = (struct sd *) gspca_dev; |
ac3d5bfe | 441 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
18f8fae1 | 442 | |
ac3d5bfe LK |
443 | *val = sens_priv->settings[VFLIP_IDX]; |
444 | return 0; | |
18f8fae1 EA |
445 | } |
446 | ||
57851d0c EA |
447 | static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, |
448 | __s32 vflip, __s32 hflip) | |
18f8fae1 EA |
449 | { |
450 | int err; | |
451 | u8 data[1]; | |
452 | struct sd *sd = (struct sd *) gspca_dev; | |
453 | ||
454 | data[0] = 0x05; | |
6dc4cff0 | 455 | err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); |
18f8fae1 | 456 | if (err < 0) |
051781b3 | 457 | return err; |
18f8fae1 | 458 | |
ac3d5bfe LK |
459 | /* six bit is vflip, seven is hflip */ |
460 | data[0] = S5K83A_FLIP_MASK; | |
461 | data[0] = (vflip) ? data[0] | 0x40 : data[0]; | |
462 | data[0] = (hflip) ? data[0] | 0x80 : data[0]; | |
18f8fae1 | 463 | |
6dc4cff0 | 464 | err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1); |
18f8fae1 | 465 | if (err < 0) |
051781b3 | 466 | return err; |
18f8fae1 | 467 | |
ac3d5bfe | 468 | data[0] = (vflip) ? 0x0b : 0x0a; |
6dc4cff0 | 469 | err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1); |
ac3d5bfe LK |
470 | if (err < 0) |
471 | return err; | |
18f8fae1 | 472 | |
ac3d5bfe LK |
473 | data[0] = (hflip) ? 0x0a : 0x0b; |
474 | err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1); | |
7b2cd079 | 475 | return err; |
18f8fae1 EA |
476 | } |
477 | ||
cf811d50 | 478 | static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) |
18f8fae1 EA |
479 | { |
480 | int err; | |
ac3d5bfe LK |
481 | u8 reg; |
482 | __s32 hflip; | |
18f8fae1 | 483 | struct sd *sd = (struct sd *) gspca_dev; |
ac3d5bfe | 484 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
18f8fae1 | 485 | |
ac3d5bfe LK |
486 | sens_priv->settings[VFLIP_IDX] = val; |
487 | ||
488 | s5k83a_get_hflip(gspca_dev, &hflip); | |
489 | ||
490 | err = s5k83a_get_rotation(sd, ®); | |
18f8fae1 | 491 | if (err < 0) |
051781b3 | 492 | return err; |
ac3d5bfe LK |
493 | if (reg) { |
494 | val = !val; | |
495 | hflip = !hflip; | |
496 | } | |
18f8fae1 | 497 | |
ac3d5bfe | 498 | err = s5k83a_set_flip_real(gspca_dev, val, hflip); |
7b2cd079 | 499 | return err; |
18f8fae1 EA |
500 | } |
501 | ||
cf811d50 | 502 | static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) |
ac3d5bfe LK |
503 | { |
504 | struct sd *sd = (struct sd *) gspca_dev; | |
505 | struct s5k83a_priv *sens_priv = sd->sensor_priv; | |
506 | ||
507 | *val = sens_priv->settings[HFLIP_IDX]; | |
508 | return 0; | |
509 | } | |
510 | ||
cf811d50 | 511 | static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val) |
18f8fae1 EA |
512 | { |
513 | int err; | |
ac3d5bfe LK |
514 | u8 reg; |
515 | __s32 vflip; | |
18f8fae1 | 516 | struct sd *sd = (struct sd *) gspca_dev; |
ac3d5bfe | 517 | struct s5k83a_priv *sens_priv = sd->sensor_priv; |
18f8fae1 | 518 | |
ac3d5bfe | 519 | sens_priv->settings[HFLIP_IDX] = val; |
18f8fae1 | 520 | |
ac3d5bfe | 521 | s5k83a_get_vflip(gspca_dev, &vflip); |
18f8fae1 | 522 | |
ac3d5bfe | 523 | err = s5k83a_get_rotation(sd, ®); |
18f8fae1 | 524 | if (err < 0) |
051781b3 | 525 | return err; |
ac3d5bfe LK |
526 | if (reg) { |
527 | val = !val; | |
528 | vflip = !vflip; | |
529 | } | |
18f8fae1 | 530 | |
ac3d5bfe | 531 | err = s5k83a_set_flip_real(gspca_dev, vflip, val); |
7b2cd079 | 532 | return err; |
18f8fae1 | 533 | } |
4a7581f0 | 534 | |
ac3d5bfe | 535 | static int s5k83a_set_led_indication(struct sd *sd, u8 val) |
4a7581f0 LK |
536 | { |
537 | int err = 0; | |
538 | u8 data[1]; | |
539 | ||
540 | err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data); | |
541 | if (err < 0) | |
542 | return err; | |
543 | ||
544 | if (val) | |
545 | data[0] = data[0] | S5K83A_GPIO_LED_MASK; | |
546 | else | |
547 | data[0] = data[0] & ~S5K83A_GPIO_LED_MASK; | |
548 | ||
549 | err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]); | |
550 | ||
57851d0c | 551 | return err; |
4a7581f0 | 552 | } |
ac3d5bfe LK |
553 | |
554 | /* Get camera rotation on Acer notebooks */ | |
555 | static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data) | |
556 | { | |
557 | int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data); | |
558 | *reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1; | |
559 | return err; | |
560 | } | |
561 | ||
562 | static void s5k83a_dump_registers(struct sd *sd) | |
563 | { | |
564 | int address; | |
565 | u8 page, old_page; | |
566 | m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); | |
567 | ||
568 | for (page = 0; page < 16; page++) { | |
569 | m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); | |
570 | info("Dumping the s5k83a register state for page 0x%x", page); | |
571 | for (address = 0; address <= 0xff; address++) { | |
572 | u8 val = 0; | |
573 | m5602_read_sensor(sd, address, &val, 1); | |
574 | info("register 0x%x contains 0x%x", | |
575 | address, val); | |
576 | } | |
577 | } | |
578 | info("s5k83a register state dump complete"); | |
579 | ||
580 | for (page = 0; page < 16; page++) { | |
581 | m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); | |
582 | info("Probing for which registers that are read/write " | |
583 | "for page 0x%x", page); | |
584 | for (address = 0; address <= 0xff; address++) { | |
585 | u8 old_val, ctrl_val, test_val = 0xff; | |
586 | ||
587 | m5602_read_sensor(sd, address, &old_val, 1); | |
588 | m5602_write_sensor(sd, address, &test_val, 1); | |
589 | m5602_read_sensor(sd, address, &ctrl_val, 1); | |
590 | ||
591 | if (ctrl_val == test_val) | |
592 | info("register 0x%x is writeable", address); | |
593 | else | |
594 | info("register 0x%x is read only", address); | |
595 | ||
596 | /* Restore original val */ | |
597 | m5602_write_sensor(sd, address, &old_val, 1); | |
598 | } | |
599 | } | |
600 | info("Read/write register probing complete"); | |
601 | m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); | |
602 | } |