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