Commit | Line | Data |
---|---|---|
11bd27b2 ST |
1 | /* |
2 | * Driver for the NXP SAA7164 PCIe bridge | |
3 | * | |
63a412ec | 4 | * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com> |
11bd27b2 ST |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * | |
15 | * GNU General Public License for more details. | |
11bd27b2 ST |
16 | */ |
17 | ||
18 | #include "saa7164.h" | |
19 | ||
11bd27b2 ST |
20 | /* Take the encoder configuration from the port struct and |
21 | * flush it to the hardware. | |
22 | */ | |
23 | static void saa7164_vbi_configure(struct saa7164_port *port) | |
24 | { | |
25 | struct saa7164_dev *dev = port->dev; | |
26 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | |
27 | ||
225b783b HV |
28 | port->vbi_params.width = port->enc_port->width; |
29 | port->vbi_params.height = port->enc_port->height; | |
11bd27b2 | 30 | port->vbi_params.is_50hz = |
225b783b | 31 | (port->enc_port->encodernorm.id & V4L2_STD_625_50) != 0; |
11bd27b2 ST |
32 | |
33 | /* Set up the DIF (enable it) for analog mode by default */ | |
34 | saa7164_api_initialize_dif(port); | |
11bd27b2 ST |
35 | dprintk(DBGLVL_VBI, "%s() ends\n", __func__); |
36 | } | |
37 | ||
38 | static int saa7164_vbi_buffers_dealloc(struct saa7164_port *port) | |
39 | { | |
40 | struct list_head *c, *n, *p, *q, *l, *v; | |
41 | struct saa7164_dev *dev = port->dev; | |
42 | struct saa7164_buffer *buf; | |
43 | struct saa7164_user_buffer *ubuf; | |
44 | ||
45 | /* Remove any allocated buffers */ | |
46 | mutex_lock(&port->dmaqueue_lock); | |
47 | ||
48 | dprintk(DBGLVL_VBI, "%s(port=%d) dmaqueue\n", __func__, port->nr); | |
49 | list_for_each_safe(c, n, &port->dmaqueue.list) { | |
50 | buf = list_entry(c, struct saa7164_buffer, list); | |
51 | list_del(c); | |
52 | saa7164_buffer_dealloc(buf); | |
53 | } | |
54 | ||
55 | dprintk(DBGLVL_VBI, "%s(port=%d) used\n", __func__, port->nr); | |
56 | list_for_each_safe(p, q, &port->list_buf_used.list) { | |
57 | ubuf = list_entry(p, struct saa7164_user_buffer, list); | |
58 | list_del(p); | |
59 | saa7164_buffer_dealloc_user(ubuf); | |
60 | } | |
61 | ||
62 | dprintk(DBGLVL_VBI, "%s(port=%d) free\n", __func__, port->nr); | |
63 | list_for_each_safe(l, v, &port->list_buf_free.list) { | |
64 | ubuf = list_entry(l, struct saa7164_user_buffer, list); | |
65 | list_del(l); | |
66 | saa7164_buffer_dealloc_user(ubuf); | |
67 | } | |
68 | ||
69 | mutex_unlock(&port->dmaqueue_lock); | |
70 | dprintk(DBGLVL_VBI, "%s(port=%d) done\n", __func__, port->nr); | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
75 | /* Dynamic buffer switch at vbi start time */ | |
76 | static int saa7164_vbi_buffers_alloc(struct saa7164_port *port) | |
77 | { | |
78 | struct saa7164_dev *dev = port->dev; | |
79 | struct saa7164_buffer *buf; | |
80 | struct saa7164_user_buffer *ubuf; | |
4d270cfb | 81 | struct tmHWStreamParameters *params = &port->hw_streamingparams; |
11bd27b2 ST |
82 | int result = -ENODEV, i; |
83 | int len = 0; | |
84 | ||
85 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | |
86 | ||
87 | /* TODO: NTSC SPECIFIC */ | |
88 | /* Init and establish defaults */ | |
89 | params->samplesperline = 1440; | |
90 | params->numberoflines = 12; | |
91 | params->numberoflines = 18; | |
92 | params->pitch = 1600; | |
93 | params->pitch = 1440; | |
94 | params->numpagetables = 2 + | |
95 | ((params->numberoflines * params->pitch) / PAGE_SIZE); | |
96 | params->bitspersample = 8; | |
97 | params->linethreshold = 0; | |
61ca1500 PH |
98 | params->pagetablelistvirt = NULL; |
99 | params->pagetablelistphys = NULL; | |
11bd27b2 ST |
100 | params->numpagetableentries = port->hwcfg.buffercount; |
101 | ||
102 | /* Allocate the PCI resources, buffers (hard) */ | |
103 | for (i = 0; i < port->hwcfg.buffercount; i++) { | |
104 | buf = saa7164_buffer_alloc(port, | |
105 | params->numberoflines * | |
106 | params->pitch); | |
107 | ||
108 | if (!buf) { | |
24f711c1 | 109 | printk(KERN_ERR "%s() failed (errno = %d), unable to allocate buffer\n", |
11bd27b2 ST |
110 | __func__, result); |
111 | result = -ENOMEM; | |
112 | goto failed; | |
113 | } else { | |
114 | ||
115 | mutex_lock(&port->dmaqueue_lock); | |
116 | list_add_tail(&buf->list, &port->dmaqueue.list); | |
117 | mutex_unlock(&port->dmaqueue_lock); | |
118 | ||
119 | } | |
120 | } | |
121 | ||
70f23fd6 | 122 | /* Allocate some kernel buffers for copying |
11bd27b2 ST |
123 | * to userpsace. |
124 | */ | |
125 | len = params->numberoflines * params->pitch; | |
126 | ||
127 | if (vbi_buffers < 16) | |
128 | vbi_buffers = 16; | |
129 | if (vbi_buffers > 512) | |
130 | vbi_buffers = 512; | |
131 | ||
132 | for (i = 0; i < vbi_buffers; i++) { | |
133 | ||
134 | ubuf = saa7164_buffer_alloc_user(dev, len); | |
135 | if (ubuf) { | |
136 | mutex_lock(&port->dmaqueue_lock); | |
137 | list_add_tail(&ubuf->list, &port->list_buf_free.list); | |
138 | mutex_unlock(&port->dmaqueue_lock); | |
139 | } | |
140 | ||
141 | } | |
142 | ||
143 | result = 0; | |
144 | ||
145 | failed: | |
146 | return result; | |
147 | } | |
148 | ||
149 | ||
150 | static int saa7164_vbi_initialize(struct saa7164_port *port) | |
151 | { | |
152 | saa7164_vbi_configure(port); | |
153 | return 0; | |
154 | } | |
155 | ||
156 | /* -- V4L2 --------------------------------------------------------- */ | |
314527ac | 157 | static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) |
11bd27b2 ST |
158 | { |
159 | struct saa7164_vbi_fh *fh = file->private_data; | |
11bd27b2 | 160 | |
225b783b | 161 | return saa7164_s_std(fh->port->enc_port, id); |
11bd27b2 ST |
162 | } |
163 | ||
8d2d41e9 HV |
164 | static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) |
165 | { | |
166 | struct saa7164_encoder_fh *fh = file->private_data; | |
11bd27b2 | 167 | |
225b783b | 168 | return saa7164_g_std(fh->port->enc_port, id); |
11bd27b2 ST |
169 | } |
170 | ||
171 | static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) | |
172 | { | |
173 | struct saa7164_vbi_fh *fh = file->private_data; | |
11bd27b2 | 174 | |
225b783b | 175 | return saa7164_g_input(fh->port->enc_port, i); |
11bd27b2 ST |
176 | } |
177 | ||
178 | static int vidioc_s_input(struct file *file, void *priv, unsigned int i) | |
179 | { | |
180 | struct saa7164_vbi_fh *fh = file->private_data; | |
11bd27b2 | 181 | |
225b783b | 182 | return saa7164_s_input(fh->port->enc_port, i); |
11bd27b2 ST |
183 | } |
184 | ||
185 | static int vidioc_g_frequency(struct file *file, void *priv, | |
186 | struct v4l2_frequency *f) | |
187 | { | |
188 | struct saa7164_vbi_fh *fh = file->private_data; | |
11bd27b2 | 189 | |
225b783b | 190 | return saa7164_g_frequency(fh->port->enc_port, f); |
11bd27b2 ST |
191 | } |
192 | ||
193 | static int vidioc_s_frequency(struct file *file, void *priv, | |
b530a447 | 194 | const struct v4l2_frequency *f) |
11bd27b2 ST |
195 | { |
196 | struct saa7164_vbi_fh *fh = file->private_data; | |
225b783b | 197 | int ret = saa7164_s_frequency(fh->port->enc_port, f); |
11bd27b2 | 198 | |
225b783b HV |
199 | if (ret == 0) |
200 | saa7164_vbi_initialize(fh->port); | |
201 | return ret; | |
11bd27b2 ST |
202 | } |
203 | ||
11bd27b2 ST |
204 | static int vidioc_querycap(struct file *file, void *priv, |
205 | struct v4l2_capability *cap) | |
206 | { | |
207 | struct saa7164_vbi_fh *fh = file->private_data; | |
208 | struct saa7164_port *port = fh->port; | |
209 | struct saa7164_dev *dev = port->dev; | |
210 | ||
211 | strcpy(cap->driver, dev->name); | |
212 | strlcpy(cap->card, saa7164_boards[dev->board].name, | |
213 | sizeof(cap->card)); | |
214 | sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); | |
215 | ||
534bc3e2 | 216 | cap->device_caps = |
11bd27b2 | 217 | V4L2_CAP_VBI_CAPTURE | |
534bc3e2 HV |
218 | V4L2_CAP_READWRITE | |
219 | V4L2_CAP_TUNER; | |
11bd27b2 | 220 | |
534bc3e2 HV |
221 | cap->capabilities = cap->device_caps | |
222 | V4L2_CAP_VIDEO_CAPTURE | | |
223 | V4L2_CAP_DEVICE_CAPS; | |
11bd27b2 ST |
224 | |
225 | return 0; | |
226 | } | |
227 | ||
11bd27b2 ST |
228 | static int saa7164_vbi_stop_port(struct saa7164_port *port) |
229 | { | |
230 | struct saa7164_dev *dev = port->dev; | |
231 | int ret; | |
232 | ||
233 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); | |
234 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | |
235 | printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n", | |
236 | __func__, ret); | |
237 | ret = -EIO; | |
238 | } else { | |
239 | dprintk(DBGLVL_VBI, "%s() Stopped\n", __func__); | |
240 | ret = 0; | |
241 | } | |
242 | ||
243 | return ret; | |
244 | } | |
245 | ||
246 | static int saa7164_vbi_acquire_port(struct saa7164_port *port) | |
247 | { | |
248 | struct saa7164_dev *dev = port->dev; | |
249 | int ret; | |
250 | ||
251 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); | |
252 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | |
253 | printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n", | |
254 | __func__, ret); | |
255 | ret = -EIO; | |
256 | } else { | |
257 | dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__); | |
258 | ret = 0; | |
259 | } | |
260 | ||
261 | return ret; | |
262 | } | |
263 | ||
264 | static int saa7164_vbi_pause_port(struct saa7164_port *port) | |
265 | { | |
266 | struct saa7164_dev *dev = port->dev; | |
267 | int ret; | |
268 | ||
269 | ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); | |
270 | if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { | |
271 | printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n", | |
272 | __func__, ret); | |
273 | ret = -EIO; | |
274 | } else { | |
275 | dprintk(DBGLVL_VBI, "%s() Paused\n", __func__); | |
276 | ret = 0; | |
277 | } | |
278 | ||
279 | return ret; | |
280 | } | |
281 | ||
282 | /* Firmware is very windows centric, meaning you have to transition | |
283 | * the part through AVStream / KS Windows stages, forwards or backwards. | |
284 | * States are: stopped, acquired (h/w), paused, started. | |
285 | * We have to leave here will all of the soft buffers on the free list, | |
286 | * else the cfg_post() func won't have soft buffers to correctly configure. | |
287 | */ | |
288 | static int saa7164_vbi_stop_streaming(struct saa7164_port *port) | |
289 | { | |
290 | struct saa7164_dev *dev = port->dev; | |
291 | struct saa7164_buffer *buf; | |
292 | struct saa7164_user_buffer *ubuf; | |
293 | struct list_head *c, *n; | |
294 | int ret; | |
295 | ||
296 | dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr); | |
297 | ||
298 | ret = saa7164_vbi_pause_port(port); | |
299 | ret = saa7164_vbi_acquire_port(port); | |
300 | ret = saa7164_vbi_stop_port(port); | |
301 | ||
302 | dprintk(DBGLVL_VBI, "%s(port=%d) Hardware stopped\n", __func__, | |
303 | port->nr); | |
304 | ||
305 | /* Reset the state of any allocated buffer resources */ | |
306 | mutex_lock(&port->dmaqueue_lock); | |
307 | ||
308 | /* Reset the hard and soft buffer state */ | |
309 | list_for_each_safe(c, n, &port->dmaqueue.list) { | |
310 | buf = list_entry(c, struct saa7164_buffer, list); | |
311 | buf->flags = SAA7164_BUFFER_FREE; | |
312 | buf->pos = 0; | |
313 | } | |
314 | ||
315 | list_for_each_safe(c, n, &port->list_buf_used.list) { | |
316 | ubuf = list_entry(c, struct saa7164_user_buffer, list); | |
317 | ubuf->pos = 0; | |
318 | list_move_tail(&ubuf->list, &port->list_buf_free.list); | |
319 | } | |
320 | ||
321 | mutex_unlock(&port->dmaqueue_lock); | |
322 | ||
323 | /* Free any allocated resources */ | |
324 | saa7164_vbi_buffers_dealloc(port); | |
325 | ||
326 | dprintk(DBGLVL_VBI, "%s(port=%d) Released\n", __func__, port->nr); | |
327 | ||
328 | return ret; | |
329 | } | |
330 | ||
331 | static int saa7164_vbi_start_streaming(struct saa7164_port *port) | |
332 | { | |
333 | struct saa7164_dev *dev = port->dev; | |
334 | int result, ret = 0; | |
335 | ||
336 | dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr); | |
337 | ||
338 | port->done_first_interrupt = 0; | |
339 | ||
340 | /* allocate all of the PCIe DMA buffer resources on the fly, | |
341 | * allowing switching between TS and PS payloads without | |
342 | * requiring a complete driver reload. | |
343 | */ | |
344 | saa7164_vbi_buffers_alloc(port); | |
345 | ||
346 | /* Configure the encoder with any cache values */ | |
bc250684 ST |
347 | #if 0 |
348 | saa7164_api_set_encoder(port); | |
349 | saa7164_api_get_encoder(port); | |
350 | #endif | |
11bd27b2 ST |
351 | |
352 | /* Place the empty buffers on the hardware */ | |
353 | saa7164_buffer_cfg_port(port); | |
354 | ||
355 | /* Negotiate format */ | |
356 | if (saa7164_api_set_vbi_format(port) != SAA_OK) { | |
357 | printk(KERN_ERR "%s() No supported VBI format\n", __func__); | |
358 | ret = -EIO; | |
359 | goto out; | |
360 | } | |
361 | ||
362 | /* Acquire the hardware */ | |
363 | result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); | |
364 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | |
365 | printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n", | |
366 | __func__, result); | |
367 | ||
368 | ret = -EIO; | |
369 | goto out; | |
370 | } else | |
371 | dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__); | |
372 | ||
373 | /* Pause the hardware */ | |
374 | result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); | |
375 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | |
376 | printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n", | |
377 | __func__, result); | |
378 | ||
379 | /* Stop the hardware, regardless */ | |
380 | result = saa7164_vbi_stop_port(port); | |
2e71064f | 381 | if (result != SAA_OK) { |
24f711c1 MCC |
382 | printk(KERN_ERR "%s() pause/forced stop transition failed, res = 0x%x\n", |
383 | __func__, result); | |
11bd27b2 ST |
384 | } |
385 | ||
386 | ret = -EIO; | |
387 | goto out; | |
388 | } else | |
389 | dprintk(DBGLVL_VBI, "%s() Paused\n", __func__); | |
390 | ||
391 | /* Start the hardware */ | |
392 | result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN); | |
393 | if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { | |
394 | printk(KERN_ERR "%s() run transition failed, result = 0x%x\n", | |
395 | __func__, result); | |
396 | ||
397 | /* Stop the hardware, regardless */ | |
398 | result = saa7164_vbi_acquire_port(port); | |
399 | result = saa7164_vbi_stop_port(port); | |
2e71064f | 400 | if (result != SAA_OK) { |
24f711c1 MCC |
401 | printk(KERN_ERR "%s() run/forced stop transition failed, res = 0x%x\n", |
402 | __func__, result); | |
11bd27b2 ST |
403 | } |
404 | ||
405 | ret = -EIO; | |
406 | } else | |
407 | dprintk(DBGLVL_VBI, "%s() Running\n", __func__); | |
408 | ||
409 | out: | |
410 | return ret; | |
411 | } | |
412 | ||
5faf7db8 MCC |
413 | static int saa7164_vbi_fmt(struct file *file, void *priv, |
414 | struct v4l2_format *f) | |
11bd27b2 | 415 | { |
11bd27b2 | 416 | /* ntsc */ |
11bd27b2 ST |
417 | f->fmt.vbi.samples_per_line = 1440; |
418 | f->fmt.vbi.sampling_rate = 27000000; | |
419 | f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; | |
420 | f->fmt.vbi.offset = 0; | |
421 | f->fmt.vbi.flags = 0; | |
422 | f->fmt.vbi.start[0] = 10; | |
423 | f->fmt.vbi.count[0] = 18; | |
424 | f->fmt.vbi.start[1] = 263 + 10 + 1; | |
425 | f->fmt.vbi.count[1] = 18; | |
031d2297 | 426 | memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved)); |
11bd27b2 ST |
427 | return 0; |
428 | } | |
429 | ||
430 | static int fops_open(struct file *file) | |
431 | { | |
214ce3fa ST |
432 | struct saa7164_dev *dev; |
433 | struct saa7164_port *port; | |
11bd27b2 | 434 | struct saa7164_vbi_fh *fh; |
11bd27b2 | 435 | |
214ce3fa ST |
436 | port = (struct saa7164_port *)video_get_drvdata(video_devdata(file)); |
437 | if (!port) | |
438 | return -ENODEV; | |
11bd27b2 | 439 | |
214ce3fa | 440 | dev = port->dev; |
11bd27b2 | 441 | |
214ce3fa | 442 | dprintk(DBGLVL_VBI, "%s()\n", __func__); |
11bd27b2 ST |
443 | |
444 | /* allocate + initialize per filehandle data */ | |
445 | fh = kzalloc(sizeof(*fh), GFP_KERNEL); | |
214ce3fa | 446 | if (NULL == fh) |
11bd27b2 | 447 | return -ENOMEM; |
11bd27b2 | 448 | |
11bd27b2 | 449 | fh->port = port; |
d6d3fe2f HV |
450 | v4l2_fh_init(&fh->fh, video_devdata(file)); |
451 | v4l2_fh_add(&fh->fh); | |
452 | file->private_data = fh; | |
11bd27b2 | 453 | |
11bd27b2 ST |
454 | return 0; |
455 | } | |
456 | ||
457 | static int fops_release(struct file *file) | |
458 | { | |
459 | struct saa7164_vbi_fh *fh = file->private_data; | |
460 | struct saa7164_port *port = fh->port; | |
461 | struct saa7164_dev *dev = port->dev; | |
462 | ||
463 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | |
464 | ||
465 | /* Shut device down on last close */ | |
466 | if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { | |
467 | if (atomic_dec_return(&port->v4l_reader_count) == 0) { | |
468 | /* stop vbi capture then cancel buffers */ | |
469 | saa7164_vbi_stop_streaming(port); | |
470 | } | |
471 | } | |
472 | ||
d6d3fe2f HV |
473 | v4l2_fh_del(&fh->fh); |
474 | v4l2_fh_exit(&fh->fh); | |
11bd27b2 ST |
475 | kfree(fh); |
476 | ||
477 | return 0; | |
478 | } | |
479 | ||
5faf7db8 MCC |
480 | static struct |
481 | saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port) | |
11bd27b2 | 482 | { |
61ca1500 | 483 | struct saa7164_user_buffer *ubuf = NULL; |
11bd27b2 ST |
484 | struct saa7164_dev *dev = port->dev; |
485 | u32 crc; | |
486 | ||
487 | mutex_lock(&port->dmaqueue_lock); | |
488 | if (!list_empty(&port->list_buf_used.list)) { | |
489 | ubuf = list_first_entry(&port->list_buf_used.list, | |
490 | struct saa7164_user_buffer, list); | |
491 | ||
492 | if (crc_checking) { | |
493 | crc = crc32(0, ubuf->data, ubuf->actual_size); | |
494 | if (crc != ubuf->crc) { | |
bc250684 ST |
495 | printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n", |
496 | __func__, | |
11bd27b2 ST |
497 | ubuf, ubuf->crc, crc); |
498 | } | |
499 | } | |
500 | ||
501 | } | |
502 | mutex_unlock(&port->dmaqueue_lock); | |
503 | ||
504 | dprintk(DBGLVL_VBI, "%s() returns %p\n", __func__, ubuf); | |
505 | ||
506 | return ubuf; | |
507 | } | |
508 | ||
509 | static ssize_t fops_read(struct file *file, char __user *buffer, | |
510 | size_t count, loff_t *pos) | |
511 | { | |
512 | struct saa7164_vbi_fh *fh = file->private_data; | |
513 | struct saa7164_port *port = fh->port; | |
514 | struct saa7164_user_buffer *ubuf = NULL; | |
515 | struct saa7164_dev *dev = port->dev; | |
106d7e37 | 516 | int ret = 0; |
11bd27b2 ST |
517 | int rem, cnt; |
518 | u8 *p; | |
519 | ||
520 | port->last_read_msecs_diff = port->last_read_msecs; | |
521 | port->last_read_msecs = jiffies_to_msecs(jiffies); | |
522 | port->last_read_msecs_diff = port->last_read_msecs - | |
523 | port->last_read_msecs_diff; | |
524 | ||
525 | saa7164_histogram_update(&port->read_interval, | |
526 | port->last_read_msecs_diff); | |
527 | ||
528 | if (*pos) { | |
529 | printk(KERN_ERR "%s() ESPIPE\n", __func__); | |
530 | return -ESPIPE; | |
531 | } | |
532 | ||
533 | if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { | |
534 | if (atomic_inc_return(&port->v4l_reader_count) == 1) { | |
535 | ||
536 | if (saa7164_vbi_initialize(port) < 0) { | |
537 | printk(KERN_ERR "%s() EINVAL\n", __func__); | |
538 | return -EINVAL; | |
539 | } | |
540 | ||
541 | saa7164_vbi_start_streaming(port); | |
542 | msleep(200); | |
543 | } | |
544 | } | |
545 | ||
546 | /* blocking wait for buffer */ | |
547 | if ((file->f_flags & O_NONBLOCK) == 0) { | |
548 | if (wait_event_interruptible(port->wait_read, | |
549 | saa7164_vbi_next_buf(port))) { | |
550 | printk(KERN_ERR "%s() ERESTARTSYS\n", __func__); | |
551 | return -ERESTARTSYS; | |
552 | } | |
553 | } | |
554 | ||
555 | /* Pull the first buffer from the used list */ | |
556 | ubuf = saa7164_vbi_next_buf(port); | |
557 | ||
558 | while ((count > 0) && ubuf) { | |
559 | ||
560 | /* set remaining bytes to copy */ | |
561 | rem = ubuf->actual_size - ubuf->pos; | |
562 | cnt = rem > count ? count : rem; | |
563 | ||
564 | p = ubuf->data + ubuf->pos; | |
565 | ||
566 | dprintk(DBGLVL_VBI, | |
567 | "%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n", | |
568 | __func__, (int)count, cnt, rem, ubuf, ubuf->pos); | |
569 | ||
570 | if (copy_to_user(buffer, p, cnt)) { | |
571 | printk(KERN_ERR "%s() copy_to_user failed\n", __func__); | |
572 | if (!ret) { | |
573 | printk(KERN_ERR "%s() EFAULT\n", __func__); | |
574 | ret = -EFAULT; | |
575 | } | |
576 | goto err; | |
577 | } | |
578 | ||
579 | ubuf->pos += cnt; | |
580 | count -= cnt; | |
581 | buffer += cnt; | |
582 | ret += cnt; | |
583 | ||
bc250684 | 584 | if (ubuf->pos > ubuf->actual_size) |
11bd27b2 | 585 | printk(KERN_ERR "read() pos > actual, huh?\n"); |
11bd27b2 ST |
586 | |
587 | if (ubuf->pos == ubuf->actual_size) { | |
588 | ||
589 | /* finished with current buffer, take next buffer */ | |
590 | ||
591 | /* Requeue the buffer on the free list */ | |
592 | ubuf->pos = 0; | |
593 | ||
594 | mutex_lock(&port->dmaqueue_lock); | |
595 | list_move_tail(&ubuf->list, &port->list_buf_free.list); | |
596 | mutex_unlock(&port->dmaqueue_lock); | |
597 | ||
598 | /* Dequeue next */ | |
599 | if ((file->f_flags & O_NONBLOCK) == 0) { | |
600 | if (wait_event_interruptible(port->wait_read, | |
601 | saa7164_vbi_next_buf(port))) { | |
602 | break; | |
603 | } | |
604 | } | |
605 | ubuf = saa7164_vbi_next_buf(port); | |
606 | } | |
607 | } | |
608 | err: | |
609 | if (!ret && !ubuf) { | |
610 | printk(KERN_ERR "%s() EAGAIN\n", __func__); | |
611 | ret = -EAGAIN; | |
612 | } | |
613 | ||
614 | return ret; | |
615 | } | |
616 | ||
c23e0cb8 | 617 | static __poll_t fops_poll(struct file *file, poll_table *wait) |
11bd27b2 ST |
618 | { |
619 | struct saa7164_vbi_fh *fh = (struct saa7164_vbi_fh *)file->private_data; | |
620 | struct saa7164_port *port = fh->port; | |
c23e0cb8 | 621 | __poll_t mask = 0; |
11bd27b2 ST |
622 | |
623 | port->last_poll_msecs_diff = port->last_poll_msecs; | |
624 | port->last_poll_msecs = jiffies_to_msecs(jiffies); | |
625 | port->last_poll_msecs_diff = port->last_poll_msecs - | |
626 | port->last_poll_msecs_diff; | |
627 | ||
628 | saa7164_histogram_update(&port->poll_interval, | |
629 | port->last_poll_msecs_diff); | |
630 | ||
bc250684 | 631 | if (!video_is_registered(port->v4l_device)) |
11bd27b2 | 632 | return -EIO; |
11bd27b2 ST |
633 | |
634 | if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { | |
635 | if (atomic_inc_return(&port->v4l_reader_count) == 1) { | |
636 | if (saa7164_vbi_initialize(port) < 0) | |
637 | return -EINVAL; | |
638 | saa7164_vbi_start_streaming(port); | |
639 | msleep(200); | |
640 | } | |
641 | } | |
642 | ||
643 | /* blocking wait for buffer */ | |
644 | if ((file->f_flags & O_NONBLOCK) == 0) { | |
645 | if (wait_event_interruptible(port->wait_read, | |
646 | saa7164_vbi_next_buf(port))) { | |
647 | return -ERESTARTSYS; | |
648 | } | |
649 | } | |
650 | ||
651 | /* Pull the first buffer from the used list */ | |
061d55eb | 652 | if (!list_empty(&port->list_buf_used.list)) |
a9a08845 | 653 | mask |= EPOLLIN | EPOLLRDNORM; |
11bd27b2 ST |
654 | |
655 | return mask; | |
656 | } | |
657 | static const struct v4l2_file_operations vbi_fops = { | |
658 | .owner = THIS_MODULE, | |
659 | .open = fops_open, | |
660 | .release = fops_release, | |
661 | .read = fops_read, | |
662 | .poll = fops_poll, | |
663 | .unlocked_ioctl = video_ioctl2, | |
664 | }; | |
665 | ||
666 | static const struct v4l2_ioctl_ops vbi_ioctl_ops = { | |
667 | .vidioc_s_std = vidioc_s_std, | |
8d2d41e9 | 668 | .vidioc_g_std = vidioc_g_std, |
225b783b | 669 | .vidioc_enum_input = saa7164_enum_input, |
11bd27b2 ST |
670 | .vidioc_g_input = vidioc_g_input, |
671 | .vidioc_s_input = vidioc_s_input, | |
225b783b HV |
672 | .vidioc_g_tuner = saa7164_g_tuner, |
673 | .vidioc_s_tuner = saa7164_s_tuner, | |
11bd27b2 ST |
674 | .vidioc_g_frequency = vidioc_g_frequency, |
675 | .vidioc_s_frequency = vidioc_s_frequency, | |
11bd27b2 | 676 | .vidioc_querycap = vidioc_querycap, |
11bd27b2 ST |
677 | .vidioc_g_fmt_vbi_cap = saa7164_vbi_fmt, |
678 | .vidioc_try_fmt_vbi_cap = saa7164_vbi_fmt, | |
679 | .vidioc_s_fmt_vbi_cap = saa7164_vbi_fmt, | |
680 | }; | |
681 | ||
682 | static struct video_device saa7164_vbi_template = { | |
683 | .name = "saa7164", | |
684 | .fops = &vbi_fops, | |
685 | .ioctl_ops = &vbi_ioctl_ops, | |
686 | .minor = -1, | |
687 | .tvnorms = SAA7164_NORMS, | |
11bd27b2 ST |
688 | }; |
689 | ||
690 | static struct video_device *saa7164_vbi_alloc( | |
691 | struct saa7164_port *port, | |
692 | struct pci_dev *pci, | |
693 | struct video_device *template, | |
694 | char *type) | |
695 | { | |
696 | struct video_device *vfd; | |
697 | struct saa7164_dev *dev = port->dev; | |
698 | ||
699 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | |
700 | ||
701 | vfd = video_device_alloc(); | |
702 | if (NULL == vfd) | |
703 | return NULL; | |
704 | ||
705 | *vfd = *template; | |
706 | snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, | |
707 | type, saa7164_boards[dev->board].name); | |
708 | ||
d66de790 | 709 | vfd->v4l2_dev = &dev->v4l2_dev; |
11bd27b2 ST |
710 | vfd->release = video_device_release; |
711 | return vfd; | |
712 | } | |
713 | ||
714 | int saa7164_vbi_register(struct saa7164_port *port) | |
715 | { | |
716 | struct saa7164_dev *dev = port->dev; | |
717 | int result = -ENODEV; | |
718 | ||
719 | dprintk(DBGLVL_VBI, "%s()\n", __func__); | |
720 | ||
721 | if (port->type != SAA7164_MPEG_VBI) | |
722 | BUG(); | |
723 | ||
724 | /* Sanity check that the PCI configuration space is active */ | |
725 | if (port->hwcfg.BARLocation == 0) { | |
24f711c1 | 726 | printk(KERN_ERR "%s() failed (errno = %d), NO PCI configuration\n", |
11bd27b2 ST |
727 | __func__, result); |
728 | result = -ENOMEM; | |
729 | goto failed; | |
730 | } | |
731 | ||
732 | /* Establish VBI defaults here */ | |
733 | ||
734 | /* Allocate and register the video device node */ | |
735 | port->v4l_device = saa7164_vbi_alloc(port, | |
736 | dev->pci, &saa7164_vbi_template, "vbi"); | |
737 | ||
61ca1500 | 738 | if (!port->v4l_device) { |
11bd27b2 ST |
739 | printk(KERN_INFO "%s: can't allocate vbi device\n", |
740 | dev->name); | |
741 | result = -ENOMEM; | |
742 | goto failed; | |
743 | } | |
744 | ||
225b783b | 745 | port->enc_port = &dev->ports[port->nr - 2]; |
214ce3fa | 746 | video_set_drvdata(port->v4l_device, port); |
11bd27b2 ST |
747 | result = video_register_device(port->v4l_device, |
748 | VFL_TYPE_VBI, -1); | |
749 | if (result < 0) { | |
750 | printk(KERN_INFO "%s: can't register vbi device\n", | |
751 | dev->name); | |
752 | /* TODO: We're going to leak here if we don't dealloc | |
753 | The buffers above. The unreg function can't deal wit it. | |
754 | */ | |
755 | goto failed; | |
756 | } | |
757 | ||
758 | printk(KERN_INFO "%s: registered device vbi%d [vbi]\n", | |
759 | dev->name, port->v4l_device->num); | |
760 | ||
761 | /* Configure the hardware defaults */ | |
762 | ||
763 | result = 0; | |
764 | failed: | |
765 | return result; | |
766 | } | |
767 | ||
768 | void saa7164_vbi_unregister(struct saa7164_port *port) | |
769 | { | |
770 | struct saa7164_dev *dev = port->dev; | |
771 | ||
772 | dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr); | |
773 | ||
774 | if (port->type != SAA7164_MPEG_VBI) | |
775 | BUG(); | |
776 | ||
777 | if (port->v4l_device) { | |
778 | if (port->v4l_device->minor != -1) | |
779 | video_unregister_device(port->v4l_device); | |
780 | else | |
781 | video_device_release(port->v4l_device); | |
782 | ||
783 | port->v4l_device = NULL; | |
784 | } | |
785 | ||
786 | } |