1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2008 Sensoray Company Inc. |
4 | */ |
5 | |
6 | #include <linux/module.h> |
7 | #include <linux/usb.h> |
8 | #include <linux/i2c.h> |
9 | #include <linux/videodev2.h> |
10 | #include <linux/slab.h> |
11 | #include <media/v4l2-device.h> |
12 | #include <media/v4l2-common.h> |
13 | #include <media/v4l2-subdev.h> |
14 | #include "go7007-priv.h" |
15 | |
16 | MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver" ); |
17 | MODULE_LICENSE("GPL v2" ); |
18 | |
19 | /* |
20 | * Note: this board has two i2c devices: a vpx3226f and a tlv320aic23b. |
21 | * Due to the unusual way these are accessed on this device we do not |
22 | * reuse the i2c drivers, but instead they are implemented in this |
23 | * driver. It would be nice to improve on this, though. |
24 | */ |
25 | |
26 | #define TLV320_ADDRESS 0x34 |
27 | #define VPX322_ADDR_ANALOGCONTROL1 0x02 |
28 | #define VPX322_ADDR_BRIGHTNESS0 0x0127 |
29 | #define VPX322_ADDR_BRIGHTNESS1 0x0131 |
30 | #define VPX322_ADDR_CONTRAST0 0x0128 |
31 | #define VPX322_ADDR_CONTRAST1 0x0132 |
32 | #define VPX322_ADDR_HUE 0x00dc |
33 | #define VPX322_ADDR_SAT 0x0030 |
34 | |
35 | struct go7007_usb_board { |
36 | unsigned int flags; |
37 | struct go7007_board_info main_info; |
38 | }; |
39 | |
40 | struct go7007_usb { |
41 | struct go7007_usb_board *board; |
42 | struct mutex i2c_lock; |
43 | struct usb_device *usbdev; |
44 | struct urb *video_urbs[8]; |
45 | struct urb *audio_urbs[8]; |
46 | struct urb *intr_urb; |
47 | }; |
48 | |
49 | static unsigned char aud_regs[] = { |
50 | 0x1e, 0x00, |
51 | 0x00, 0x17, |
52 | 0x02, 0x17, |
53 | 0x04, 0xf9, |
54 | 0x06, 0xf9, |
55 | 0x08, 0x02, |
56 | 0x0a, 0x00, |
57 | 0x0c, 0x00, |
58 | 0x0a, 0x00, |
59 | 0x0c, 0x00, |
60 | 0x0e, 0x02, |
61 | 0x10, 0x00, |
62 | 0x12, 0x01, |
63 | 0x00, 0x00, |
64 | }; |
65 | |
66 | |
67 | static unsigned char vid_regs[] = { |
68 | 0xF2, 0x0f, |
69 | 0xAA, 0x00, |
70 | 0xF8, 0xff, |
71 | 0x00, 0x00, |
72 | }; |
73 | |
74 | static u16 vid_regs_fp[] = { |
75 | 0x028, 0x067, |
76 | 0x120, 0x016, |
77 | 0x121, 0xcF2, |
78 | 0x122, 0x0F2, |
79 | 0x123, 0x00c, |
80 | 0x124, 0x2d0, |
81 | 0x125, 0x2e0, |
82 | 0x126, 0x004, |
83 | 0x128, 0x1E0, |
84 | 0x12A, 0x016, |
85 | 0x12B, 0x0F2, |
86 | 0x12C, 0x0F2, |
87 | 0x12D, 0x00c, |
88 | 0x12E, 0x2d0, |
89 | 0x12F, 0x2e0, |
90 | 0x130, 0x004, |
91 | 0x132, 0x1E0, |
92 | 0x140, 0x060, |
93 | 0x153, 0x00C, |
94 | 0x154, 0x200, |
95 | 0x150, 0x801, |
96 | 0x000, 0x000 |
97 | }; |
98 | |
99 | /* PAL specific values */ |
100 | static u16 vid_regs_fp_pal[] = { |
101 | 0x120, 0x017, |
102 | 0x121, 0xd22, |
103 | 0x122, 0x122, |
104 | 0x12A, 0x017, |
105 | 0x12B, 0x122, |
106 | 0x12C, 0x122, |
107 | 0x140, 0x060, |
108 | 0x000, 0x000, |
109 | }; |
110 | |
111 | struct s2250 { |
112 | struct v4l2_subdev sd; |
113 | struct v4l2_ctrl_handler hdl; |
114 | v4l2_std_id std; |
115 | int input; |
116 | int brightness; |
117 | int contrast; |
118 | int saturation; |
119 | int hue; |
120 | int reg12b_val; |
121 | int audio_input; |
122 | struct i2c_client *audio; |
123 | }; |
124 | |
125 | static inline struct s2250 *to_state(struct v4l2_subdev *sd) |
126 | { |
127 | return container_of(sd, struct s2250, sd); |
128 | } |
129 | |
130 | /* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ |
131 | static int go7007_usb_vendor_request(struct go7007 *go, u16 request, |
132 | u16 value, u16 index, void *transfer_buffer, int length, int in) |
133 | { |
134 | struct go7007_usb *usb = go->hpi_context; |
135 | int timeout = 5000; |
136 | |
137 | if (in) { |
138 | return usb_control_msg(dev: usb->usbdev, |
139 | usb_rcvctrlpipe(usb->usbdev, 0), request, |
140 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
141 | value, index, data: transfer_buffer, size: length, timeout); |
142 | } else { |
143 | return usb_control_msg(dev: usb->usbdev, |
144 | usb_sndctrlpipe(usb->usbdev, 0), request, |
145 | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
146 | value, index, data: transfer_buffer, size: length, timeout); |
147 | } |
148 | } |
149 | /* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ |
150 | |
151 | static int write_reg(struct i2c_client *client, u8 reg, u8 value) |
152 | { |
153 | struct go7007 *go = i2c_get_adapdata(adap: client->adapter); |
154 | struct go7007_usb *usb; |
155 | int rc; |
156 | int dev_addr = client->addr << 1; /* firmware wants 8-bit address */ |
157 | u8 *buf; |
158 | |
159 | if (go == NULL) |
160 | return -ENODEV; |
161 | |
162 | if (go->status == STATUS_SHUTDOWN) |
163 | return -EBUSY; |
164 | |
165 | buf = kzalloc(size: 16, GFP_KERNEL); |
166 | if (buf == NULL) |
167 | return -ENOMEM; |
168 | |
169 | usb = go->hpi_context; |
170 | if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { |
171 | dev_info(&client->dev, "i2c lock failed\n" ); |
172 | kfree(objp: buf); |
173 | return -EINTR; |
174 | } |
175 | rc = go7007_usb_vendor_request(go, request: 0x55, value: dev_addr, |
176 | index: (reg<<8 | value), |
177 | transfer_buffer: buf, |
178 | length: 16, in: 1); |
179 | |
180 | mutex_unlock(lock: &usb->i2c_lock); |
181 | kfree(objp: buf); |
182 | return rc; |
183 | } |
184 | |
185 | static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) |
186 | { |
187 | struct go7007 *go = i2c_get_adapdata(adap: client->adapter); |
188 | struct go7007_usb *usb; |
189 | int rc; |
190 | u8 *buf; |
191 | struct s2250 *dec = i2c_get_clientdata(client); |
192 | |
193 | if (go == NULL) |
194 | return -ENODEV; |
195 | |
196 | if (go->status == STATUS_SHUTDOWN) |
197 | return -EBUSY; |
198 | |
199 | buf = kzalloc(size: 16, GFP_KERNEL); |
200 | |
201 | if (buf == NULL) |
202 | return -ENOMEM; |
203 | |
204 | |
205 | |
206 | memset(buf, 0xcd, 6); |
207 | |
208 | usb = go->hpi_context; |
209 | if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { |
210 | dev_info(&client->dev, "i2c lock failed\n" ); |
211 | kfree(objp: buf); |
212 | return -EINTR; |
213 | } |
214 | rc = go7007_usb_vendor_request(go, request: 0x57, value: addr, index: val, transfer_buffer: buf, length: 16, in: 1); |
215 | mutex_unlock(lock: &usb->i2c_lock); |
216 | if (rc < 0) { |
217 | kfree(objp: buf); |
218 | return rc; |
219 | } |
220 | |
221 | if (buf[0] == 0) { |
222 | unsigned int subaddr, val_read; |
223 | |
224 | subaddr = (buf[4] << 8) + buf[5]; |
225 | val_read = (buf[2] << 8) + buf[3]; |
226 | kfree(objp: buf); |
227 | if (val_read != val) { |
228 | dev_info(&client->dev, "invalid fp write %x %x\n" , |
229 | val_read, val); |
230 | return -EFAULT; |
231 | } |
232 | if (subaddr != addr) { |
233 | dev_info(&client->dev, "invalid fp write addr %x %x\n" , |
234 | subaddr, addr); |
235 | return -EFAULT; |
236 | } |
237 | } else { |
238 | kfree(objp: buf); |
239 | return -EFAULT; |
240 | } |
241 | |
242 | /* save last 12b value */ |
243 | if (addr == 0x12b) |
244 | dec->reg12b_val = val; |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) |
250 | { |
251 | struct go7007 *go = i2c_get_adapdata(adap: client->adapter); |
252 | struct go7007_usb *usb; |
253 | int rc; |
254 | u8 *buf; |
255 | |
256 | if (go == NULL) |
257 | return -ENODEV; |
258 | |
259 | if (go->status == STATUS_SHUTDOWN) |
260 | return -EBUSY; |
261 | |
262 | buf = kzalloc(size: 16, GFP_KERNEL); |
263 | |
264 | if (buf == NULL) |
265 | return -ENOMEM; |
266 | |
267 | |
268 | |
269 | memset(buf, 0xcd, 6); |
270 | usb = go->hpi_context; |
271 | if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { |
272 | dev_info(&client->dev, "i2c lock failed\n" ); |
273 | kfree(objp: buf); |
274 | return -EINTR; |
275 | } |
276 | rc = go7007_usb_vendor_request(go, request: 0x58, value: addr, index: 0, transfer_buffer: buf, length: 16, in: 1); |
277 | mutex_unlock(lock: &usb->i2c_lock); |
278 | if (rc < 0) { |
279 | kfree(objp: buf); |
280 | return rc; |
281 | } |
282 | |
283 | *val = (buf[0] << 8) | buf[1]; |
284 | kfree(objp: buf); |
285 | |
286 | return 0; |
287 | } |
288 | |
289 | |
290 | static int write_regs(struct i2c_client *client, u8 *regs) |
291 | { |
292 | int i; |
293 | |
294 | for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { |
295 | if (write_reg(client, reg: regs[i], value: regs[i+1]) < 0) { |
296 | dev_info(&client->dev, "failed\n" ); |
297 | return -1; |
298 | } |
299 | } |
300 | return 0; |
301 | } |
302 | |
303 | static int write_regs_fp(struct i2c_client *client, u16 *regs) |
304 | { |
305 | int i; |
306 | |
307 | for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { |
308 | if (write_reg_fp(client, addr: regs[i], val: regs[i+1]) < 0) { |
309 | dev_info(&client->dev, "failed fp\n" ); |
310 | return -1; |
311 | } |
312 | } |
313 | return 0; |
314 | } |
315 | |
316 | |
317 | /* ------------------------------------------------------------------------- */ |
318 | |
319 | static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, |
320 | u32 config) |
321 | { |
322 | struct s2250 *state = to_state(sd); |
323 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
324 | int vidsys; |
325 | |
326 | vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00; |
327 | if (input == 0) { |
328 | /* composite */ |
329 | write_reg_fp(client, addr: 0x20, val: 0x020 | vidsys); |
330 | write_reg_fp(client, addr: 0x21, val: 0x662); |
331 | write_reg_fp(client, addr: 0x140, val: 0x060); |
332 | } else if (input == 1) { |
333 | /* S-Video */ |
334 | write_reg_fp(client, addr: 0x20, val: 0x040 | vidsys); |
335 | write_reg_fp(client, addr: 0x21, val: 0x666); |
336 | write_reg_fp(client, addr: 0x140, val: 0x060); |
337 | } else { |
338 | return -EINVAL; |
339 | } |
340 | state->input = input; |
341 | return 0; |
342 | } |
343 | |
344 | static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) |
345 | { |
346 | struct s2250 *state = to_state(sd); |
347 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
348 | u16 vidsource; |
349 | |
350 | vidsource = (state->input == 1) ? 0x040 : 0x020; |
351 | if (norm & V4L2_STD_625_50) { |
352 | write_regs_fp(client, regs: vid_regs_fp); |
353 | write_regs_fp(client, regs: vid_regs_fp_pal); |
354 | write_reg_fp(client, addr: 0x20, val: vidsource); |
355 | } else { |
356 | write_regs_fp(client, regs: vid_regs_fp); |
357 | write_reg_fp(client, addr: 0x20, val: vidsource | 1); |
358 | } |
359 | state->std = norm; |
360 | return 0; |
361 | } |
362 | |
363 | static int s2250_s_ctrl(struct v4l2_ctrl *ctrl) |
364 | { |
365 | struct s2250 *state = container_of(ctrl->handler, struct s2250, hdl); |
366 | struct i2c_client *client = v4l2_get_subdevdata(sd: &state->sd); |
367 | u16 oldvalue; |
368 | |
369 | switch (ctrl->id) { |
370 | case V4L2_CID_BRIGHTNESS: |
371 | read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, val: &oldvalue); |
372 | write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, |
373 | val: ctrl->val | (oldvalue & ~0xff)); |
374 | read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, val: &oldvalue); |
375 | write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, |
376 | val: ctrl->val | (oldvalue & ~0xff)); |
377 | write_reg_fp(client, addr: 0x140, val: 0x60); |
378 | break; |
379 | case V4L2_CID_CONTRAST: |
380 | read_reg_fp(client, VPX322_ADDR_CONTRAST0, val: &oldvalue); |
381 | write_reg_fp(client, VPX322_ADDR_CONTRAST0, |
382 | val: ctrl->val | (oldvalue & ~0x3f)); |
383 | read_reg_fp(client, VPX322_ADDR_CONTRAST1, val: &oldvalue); |
384 | write_reg_fp(client, VPX322_ADDR_CONTRAST1, |
385 | val: ctrl->val | (oldvalue & ~0x3f)); |
386 | write_reg_fp(client, addr: 0x140, val: 0x60); |
387 | break; |
388 | case V4L2_CID_SATURATION: |
389 | write_reg_fp(client, VPX322_ADDR_SAT, val: ctrl->val); |
390 | break; |
391 | case V4L2_CID_HUE: |
392 | write_reg_fp(client, VPX322_ADDR_HUE, val: ctrl->val); |
393 | break; |
394 | default: |
395 | return -EINVAL; |
396 | } |
397 | return 0; |
398 | } |
399 | |
400 | static int s2250_set_fmt(struct v4l2_subdev *sd, |
401 | struct v4l2_subdev_state *sd_state, |
402 | struct v4l2_subdev_format *format) |
403 | { |
404 | struct v4l2_mbus_framefmt *fmt = &format->format; |
405 | struct s2250 *state = to_state(sd); |
406 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
407 | |
408 | if (format->pad) |
409 | return -EINVAL; |
410 | |
411 | if (format->which == V4L2_SUBDEV_FORMAT_TRY) |
412 | return 0; |
413 | |
414 | if (fmt->height < 640) { |
415 | write_reg_fp(client, addr: 0x12b, val: state->reg12b_val | 0x400); |
416 | write_reg_fp(client, addr: 0x140, val: 0x060); |
417 | } else { |
418 | write_reg_fp(client, addr: 0x12b, val: state->reg12b_val & ~0x400); |
419 | write_reg_fp(client, addr: 0x140, val: 0x060); |
420 | } |
421 | return 0; |
422 | } |
423 | |
424 | static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, |
425 | u32 config) |
426 | { |
427 | struct s2250 *state = to_state(sd); |
428 | |
429 | switch (input) { |
430 | case 0: |
431 | write_reg(client: state->audio, reg: 0x08, value: 0x02); /* Line In */ |
432 | break; |
433 | case 1: |
434 | write_reg(client: state->audio, reg: 0x08, value: 0x04); /* Mic */ |
435 | break; |
436 | case 2: |
437 | write_reg(client: state->audio, reg: 0x08, value: 0x05); /* Mic Boost */ |
438 | break; |
439 | default: |
440 | return -EINVAL; |
441 | } |
442 | state->audio_input = input; |
443 | return 0; |
444 | } |
445 | |
446 | |
447 | static int s2250_log_status(struct v4l2_subdev *sd) |
448 | { |
449 | struct s2250 *state = to_state(sd); |
450 | |
451 | v4l2_info(sd, "Standard: %s\n" , state->std == V4L2_STD_NTSC ? "NTSC" : |
452 | state->std == V4L2_STD_PAL ? "PAL" : |
453 | state->std == V4L2_STD_SECAM ? "SECAM" : |
454 | "unknown" ); |
455 | v4l2_info(sd, "Input: %s\n" , state->input == 0 ? "Composite" : |
456 | state->input == 1 ? "S-video" : |
457 | "error" ); |
458 | v4l2_info(sd, "Audio input: %s\n" , state->audio_input == 0 ? "Line In" : |
459 | state->audio_input == 1 ? "Mic" : |
460 | state->audio_input == 2 ? "Mic Boost" : |
461 | "error" ); |
462 | return v4l2_ctrl_subdev_log_status(sd); |
463 | } |
464 | |
465 | /* --------------------------------------------------------------------------*/ |
466 | |
467 | static const struct v4l2_ctrl_ops s2250_ctrl_ops = { |
468 | .s_ctrl = s2250_s_ctrl, |
469 | }; |
470 | |
471 | static const struct v4l2_subdev_core_ops s2250_core_ops = { |
472 | .log_status = s2250_log_status, |
473 | }; |
474 | |
475 | static const struct v4l2_subdev_audio_ops s2250_audio_ops = { |
476 | .s_routing = s2250_s_audio_routing, |
477 | }; |
478 | |
479 | static const struct v4l2_subdev_video_ops s2250_video_ops = { |
480 | .s_std = s2250_s_std, |
481 | .s_routing = s2250_s_video_routing, |
482 | }; |
483 | |
484 | static const struct v4l2_subdev_pad_ops s2250_pad_ops = { |
485 | .set_fmt = s2250_set_fmt, |
486 | }; |
487 | |
488 | static const struct v4l2_subdev_ops s2250_ops = { |
489 | .core = &s2250_core_ops, |
490 | .audio = &s2250_audio_ops, |
491 | .video = &s2250_video_ops, |
492 | .pad = &s2250_pad_ops, |
493 | }; |
494 | |
495 | /* --------------------------------------------------------------------------*/ |
496 | |
497 | static int s2250_probe(struct i2c_client *client) |
498 | { |
499 | struct i2c_client *audio; |
500 | struct i2c_adapter *adapter = client->adapter; |
501 | struct s2250 *state; |
502 | struct v4l2_subdev *sd; |
503 | u8 *data; |
504 | struct go7007 *go = i2c_get_adapdata(adap: adapter); |
505 | struct go7007_usb *usb = go->hpi_context; |
506 | int err = -EIO; |
507 | |
508 | audio = i2c_new_dummy_device(adapter, TLV320_ADDRESS >> 1); |
509 | if (IS_ERR(ptr: audio)) |
510 | return PTR_ERR(ptr: audio); |
511 | |
512 | state = kzalloc(size: sizeof(struct s2250), GFP_KERNEL); |
513 | if (state == NULL) { |
514 | i2c_unregister_device(client: audio); |
515 | return -ENOMEM; |
516 | } |
517 | |
518 | sd = &state->sd; |
519 | v4l2_i2c_subdev_init(sd, client, ops: &s2250_ops); |
520 | |
521 | v4l2_info(sd, "initializing %s at address 0x%x on %s\n" , |
522 | "Sensoray 2250/2251" , client->addr, client->adapter->name); |
523 | |
524 | v4l2_ctrl_handler_init(&state->hdl, 4); |
525 | v4l2_ctrl_new_std(hdl: &state->hdl, ops: &s2250_ctrl_ops, |
526 | V4L2_CID_BRIGHTNESS, min: -128, max: 127, step: 1, def: 0); |
527 | v4l2_ctrl_new_std(hdl: &state->hdl, ops: &s2250_ctrl_ops, |
528 | V4L2_CID_CONTRAST, min: 0, max: 0x3f, step: 1, def: 0x32); |
529 | v4l2_ctrl_new_std(hdl: &state->hdl, ops: &s2250_ctrl_ops, |
530 | V4L2_CID_SATURATION, min: 0, max: 4094, step: 1, def: 2070); |
531 | v4l2_ctrl_new_std(hdl: &state->hdl, ops: &s2250_ctrl_ops, |
532 | V4L2_CID_HUE, min: -512, max: 511, step: 1, def: 0); |
533 | sd->ctrl_handler = &state->hdl; |
534 | if (state->hdl.error) { |
535 | err = state->hdl.error; |
536 | goto fail; |
537 | } |
538 | |
539 | state->std = V4L2_STD_NTSC; |
540 | state->brightness = 50; |
541 | state->contrast = 50; |
542 | state->saturation = 50; |
543 | state->hue = 0; |
544 | state->audio = audio; |
545 | |
546 | /* initialize the audio */ |
547 | if (write_regs(client: audio, regs: aud_regs) < 0) { |
548 | dev_err(&client->dev, "error initializing audio\n" ); |
549 | goto fail; |
550 | } |
551 | |
552 | if (write_regs(client, regs: vid_regs) < 0) { |
553 | dev_err(&client->dev, "error initializing decoder\n" ); |
554 | goto fail; |
555 | } |
556 | if (write_regs_fp(client, regs: vid_regs_fp) < 0) { |
557 | dev_err(&client->dev, "error initializing decoder\n" ); |
558 | goto fail; |
559 | } |
560 | /* set default channel */ |
561 | /* composite */ |
562 | write_reg_fp(client, addr: 0x20, val: 0x020 | 1); |
563 | write_reg_fp(client, addr: 0x21, val: 0x662); |
564 | write_reg_fp(client, addr: 0x140, val: 0x060); |
565 | |
566 | /* set default audio input */ |
567 | state->audio_input = 0; |
568 | write_reg(client, reg: 0x08, value: 0x02); /* Line In */ |
569 | |
570 | if (mutex_lock_interruptible(&usb->i2c_lock) == 0) { |
571 | data = kzalloc(size: 16, GFP_KERNEL); |
572 | if (data != NULL) { |
573 | int rc = go7007_usb_vendor_request(go, request: 0x41, value: 0, index: 0, |
574 | transfer_buffer: data, length: 16, in: 1); |
575 | |
576 | if (rc > 0) { |
577 | u8 mask; |
578 | |
579 | data[0] = 0; |
580 | mask = 1<<5; |
581 | data[0] &= ~mask; |
582 | data[1] |= mask; |
583 | go7007_usb_vendor_request(go, request: 0x40, value: 0, |
584 | index: (data[1]<<8) |
585 | + data[1], |
586 | transfer_buffer: data, length: 16, in: 0); |
587 | } |
588 | kfree(objp: data); |
589 | } |
590 | mutex_unlock(lock: &usb->i2c_lock); |
591 | } |
592 | |
593 | v4l2_info(sd, "initialized successfully\n" ); |
594 | return 0; |
595 | |
596 | fail: |
597 | i2c_unregister_device(client: audio); |
598 | v4l2_ctrl_handler_free(hdl: &state->hdl); |
599 | kfree(objp: state); |
600 | return err; |
601 | } |
602 | |
603 | static void s2250_remove(struct i2c_client *client) |
604 | { |
605 | struct s2250 *state = to_state(sd: i2c_get_clientdata(client)); |
606 | |
607 | i2c_unregister_device(client: state->audio); |
608 | v4l2_device_unregister_subdev(sd: &state->sd); |
609 | v4l2_ctrl_handler_free(hdl: &state->hdl); |
610 | kfree(objp: state); |
611 | } |
612 | |
613 | static const struct i2c_device_id s2250_id[] = { |
614 | { "s2250" , 0 }, |
615 | { } |
616 | }; |
617 | MODULE_DEVICE_TABLE(i2c, s2250_id); |
618 | |
619 | static struct i2c_driver s2250_driver = { |
620 | .driver = { |
621 | .name = "s2250" , |
622 | }, |
623 | .probe = s2250_probe, |
624 | .remove = s2250_remove, |
625 | .id_table = s2250_id, |
626 | }; |
627 | |
628 | module_i2c_driver(s2250_driver); |
629 | |