Commit | Line | Data |
---|---|---|
5cebaac6 MR |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * video-i2c.c - Support for I2C transport video devices | |
4 | * | |
5 | * Copyright (C) 2018 Matt Ranostay <matt.ranostay@konsulko.com> | |
6 | * | |
7 | * Supported: | |
8 | * - Panasonic AMG88xx Grid-Eye Sensors | |
9 | */ | |
10 | ||
11 | #include <linux/delay.h> | |
12 | #include <linux/freezer.h> | |
acbea679 | 13 | #include <linux/hwmon.h> |
5cebaac6 MR |
14 | #include <linux/kthread.h> |
15 | #include <linux/i2c.h> | |
16 | #include <linux/list.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/mutex.h> | |
19 | #include <linux/of_device.h> | |
20 | #include <linux/sched.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/videodev2.h> | |
23 | #include <media/v4l2-common.h> | |
24 | #include <media/v4l2-device.h> | |
25 | #include <media/v4l2-event.h> | |
26 | #include <media/v4l2-fh.h> | |
27 | #include <media/v4l2-ioctl.h> | |
28 | #include <media/videobuf2-v4l2.h> | |
29 | #include <media/videobuf2-vmalloc.h> | |
30 | ||
31 | #define VIDEO_I2C_DRIVER "video-i2c" | |
32 | ||
33 | struct video_i2c_chip; | |
34 | ||
35 | struct video_i2c_buffer { | |
36 | struct vb2_v4l2_buffer vb; | |
37 | struct list_head list; | |
38 | }; | |
39 | ||
40 | struct video_i2c_data { | |
41 | struct i2c_client *client; | |
42 | const struct video_i2c_chip *chip; | |
43 | struct mutex lock; | |
44 | spinlock_t slock; | |
45 | unsigned int sequence; | |
46 | struct mutex queue_lock; | |
47 | ||
48 | struct v4l2_device v4l2_dev; | |
49 | struct video_device vdev; | |
50 | struct vb2_queue vb_vidq; | |
51 | ||
52 | struct task_struct *kthread_vid_cap; | |
53 | struct list_head vid_cap_active; | |
54 | }; | |
55 | ||
1b3d5f2a | 56 | static const struct v4l2_fmtdesc amg88xx_format = { |
5cebaac6 MR |
57 | .pixelformat = V4L2_PIX_FMT_Y12, |
58 | }; | |
59 | ||
1b3d5f2a | 60 | static const struct v4l2_frmsize_discrete amg88xx_size = { |
5cebaac6 MR |
61 | .width = 8, |
62 | .height = 8, | |
63 | }; | |
64 | ||
65 | struct video_i2c_chip { | |
66 | /* video dimensions */ | |
67 | const struct v4l2_fmtdesc *format; | |
68 | const struct v4l2_frmsize_discrete *size; | |
69 | ||
70 | /* max frames per second */ | |
71 | unsigned int max_fps; | |
72 | ||
73 | /* pixel buffer size */ | |
74 | unsigned int buffer_size; | |
75 | ||
76 | /* pixel size in bits */ | |
77 | unsigned int bpp; | |
78 | ||
79 | /* xfer function */ | |
80 | int (*xfer)(struct video_i2c_data *data, char *buf); | |
acbea679 MR |
81 | |
82 | /* hwmon init function */ | |
83 | int (*hwmon_init)(struct video_i2c_data *data); | |
5cebaac6 MR |
84 | }; |
85 | ||
86 | static int amg88xx_xfer(struct video_i2c_data *data, char *buf) | |
87 | { | |
88 | struct i2c_client *client = data->client; | |
89 | struct i2c_msg msg[2]; | |
90 | u8 reg = 0x80; | |
91 | int ret; | |
92 | ||
93 | msg[0].addr = client->addr; | |
94 | msg[0].flags = 0; | |
95 | msg[0].len = 1; | |
96 | msg[0].buf = (char *)® | |
97 | ||
98 | msg[1].addr = client->addr; | |
99 | msg[1].flags = I2C_M_RD; | |
100 | msg[1].len = data->chip->buffer_size; | |
101 | msg[1].buf = (char *)buf; | |
102 | ||
103 | ret = i2c_transfer(client->adapter, msg, 2); | |
104 | ||
105 | return (ret == 2) ? 0 : -EIO; | |
106 | } | |
107 | ||
acbea679 MR |
108 | #if IS_ENABLED(CONFIG_HWMON) |
109 | ||
110 | static const u32 amg88xx_temp_config[] = { | |
111 | HWMON_T_INPUT, | |
112 | 0 | |
113 | }; | |
114 | ||
115 | static const struct hwmon_channel_info amg88xx_temp = { | |
116 | .type = hwmon_temp, | |
117 | .config = amg88xx_temp_config, | |
118 | }; | |
119 | ||
120 | static const struct hwmon_channel_info *amg88xx_info[] = { | |
121 | &amg88xx_temp, | |
122 | NULL | |
123 | }; | |
124 | ||
125 | static umode_t amg88xx_is_visible(const void *drvdata, | |
126 | enum hwmon_sensor_types type, | |
127 | u32 attr, int channel) | |
128 | { | |
129 | return 0444; | |
130 | } | |
131 | ||
132 | static int amg88xx_read(struct device *dev, enum hwmon_sensor_types type, | |
133 | u32 attr, int channel, long *val) | |
134 | { | |
135 | struct video_i2c_data *data = dev_get_drvdata(dev); | |
136 | struct i2c_client *client = data->client; | |
137 | int tmp = i2c_smbus_read_word_data(client, 0x0e); | |
138 | ||
139 | if (tmp < 0) | |
140 | return tmp; | |
141 | ||
142 | /* | |
143 | * Check for sign bit, this isn't a two's complement value but an | |
144 | * absolute temperature that needs to be inverted in the case of being | |
145 | * negative. | |
146 | */ | |
147 | if (tmp & BIT(11)) | |
148 | tmp = -(tmp & 0x7ff); | |
149 | ||
150 | *val = (tmp * 625) / 10; | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | static const struct hwmon_ops amg88xx_hwmon_ops = { | |
156 | .is_visible = amg88xx_is_visible, | |
157 | .read = amg88xx_read, | |
158 | }; | |
159 | ||
160 | static const struct hwmon_chip_info amg88xx_chip_info = { | |
161 | .ops = &amg88xx_hwmon_ops, | |
162 | .info = amg88xx_info, | |
163 | }; | |
164 | ||
165 | static int amg88xx_hwmon_init(struct video_i2c_data *data) | |
166 | { | |
167 | void *hwmon = devm_hwmon_device_register_with_info(&data->client->dev, | |
168 | "amg88xx", data, &amg88xx_chip_info, NULL); | |
169 | ||
17f330ce | 170 | return PTR_ERR_OR_ZERO(hwmon); |
acbea679 MR |
171 | } |
172 | #else | |
173 | #define amg88xx_hwmon_init NULL | |
174 | #endif | |
175 | ||
5cebaac6 MR |
176 | #define AMG88XX 0 |
177 | ||
178 | static const struct video_i2c_chip video_i2c_chip[] = { | |
179 | [AMG88XX] = { | |
180 | .size = &amg88xx_size, | |
181 | .format = &amg88xx_format, | |
182 | .max_fps = 10, | |
183 | .buffer_size = 128, | |
184 | .bpp = 16, | |
185 | .xfer = &amg88xx_xfer, | |
acbea679 | 186 | .hwmon_init = amg88xx_hwmon_init, |
5cebaac6 MR |
187 | }, |
188 | }; | |
189 | ||
190 | static const struct v4l2_file_operations video_i2c_fops = { | |
191 | .owner = THIS_MODULE, | |
192 | .open = v4l2_fh_open, | |
193 | .release = vb2_fop_release, | |
194 | .poll = vb2_fop_poll, | |
195 | .read = vb2_fop_read, | |
196 | .mmap = vb2_fop_mmap, | |
197 | .unlocked_ioctl = video_ioctl2, | |
198 | }; | |
199 | ||
200 | static int queue_setup(struct vb2_queue *vq, | |
201 | unsigned int *nbuffers, unsigned int *nplanes, | |
202 | unsigned int sizes[], struct device *alloc_devs[]) | |
203 | { | |
204 | struct video_i2c_data *data = vb2_get_drv_priv(vq); | |
205 | unsigned int size = data->chip->buffer_size; | |
206 | ||
207 | if (vq->num_buffers + *nbuffers < 2) | |
208 | *nbuffers = 2; | |
209 | ||
210 | if (*nplanes) | |
211 | return sizes[0] < size ? -EINVAL : 0; | |
212 | ||
213 | *nplanes = 1; | |
214 | sizes[0] = size; | |
215 | ||
216 | return 0; | |
217 | } | |
218 | ||
219 | static int buffer_prepare(struct vb2_buffer *vb) | |
220 | { | |
221 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | |
222 | struct video_i2c_data *data = vb2_get_drv_priv(vb->vb2_queue); | |
223 | unsigned int size = data->chip->buffer_size; | |
224 | ||
225 | if (vb2_plane_size(vb, 0) < size) | |
226 | return -EINVAL; | |
227 | ||
228 | vbuf->field = V4L2_FIELD_NONE; | |
229 | vb2_set_plane_payload(vb, 0, size); | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
234 | static void buffer_queue(struct vb2_buffer *vb) | |
235 | { | |
236 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); | |
237 | struct video_i2c_data *data = vb2_get_drv_priv(vb->vb2_queue); | |
238 | struct video_i2c_buffer *buf = | |
239 | container_of(vbuf, struct video_i2c_buffer, vb); | |
240 | ||
241 | spin_lock(&data->slock); | |
242 | list_add_tail(&buf->list, &data->vid_cap_active); | |
243 | spin_unlock(&data->slock); | |
244 | } | |
245 | ||
246 | static int video_i2c_thread_vid_cap(void *priv) | |
247 | { | |
248 | struct video_i2c_data *data = priv; | |
249 | unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps); | |
250 | ||
251 | set_freezable(); | |
252 | ||
253 | do { | |
254 | unsigned long start_jiffies = jiffies; | |
255 | struct video_i2c_buffer *vid_cap_buf = NULL; | |
256 | int schedule_delay; | |
257 | ||
258 | try_to_freeze(); | |
259 | ||
260 | spin_lock(&data->slock); | |
261 | ||
262 | if (!list_empty(&data->vid_cap_active)) { | |
263 | vid_cap_buf = list_last_entry(&data->vid_cap_active, | |
264 | struct video_i2c_buffer, list); | |
265 | list_del(&vid_cap_buf->list); | |
266 | } | |
267 | ||
268 | spin_unlock(&data->slock); | |
269 | ||
270 | if (vid_cap_buf) { | |
271 | struct vb2_buffer *vb2_buf = &vid_cap_buf->vb.vb2_buf; | |
272 | void *vbuf = vb2_plane_vaddr(vb2_buf, 0); | |
273 | int ret; | |
274 | ||
275 | ret = data->chip->xfer(data, vbuf); | |
276 | vb2_buf->timestamp = ktime_get_ns(); | |
277 | vid_cap_buf->vb.sequence = data->sequence++; | |
278 | vb2_buffer_done(vb2_buf, ret ? | |
279 | VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); | |
280 | } | |
281 | ||
282 | schedule_delay = delay - (jiffies - start_jiffies); | |
283 | ||
284 | if (time_after(jiffies, start_jiffies + delay)) | |
285 | schedule_delay = delay; | |
286 | ||
287 | schedule_timeout_interruptible(schedule_delay); | |
288 | } while (!kthread_should_stop()); | |
289 | ||
290 | return 0; | |
291 | } | |
292 | ||
293 | static void video_i2c_del_list(struct vb2_queue *vq, enum vb2_buffer_state state) | |
294 | { | |
295 | struct video_i2c_data *data = vb2_get_drv_priv(vq); | |
296 | struct video_i2c_buffer *buf, *tmp; | |
297 | ||
298 | spin_lock(&data->slock); | |
299 | ||
300 | list_for_each_entry_safe(buf, tmp, &data->vid_cap_active, list) { | |
301 | list_del(&buf->list); | |
302 | vb2_buffer_done(&buf->vb.vb2_buf, state); | |
303 | } | |
304 | ||
305 | spin_unlock(&data->slock); | |
306 | } | |
307 | ||
308 | static int start_streaming(struct vb2_queue *vq, unsigned int count) | |
309 | { | |
310 | struct video_i2c_data *data = vb2_get_drv_priv(vq); | |
311 | ||
312 | if (data->kthread_vid_cap) | |
313 | return 0; | |
314 | ||
315 | data->sequence = 0; | |
316 | data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data, | |
317 | "%s-vid-cap", data->v4l2_dev.name); | |
318 | if (!IS_ERR(data->kthread_vid_cap)) | |
319 | return 0; | |
320 | ||
321 | video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED); | |
322 | ||
323 | return PTR_ERR(data->kthread_vid_cap); | |
324 | } | |
325 | ||
326 | static void stop_streaming(struct vb2_queue *vq) | |
327 | { | |
328 | struct video_i2c_data *data = vb2_get_drv_priv(vq); | |
329 | ||
330 | if (data->kthread_vid_cap == NULL) | |
331 | return; | |
332 | ||
333 | kthread_stop(data->kthread_vid_cap); | |
334 | data->kthread_vid_cap = NULL; | |
335 | ||
336 | video_i2c_del_list(vq, VB2_BUF_STATE_ERROR); | |
337 | } | |
338 | ||
339 | static struct vb2_ops video_i2c_video_qops = { | |
340 | .queue_setup = queue_setup, | |
341 | .buf_prepare = buffer_prepare, | |
342 | .buf_queue = buffer_queue, | |
343 | .start_streaming = start_streaming, | |
344 | .stop_streaming = stop_streaming, | |
345 | .wait_prepare = vb2_ops_wait_prepare, | |
346 | .wait_finish = vb2_ops_wait_finish, | |
347 | }; | |
348 | ||
349 | static int video_i2c_querycap(struct file *file, void *priv, | |
350 | struct v4l2_capability *vcap) | |
351 | { | |
352 | struct video_i2c_data *data = video_drvdata(file); | |
353 | struct i2c_client *client = data->client; | |
354 | ||
c0decac1 MCC |
355 | strscpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver)); |
356 | strscpy(vcap->card, data->vdev.name, sizeof(vcap->card)); | |
5cebaac6 MR |
357 | |
358 | sprintf(vcap->bus_info, "I2C:%d-%d", client->adapter->nr, client->addr); | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
363 | static int video_i2c_g_input(struct file *file, void *fh, unsigned int *inp) | |
364 | { | |
365 | *inp = 0; | |
366 | ||
367 | return 0; | |
368 | } | |
369 | ||
370 | static int video_i2c_s_input(struct file *file, void *fh, unsigned int inp) | |
371 | { | |
372 | return (inp > 0) ? -EINVAL : 0; | |
373 | } | |
374 | ||
375 | static int video_i2c_enum_input(struct file *file, void *fh, | |
376 | struct v4l2_input *vin) | |
377 | { | |
378 | if (vin->index > 0) | |
379 | return -EINVAL; | |
380 | ||
c0decac1 | 381 | strscpy(vin->name, "Camera", sizeof(vin->name)); |
5cebaac6 MR |
382 | |
383 | vin->type = V4L2_INPUT_TYPE_CAMERA; | |
384 | ||
385 | return 0; | |
386 | } | |
387 | ||
388 | static int video_i2c_enum_fmt_vid_cap(struct file *file, void *fh, | |
389 | struct v4l2_fmtdesc *fmt) | |
390 | { | |
391 | struct video_i2c_data *data = video_drvdata(file); | |
392 | enum v4l2_buf_type type = fmt->type; | |
393 | ||
394 | if (fmt->index > 0) | |
395 | return -EINVAL; | |
396 | ||
397 | *fmt = *data->chip->format; | |
398 | fmt->type = type; | |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
403 | static int video_i2c_enum_framesizes(struct file *file, void *fh, | |
404 | struct v4l2_frmsizeenum *fsize) | |
405 | { | |
406 | const struct video_i2c_data *data = video_drvdata(file); | |
407 | const struct v4l2_frmsize_discrete *size = data->chip->size; | |
408 | ||
409 | /* currently only one frame size is allowed */ | |
410 | if (fsize->index > 0) | |
411 | return -EINVAL; | |
412 | ||
413 | if (fsize->pixel_format != data->chip->format->pixelformat) | |
414 | return -EINVAL; | |
415 | ||
416 | fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; | |
417 | fsize->discrete.width = size->width; | |
418 | fsize->discrete.height = size->height; | |
419 | ||
420 | return 0; | |
421 | } | |
422 | ||
423 | static int video_i2c_enum_frameintervals(struct file *file, void *priv, | |
424 | struct v4l2_frmivalenum *fe) | |
425 | { | |
426 | const struct video_i2c_data *data = video_drvdata(file); | |
427 | const struct v4l2_frmsize_discrete *size = data->chip->size; | |
428 | ||
429 | if (fe->index > 0) | |
430 | return -EINVAL; | |
431 | ||
432 | if (fe->width != size->width || fe->height != size->height) | |
433 | return -EINVAL; | |
434 | ||
435 | fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; | |
436 | fe->discrete.numerator = 1; | |
437 | fe->discrete.denominator = data->chip->max_fps; | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
442 | static int video_i2c_try_fmt_vid_cap(struct file *file, void *fh, | |
443 | struct v4l2_format *fmt) | |
444 | { | |
445 | const struct video_i2c_data *data = video_drvdata(file); | |
446 | const struct v4l2_frmsize_discrete *size = data->chip->size; | |
447 | struct v4l2_pix_format *pix = &fmt->fmt.pix; | |
448 | unsigned int bpp = data->chip->bpp / 8; | |
449 | ||
450 | pix->width = size->width; | |
451 | pix->height = size->height; | |
452 | pix->pixelformat = data->chip->format->pixelformat; | |
453 | pix->field = V4L2_FIELD_NONE; | |
454 | pix->bytesperline = pix->width * bpp; | |
455 | pix->sizeimage = pix->bytesperline * pix->height; | |
456 | pix->colorspace = V4L2_COLORSPACE_RAW; | |
457 | ||
458 | return 0; | |
459 | } | |
460 | ||
461 | static int video_i2c_s_fmt_vid_cap(struct file *file, void *fh, | |
462 | struct v4l2_format *fmt) | |
463 | { | |
464 | struct video_i2c_data *data = video_drvdata(file); | |
465 | ||
466 | if (vb2_is_busy(&data->vb_vidq)) | |
467 | return -EBUSY; | |
468 | ||
469 | return video_i2c_try_fmt_vid_cap(file, fh, fmt); | |
470 | } | |
471 | ||
472 | static int video_i2c_g_parm(struct file *filp, void *priv, | |
473 | struct v4l2_streamparm *parm) | |
474 | { | |
475 | struct video_i2c_data *data = video_drvdata(filp); | |
476 | ||
477 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
478 | return -EINVAL; | |
479 | ||
480 | parm->parm.capture.readbuffers = 1; | |
481 | parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; | |
482 | parm->parm.capture.timeperframe.numerator = 1; | |
483 | parm->parm.capture.timeperframe.denominator = data->chip->max_fps; | |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
488 | static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = { | |
489 | .vidioc_querycap = video_i2c_querycap, | |
490 | .vidioc_g_input = video_i2c_g_input, | |
491 | .vidioc_s_input = video_i2c_s_input, | |
492 | .vidioc_enum_input = video_i2c_enum_input, | |
493 | .vidioc_enum_fmt_vid_cap = video_i2c_enum_fmt_vid_cap, | |
494 | .vidioc_enum_framesizes = video_i2c_enum_framesizes, | |
495 | .vidioc_enum_frameintervals = video_i2c_enum_frameintervals, | |
496 | .vidioc_g_fmt_vid_cap = video_i2c_try_fmt_vid_cap, | |
497 | .vidioc_s_fmt_vid_cap = video_i2c_s_fmt_vid_cap, | |
498 | .vidioc_g_parm = video_i2c_g_parm, | |
499 | .vidioc_s_parm = video_i2c_g_parm, | |
500 | .vidioc_try_fmt_vid_cap = video_i2c_try_fmt_vid_cap, | |
501 | .vidioc_reqbufs = vb2_ioctl_reqbufs, | |
502 | .vidioc_create_bufs = vb2_ioctl_create_bufs, | |
503 | .vidioc_prepare_buf = vb2_ioctl_prepare_buf, | |
504 | .vidioc_querybuf = vb2_ioctl_querybuf, | |
505 | .vidioc_qbuf = vb2_ioctl_qbuf, | |
506 | .vidioc_dqbuf = vb2_ioctl_dqbuf, | |
507 | .vidioc_streamon = vb2_ioctl_streamon, | |
508 | .vidioc_streamoff = vb2_ioctl_streamoff, | |
509 | }; | |
510 | ||
511 | static void video_i2c_release(struct video_device *vdev) | |
512 | { | |
513 | kfree(video_get_drvdata(vdev)); | |
514 | } | |
515 | ||
516 | static int video_i2c_probe(struct i2c_client *client, | |
517 | const struct i2c_device_id *id) | |
518 | { | |
519 | struct video_i2c_data *data; | |
520 | struct v4l2_device *v4l2_dev; | |
521 | struct vb2_queue *queue; | |
522 | int ret = -ENODEV; | |
523 | ||
524 | data = kzalloc(sizeof(*data), GFP_KERNEL); | |
525 | if (!data) | |
526 | return -ENOMEM; | |
527 | ||
528 | if (dev_fwnode(&client->dev)) | |
529 | data->chip = device_get_match_data(&client->dev); | |
530 | else if (id) | |
531 | data->chip = &video_i2c_chip[id->driver_data]; | |
532 | else | |
533 | goto error_free_device; | |
534 | ||
535 | data->client = client; | |
536 | v4l2_dev = &data->v4l2_dev; | |
c0decac1 | 537 | strscpy(v4l2_dev->name, VIDEO_I2C_DRIVER, sizeof(v4l2_dev->name)); |
5cebaac6 MR |
538 | |
539 | ret = v4l2_device_register(&client->dev, v4l2_dev); | |
540 | if (ret < 0) | |
541 | goto error_free_device; | |
542 | ||
543 | mutex_init(&data->lock); | |
544 | mutex_init(&data->queue_lock); | |
545 | ||
546 | queue = &data->vb_vidq; | |
547 | queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
548 | queue->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR | VB2_READ; | |
549 | queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; | |
550 | queue->drv_priv = data; | |
551 | queue->buf_struct_size = sizeof(struct video_i2c_buffer); | |
552 | queue->min_buffers_needed = 1; | |
553 | queue->ops = &video_i2c_video_qops; | |
554 | queue->mem_ops = &vb2_vmalloc_memops; | |
555 | ||
556 | ret = vb2_queue_init(queue); | |
557 | if (ret < 0) | |
558 | goto error_unregister_device; | |
559 | ||
560 | data->vdev.queue = queue; | |
561 | data->vdev.queue->lock = &data->queue_lock; | |
562 | ||
563 | snprintf(data->vdev.name, sizeof(data->vdev.name), | |
564 | "I2C %d-%d Transport Video", | |
565 | client->adapter->nr, client->addr); | |
566 | ||
567 | data->vdev.v4l2_dev = v4l2_dev; | |
568 | data->vdev.fops = &video_i2c_fops; | |
569 | data->vdev.lock = &data->lock; | |
570 | data->vdev.ioctl_ops = &video_i2c_ioctl_ops; | |
571 | data->vdev.release = video_i2c_release; | |
572 | data->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | | |
573 | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; | |
574 | ||
575 | spin_lock_init(&data->slock); | |
576 | INIT_LIST_HEAD(&data->vid_cap_active); | |
577 | ||
578 | video_set_drvdata(&data->vdev, data); | |
579 | i2c_set_clientdata(client, data); | |
580 | ||
acbea679 MR |
581 | if (data->chip->hwmon_init) { |
582 | ret = data->chip->hwmon_init(data); | |
583 | if (ret < 0) { | |
584 | dev_warn(&client->dev, | |
585 | "failed to register hwmon device\n"); | |
586 | } | |
587 | } | |
588 | ||
5cebaac6 MR |
589 | ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1); |
590 | if (ret < 0) | |
591 | goto error_unregister_device; | |
592 | ||
593 | return 0; | |
594 | ||
595 | error_unregister_device: | |
596 | v4l2_device_unregister(v4l2_dev); | |
597 | mutex_destroy(&data->lock); | |
598 | mutex_destroy(&data->queue_lock); | |
599 | ||
600 | error_free_device: | |
601 | kfree(data); | |
602 | ||
603 | return ret; | |
604 | } | |
605 | ||
606 | static int video_i2c_remove(struct i2c_client *client) | |
607 | { | |
608 | struct video_i2c_data *data = i2c_get_clientdata(client); | |
609 | ||
610 | video_unregister_device(&data->vdev); | |
611 | v4l2_device_unregister(&data->v4l2_dev); | |
612 | ||
613 | mutex_destroy(&data->lock); | |
614 | mutex_destroy(&data->queue_lock); | |
615 | ||
616 | return 0; | |
617 | } | |
618 | ||
619 | static const struct i2c_device_id video_i2c_id_table[] = { | |
620 | { "amg88xx", AMG88XX }, | |
621 | {} | |
622 | }; | |
623 | MODULE_DEVICE_TABLE(i2c, video_i2c_id_table); | |
624 | ||
625 | static const struct of_device_id video_i2c_of_match[] = { | |
626 | { .compatible = "panasonic,amg88xx", .data = &video_i2c_chip[AMG88XX] }, | |
627 | {} | |
628 | }; | |
629 | MODULE_DEVICE_TABLE(of, video_i2c_of_match); | |
630 | ||
631 | static struct i2c_driver video_i2c_driver = { | |
632 | .driver = { | |
633 | .name = VIDEO_I2C_DRIVER, | |
634 | .of_match_table = video_i2c_of_match, | |
635 | }, | |
636 | .probe = video_i2c_probe, | |
637 | .remove = video_i2c_remove, | |
638 | .id_table = video_i2c_id_table, | |
639 | }; | |
640 | ||
641 | module_i2c_driver(video_i2c_driver); | |
642 | ||
643 | MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); | |
644 | MODULE_DESCRIPTION("I2C transport video support"); | |
645 | MODULE_LICENSE("GPL v2"); |