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