1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * |
4 | * device driver for philips saa7134 based TV cards |
5 | * video4linux video interface |
6 | * |
7 | * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] |
8 | */ |
9 | |
10 | #include "saa7134.h" |
11 | #include "saa7134-reg.h" |
12 | |
13 | #include <linux/init.h> |
14 | #include <linux/list.h> |
15 | #include <linux/module.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/delay.h> |
18 | |
19 | /* ------------------------------------------------------------------ */ |
20 | |
21 | static unsigned int ts_debug; |
22 | module_param(ts_debug, int, 0644); |
23 | MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]" ); |
24 | |
25 | #define ts_dbg(fmt, arg...) do { \ |
26 | if (ts_debug) \ |
27 | printk(KERN_DEBUG pr_fmt("ts: " fmt), ## arg); \ |
28 | } while (0) |
29 | |
30 | /* ------------------------------------------------------------------ */ |
31 | static int buffer_activate(struct saa7134_dev *dev, |
32 | struct saa7134_buf *buf, |
33 | struct saa7134_buf *next) |
34 | { |
35 | |
36 | ts_dbg("buffer_activate [%p]" , buf); |
37 | buf->top_seen = 0; |
38 | |
39 | if (!dev->ts_started) |
40 | dev->ts_field = V4L2_FIELD_TOP; |
41 | |
42 | if (NULL == next) |
43 | next = buf; |
44 | if (V4L2_FIELD_TOP == dev->ts_field) { |
45 | ts_dbg("- [top] buf=%p next=%p\n" , buf, next); |
46 | saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf)); |
47 | saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next)); |
48 | dev->ts_field = V4L2_FIELD_BOTTOM; |
49 | } else { |
50 | ts_dbg("- [bottom] buf=%p next=%p\n" , buf, next); |
51 | saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next)); |
52 | saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf)); |
53 | dev->ts_field = V4L2_FIELD_TOP; |
54 | } |
55 | |
56 | /* start DMA */ |
57 | saa7134_set_dmabits(dev); |
58 | |
59 | mod_timer(timer: &dev->ts_q.timeout, expires: jiffies+TS_BUFFER_TIMEOUT); |
60 | |
61 | if (!dev->ts_started) |
62 | saa7134_ts_start(dev); |
63 | |
64 | return 0; |
65 | } |
66 | |
67 | int saa7134_ts_buffer_init(struct vb2_buffer *vb2) |
68 | { |
69 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); |
70 | struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; |
71 | struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2); |
72 | |
73 | dmaq->curr = NULL; |
74 | buf->activate = buffer_activate; |
75 | |
76 | return 0; |
77 | } |
78 | EXPORT_SYMBOL_GPL(saa7134_ts_buffer_init); |
79 | |
80 | int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2) |
81 | { |
82 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2); |
83 | struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; |
84 | struct saa7134_dev *dev = dmaq->dev; |
85 | struct saa7134_buf *buf = container_of(vbuf, struct saa7134_buf, vb2); |
86 | struct sg_table *dma = vb2_dma_sg_plane_desc(vb: vb2, plane_no: 0); |
87 | unsigned int lines, llength, size; |
88 | |
89 | ts_dbg("buffer_prepare [%p]\n" , buf); |
90 | |
91 | llength = TS_PACKET_SIZE; |
92 | lines = dev->ts.nr_packets; |
93 | |
94 | size = lines * llength; |
95 | if (vb2_plane_size(vb: vb2, plane_no: 0) < size) |
96 | return -EINVAL; |
97 | |
98 | vb2_set_plane_payload(vb: vb2, plane_no: 0, size); |
99 | vbuf->field = dev->field; |
100 | |
101 | return saa7134_pgtable_build(pci: dev->pci, pt: &dmaq->pt, list: dma->sgl, length: dma->nents, |
102 | startpage: saa7134_buffer_startpage(buf)); |
103 | } |
104 | EXPORT_SYMBOL_GPL(saa7134_ts_buffer_prepare); |
105 | |
106 | int saa7134_ts_queue_setup(struct vb2_queue *q, |
107 | unsigned int *nbuffers, unsigned int *nplanes, |
108 | unsigned int sizes[], struct device *alloc_devs[]) |
109 | { |
110 | struct saa7134_dmaqueue *dmaq = q->drv_priv; |
111 | struct saa7134_dev *dev = dmaq->dev; |
112 | int size = TS_PACKET_SIZE * dev->ts.nr_packets; |
113 | |
114 | if (0 == *nbuffers) |
115 | *nbuffers = dev->ts.nr_bufs; |
116 | *nbuffers = saa7134_buffer_count(size, count: *nbuffers); |
117 | if (*nbuffers < 3) |
118 | *nbuffers = 3; |
119 | *nplanes = 1; |
120 | sizes[0] = size; |
121 | return 0; |
122 | } |
123 | EXPORT_SYMBOL_GPL(saa7134_ts_queue_setup); |
124 | |
125 | int saa7134_ts_start_streaming(struct vb2_queue *vq, unsigned int count) |
126 | { |
127 | struct saa7134_dmaqueue *dmaq = vq->drv_priv; |
128 | struct saa7134_dev *dev = dmaq->dev; |
129 | |
130 | /* |
131 | * Planar video capture and TS share the same DMA channel, |
132 | * so only one can be active at a time. |
133 | */ |
134 | if (vb2_is_busy(q: &dev->video_vbq) && dev->fmt->planar) { |
135 | struct saa7134_buf *buf, *tmp; |
136 | |
137 | list_for_each_entry_safe(buf, tmp, &dmaq->queue, entry) { |
138 | list_del(entry: &buf->entry); |
139 | vb2_buffer_done(vb: &buf->vb2.vb2_buf, |
140 | state: VB2_BUF_STATE_QUEUED); |
141 | } |
142 | if (dmaq->curr) { |
143 | vb2_buffer_done(vb: &dmaq->curr->vb2.vb2_buf, |
144 | state: VB2_BUF_STATE_QUEUED); |
145 | dmaq->curr = NULL; |
146 | } |
147 | return -EBUSY; |
148 | } |
149 | dmaq->seq_nr = 0; |
150 | return 0; |
151 | } |
152 | EXPORT_SYMBOL_GPL(saa7134_ts_start_streaming); |
153 | |
154 | void saa7134_ts_stop_streaming(struct vb2_queue *vq) |
155 | { |
156 | struct saa7134_dmaqueue *dmaq = vq->drv_priv; |
157 | struct saa7134_dev *dev = dmaq->dev; |
158 | |
159 | saa7134_ts_stop(dev); |
160 | saa7134_stop_streaming(dev, q: dmaq); |
161 | } |
162 | EXPORT_SYMBOL_GPL(saa7134_ts_stop_streaming); |
163 | |
164 | struct vb2_ops saa7134_ts_qops = { |
165 | .queue_setup = saa7134_ts_queue_setup, |
166 | .buf_init = saa7134_ts_buffer_init, |
167 | .buf_prepare = saa7134_ts_buffer_prepare, |
168 | .buf_queue = saa7134_vb2_buffer_queue, |
169 | .wait_prepare = vb2_ops_wait_prepare, |
170 | .wait_finish = vb2_ops_wait_finish, |
171 | .stop_streaming = saa7134_ts_stop_streaming, |
172 | }; |
173 | EXPORT_SYMBOL_GPL(saa7134_ts_qops); |
174 | |
175 | /* ----------------------------------------------------------- */ |
176 | /* exported stuff */ |
177 | |
178 | static unsigned int tsbufs = 8; |
179 | module_param(tsbufs, int, 0444); |
180 | MODULE_PARM_DESC(tsbufs, "number of ts buffers for read/write IO, range 2-32" ); |
181 | |
182 | static unsigned int ts_nr_packets = 64; |
183 | module_param(ts_nr_packets, int, 0444); |
184 | MODULE_PARM_DESC(ts_nr_packets,"size of a ts buffers (in ts packets)" ); |
185 | |
186 | int saa7134_ts_init_hw(struct saa7134_dev *dev) |
187 | { |
188 | /* deactivate TS softreset */ |
189 | saa_writeb(SAA7134_TS_SERIAL1, 0x00); |
190 | /* TSSOP high active, TSVAL high active, TSLOCK ignored */ |
191 | saa_writeb(SAA7134_TS_PARALLEL, 0x6c); |
192 | saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1)); |
193 | saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff)); |
194 | saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff)); |
195 | /* TSNOPIT=0, TSCOLAP=0 */ |
196 | saa_writeb(SAA7134_TS_DMA2, |
197 | ((((dev->ts.nr_packets-1)>>16)&0x3f) | 0x00)); |
198 | |
199 | return 0; |
200 | } |
201 | |
202 | int saa7134_ts_init1(struct saa7134_dev *dev) |
203 | { |
204 | /* sanitycheck insmod options */ |
205 | if (tsbufs < 2) |
206 | tsbufs = 2; |
207 | if (tsbufs > VIDEO_MAX_FRAME) |
208 | tsbufs = VIDEO_MAX_FRAME; |
209 | if (ts_nr_packets < 4) |
210 | ts_nr_packets = 4; |
211 | if (ts_nr_packets > 312) |
212 | ts_nr_packets = 312; |
213 | dev->ts.nr_bufs = tsbufs; |
214 | dev->ts.nr_packets = ts_nr_packets; |
215 | |
216 | INIT_LIST_HEAD(list: &dev->ts_q.queue); |
217 | timer_setup(&dev->ts_q.timeout, saa7134_buffer_timeout, 0); |
218 | dev->ts_q.dev = dev; |
219 | dev->ts_q.need_two = 1; |
220 | dev->ts_started = 0; |
221 | saa7134_pgtable_alloc(pci: dev->pci, pt: &dev->ts_q.pt); |
222 | |
223 | /* init TS hw */ |
224 | saa7134_ts_init_hw(dev); |
225 | |
226 | return 0; |
227 | } |
228 | |
229 | /* Function for stop TS */ |
230 | int saa7134_ts_stop(struct saa7134_dev *dev) |
231 | { |
232 | ts_dbg("TS stop\n" ); |
233 | |
234 | if (!dev->ts_started) |
235 | return 0; |
236 | |
237 | /* Stop TS stream */ |
238 | switch (saa7134_boards[dev->board].ts_type) { |
239 | case SAA7134_MPEG_TS_PARALLEL: |
240 | saa_writeb(SAA7134_TS_PARALLEL, 0x6c); |
241 | dev->ts_started = 0; |
242 | break; |
243 | case SAA7134_MPEG_TS_SERIAL: |
244 | saa_writeb(SAA7134_TS_SERIAL0, 0x40); |
245 | dev->ts_started = 0; |
246 | break; |
247 | } |
248 | return 0; |
249 | } |
250 | |
251 | /* Function for start TS */ |
252 | int saa7134_ts_start(struct saa7134_dev *dev) |
253 | { |
254 | ts_dbg("TS start\n" ); |
255 | |
256 | if (WARN_ON(dev->ts_started)) |
257 | return 0; |
258 | |
259 | /* dma: setup channel 5 (= TS) */ |
260 | saa_writeb(SAA7134_TS_DMA0, (dev->ts.nr_packets - 1) & 0xff); |
261 | saa_writeb(SAA7134_TS_DMA1, |
262 | ((dev->ts.nr_packets - 1) >> 8) & 0xff); |
263 | /* TSNOPIT=0, TSCOLAP=0 */ |
264 | saa_writeb(SAA7134_TS_DMA2, |
265 | (((dev->ts.nr_packets - 1) >> 16) & 0x3f) | 0x00); |
266 | saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE); |
267 | saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_16 | |
268 | SAA7134_RS_CONTROL_ME | |
269 | (dev->ts_q.pt.dma >> 12)); |
270 | |
271 | /* reset hardware TS buffers */ |
272 | saa_writeb(SAA7134_TS_SERIAL1, 0x00); |
273 | saa_writeb(SAA7134_TS_SERIAL1, 0x03); |
274 | saa_writeb(SAA7134_TS_SERIAL1, 0x00); |
275 | saa_writeb(SAA7134_TS_SERIAL1, 0x01); |
276 | |
277 | /* TS clock non-inverted */ |
278 | saa_writeb(SAA7134_TS_SERIAL1, 0x00); |
279 | |
280 | /* Start TS stream */ |
281 | switch (saa7134_boards[dev->board].ts_type) { |
282 | case SAA7134_MPEG_TS_PARALLEL: |
283 | saa_writeb(SAA7134_TS_SERIAL0, 0x40); |
284 | saa_writeb(SAA7134_TS_PARALLEL, 0xec | |
285 | (saa7134_boards[dev->board].ts_force_val << 4)); |
286 | break; |
287 | case SAA7134_MPEG_TS_SERIAL: |
288 | saa_writeb(SAA7134_TS_SERIAL0, 0xd8); |
289 | saa_writeb(SAA7134_TS_PARALLEL, 0x6c | |
290 | (saa7134_boards[dev->board].ts_force_val << 4)); |
291 | saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc); |
292 | saa_writeb(SAA7134_TS_SERIAL1, 0x02); |
293 | break; |
294 | } |
295 | |
296 | dev->ts_started = 1; |
297 | |
298 | return 0; |
299 | } |
300 | |
301 | int saa7134_ts_fini(struct saa7134_dev *dev) |
302 | { |
303 | del_timer_sync(timer: &dev->ts_q.timeout); |
304 | saa7134_pgtable_free(pci: dev->pci, pt: &dev->ts_q.pt); |
305 | return 0; |
306 | } |
307 | |
308 | void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status) |
309 | { |
310 | enum v4l2_field field; |
311 | |
312 | spin_lock(lock: &dev->slock); |
313 | if (dev->ts_q.curr) { |
314 | field = dev->ts_field; |
315 | if (field != V4L2_FIELD_TOP) { |
316 | if ((status & 0x100000) != 0x000000) |
317 | goto done; |
318 | } else { |
319 | if ((status & 0x100000) != 0x100000) |
320 | goto done; |
321 | } |
322 | saa7134_buffer_finish(dev, q: &dev->ts_q, state: VB2_BUF_STATE_DONE); |
323 | } |
324 | saa7134_buffer_next(dev,q: &dev->ts_q); |
325 | |
326 | done: |
327 | spin_unlock(lock: &dev->slock); |
328 | } |
329 | |