1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Support for a cx23416 mpeg encoder via cx2388x host port. |
4 | * "blackbird" reference design. |
5 | * |
6 | * (c) 2004 Jelle Foks <jelle@foks.us> |
7 | * (c) 2004 Gerd Knorr <kraxel@bytesex.org> |
8 | * |
9 | * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@kernel.org> |
10 | * - video_ioctl2 conversion |
11 | * |
12 | * Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/> |
13 | */ |
14 | |
15 | #include "cx88.h" |
16 | |
17 | #include <linux/module.h> |
18 | #include <linux/init.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/fs.h> |
21 | #include <linux/delay.h> |
22 | #include <linux/device.h> |
23 | #include <linux/firmware.h> |
24 | #include <media/v4l2-common.h> |
25 | #include <media/v4l2-ioctl.h> |
26 | #include <media/v4l2-event.h> |
27 | #include <media/drv-intf/cx2341x.h> |
28 | |
29 | MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards" ); |
30 | MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]" ); |
31 | MODULE_LICENSE("GPL v2" ); |
32 | MODULE_VERSION(CX88_VERSION); |
33 | |
34 | static unsigned int debug; |
35 | module_param(debug, int, 0644); |
36 | MODULE_PARM_DESC(debug, "enable debug messages [blackbird]" ); |
37 | |
38 | #define dprintk(level, fmt, arg...) do { \ |
39 | if (debug + 1 > level) \ |
40 | printk(KERN_DEBUG pr_fmt("%s: blackbird:" fmt), \ |
41 | __func__, ##arg); \ |
42 | } while (0) |
43 | |
44 | /* ------------------------------------------------------------------ */ |
45 | |
46 | #define BLACKBIRD_FIRM_IMAGE_SIZE 376836 |
47 | |
48 | /* defines below are from ivtv-driver.h */ |
49 | |
50 | #define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF |
51 | |
52 | /* Firmware API commands */ |
53 | #define IVTV_API_STD_TIMEOUT 500 |
54 | |
55 | enum blackbird_capture_type { |
56 | BLACKBIRD_MPEG_CAPTURE, |
57 | BLACKBIRD_RAW_CAPTURE, |
58 | BLACKBIRD_RAW_PASSTHRU_CAPTURE |
59 | }; |
60 | |
61 | enum blackbird_capture_bits { |
62 | BLACKBIRD_RAW_BITS_NONE = 0x00, |
63 | BLACKBIRD_RAW_BITS_YUV_CAPTURE = 0x01, |
64 | BLACKBIRD_RAW_BITS_PCM_CAPTURE = 0x02, |
65 | BLACKBIRD_RAW_BITS_VBI_CAPTURE = 0x04, |
66 | BLACKBIRD_RAW_BITS_PASSTHRU_CAPTURE = 0x08, |
67 | BLACKBIRD_RAW_BITS_TO_HOST_CAPTURE = 0x10 |
68 | }; |
69 | |
70 | enum blackbird_capture_end { |
71 | BLACKBIRD_END_AT_GOP, /* stop at the end of gop, generate irq */ |
72 | BLACKBIRD_END_NOW, /* stop immediately, no irq */ |
73 | }; |
74 | |
75 | enum blackbird_framerate { |
76 | BLACKBIRD_FRAMERATE_NTSC_30, /* NTSC: 30fps */ |
77 | BLACKBIRD_FRAMERATE_PAL_25 /* PAL: 25fps */ |
78 | }; |
79 | |
80 | enum blackbird_stream_port { |
81 | BLACKBIRD_OUTPUT_PORT_MEMORY, |
82 | BLACKBIRD_OUTPUT_PORT_STREAMING, |
83 | BLACKBIRD_OUTPUT_PORT_SERIAL |
84 | }; |
85 | |
86 | enum blackbird_data_xfer_status { |
87 | BLACKBIRD_MORE_BUFFERS_FOLLOW, |
88 | BLACKBIRD_LAST_BUFFER, |
89 | }; |
90 | |
91 | enum blackbird_picture_mask { |
92 | BLACKBIRD_PICTURE_MASK_NONE, |
93 | BLACKBIRD_PICTURE_MASK_I_FRAMES, |
94 | BLACKBIRD_PICTURE_MASK_I_P_FRAMES = 0x3, |
95 | BLACKBIRD_PICTURE_MASK_ALL_FRAMES = 0x7, |
96 | }; |
97 | |
98 | enum blackbird_vbi_mode_bits { |
99 | BLACKBIRD_VBI_BITS_SLICED, |
100 | BLACKBIRD_VBI_BITS_RAW, |
101 | }; |
102 | |
103 | enum blackbird_vbi_insertion_bits { |
104 | BLACKBIRD_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, |
105 | BLACKBIRD_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, |
106 | BLACKBIRD_VBI_BITS_SEPARATE_STREAM = 0x2 << 1, |
107 | BLACKBIRD_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, |
108 | BLACKBIRD_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, |
109 | }; |
110 | |
111 | enum blackbird_dma_unit { |
112 | BLACKBIRD_DMA_BYTES, |
113 | BLACKBIRD_DMA_FRAMES, |
114 | }; |
115 | |
116 | enum blackbird_dma_transfer_status_bits { |
117 | BLACKBIRD_DMA_TRANSFER_BITS_DONE = 0x01, |
118 | BLACKBIRD_DMA_TRANSFER_BITS_ERROR = 0x04, |
119 | BLACKBIRD_DMA_TRANSFER_BITS_LL_ERROR = 0x10, |
120 | }; |
121 | |
122 | enum blackbird_pause { |
123 | BLACKBIRD_PAUSE_ENCODING, |
124 | BLACKBIRD_RESUME_ENCODING, |
125 | }; |
126 | |
127 | enum blackbird_copyright { |
128 | BLACKBIRD_COPYRIGHT_OFF, |
129 | BLACKBIRD_COPYRIGHT_ON, |
130 | }; |
131 | |
132 | enum blackbird_notification_type { |
133 | BLACKBIRD_NOTIFICATION_REFRESH, |
134 | }; |
135 | |
136 | enum blackbird_notification_status { |
137 | BLACKBIRD_NOTIFICATION_OFF, |
138 | BLACKBIRD_NOTIFICATION_ON, |
139 | }; |
140 | |
141 | enum blackbird_notification_mailbox { |
142 | BLACKBIRD_NOTIFICATION_NO_MAILBOX = -1, |
143 | }; |
144 | |
145 | enum blackbird_field1_lines { |
146 | BLACKBIRD_FIELD1_SAA7114 = 0x00EF, /* 239 */ |
147 | BLACKBIRD_FIELD1_SAA7115 = 0x00F0, /* 240 */ |
148 | BLACKBIRD_FIELD1_MICRONAS = 0x0105, /* 261 */ |
149 | }; |
150 | |
151 | enum blackbird_field2_lines { |
152 | BLACKBIRD_FIELD2_SAA7114 = 0x00EF, /* 239 */ |
153 | BLACKBIRD_FIELD2_SAA7115 = 0x00F0, /* 240 */ |
154 | BLACKBIRD_FIELD2_MICRONAS = 0x0106, /* 262 */ |
155 | }; |
156 | |
157 | enum blackbird_custom_data_type { |
158 | BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, |
159 | BLACKBIRD_CUSTOM_PRIVATE_PACKET, |
160 | }; |
161 | |
162 | enum blackbird_mute { |
163 | BLACKBIRD_UNMUTE, |
164 | BLACKBIRD_MUTE, |
165 | }; |
166 | |
167 | enum blackbird_mute_video_mask { |
168 | BLACKBIRD_MUTE_VIDEO_V_MASK = 0x0000FF00, |
169 | BLACKBIRD_MUTE_VIDEO_U_MASK = 0x00FF0000, |
170 | BLACKBIRD_MUTE_VIDEO_Y_MASK = 0xFF000000, |
171 | }; |
172 | |
173 | enum blackbird_mute_video_shift { |
174 | BLACKBIRD_MUTE_VIDEO_V_SHIFT = 8, |
175 | BLACKBIRD_MUTE_VIDEO_U_SHIFT = 16, |
176 | BLACKBIRD_MUTE_VIDEO_Y_SHIFT = 24, |
177 | }; |
178 | |
179 | /* Registers */ |
180 | #define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/) |
181 | #define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/) |
182 | #define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/) |
183 | #define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/) |
184 | #define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/) |
185 | #define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/) |
186 | |
187 | /* ------------------------------------------------------------------ */ |
188 | |
189 | static void host_setup(struct cx88_core *core) |
190 | { |
191 | /* toggle reset of the host */ |
192 | cx_write(MO_GPHST_SOFT_RST, 1); |
193 | udelay(100); |
194 | cx_write(MO_GPHST_SOFT_RST, 0); |
195 | udelay(100); |
196 | |
197 | /* host port setup */ |
198 | cx_write(MO_GPHST_WSC, 0x44444444U); |
199 | cx_write(MO_GPHST_XFR, 0); |
200 | cx_write(MO_GPHST_WDTH, 15); |
201 | cx_write(MO_GPHST_HDSHK, 0); |
202 | cx_write(MO_GPHST_MUX16, 0x44448888U); |
203 | cx_write(MO_GPHST_MODE, 0); |
204 | } |
205 | |
206 | /* ------------------------------------------------------------------ */ |
207 | |
208 | #define P1_MDATA0 0x390000 |
209 | #define P1_MDATA1 0x390001 |
210 | #define P1_MDATA2 0x390002 |
211 | #define P1_MDATA3 0x390003 |
212 | #define P1_MADDR2 0x390004 |
213 | #define P1_MADDR1 0x390005 |
214 | #define P1_MADDR0 0x390006 |
215 | #define P1_RDATA0 0x390008 |
216 | #define P1_RDATA1 0x390009 |
217 | #define P1_RDATA2 0x39000A |
218 | #define P1_RDATA3 0x39000B |
219 | #define P1_RADDR0 0x39000C |
220 | #define P1_RADDR1 0x39000D |
221 | #define P1_RRDWR 0x39000E |
222 | |
223 | static int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state) |
224 | { |
225 | unsigned long timeout = jiffies + msecs_to_jiffies(m: 1); |
226 | u32 gpio0, need; |
227 | |
228 | need = state ? 2 : 0; |
229 | for (;;) { |
230 | gpio0 = cx_read(MO_GP0_IO) & 2; |
231 | if (need == gpio0) |
232 | return 0; |
233 | if (time_after(jiffies, timeout)) |
234 | return -1; |
235 | udelay(1); |
236 | } |
237 | } |
238 | |
239 | static int memory_write(struct cx88_core *core, u32 address, u32 value) |
240 | { |
241 | /* Warning: address is dword address (4 bytes) */ |
242 | cx_writeb(P1_MDATA0, (unsigned int)value); |
243 | cx_writeb(P1_MDATA1, (unsigned int)(value >> 8)); |
244 | cx_writeb(P1_MDATA2, (unsigned int)(value >> 16)); |
245 | cx_writeb(P1_MDATA3, (unsigned int)(value >> 24)); |
246 | cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40); |
247 | cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); |
248 | cx_writeb(P1_MADDR0, (unsigned int)address); |
249 | cx_read(P1_MDATA0); |
250 | cx_read(P1_MADDR0); |
251 | |
252 | return wait_ready_gpio0_bit1(core, state: 1); |
253 | } |
254 | |
255 | static int memory_read(struct cx88_core *core, u32 address, u32 *value) |
256 | { |
257 | int retval; |
258 | u32 val; |
259 | |
260 | /* Warning: address is dword address (4 bytes) */ |
261 | cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0); |
262 | cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); |
263 | cx_writeb(P1_MADDR0, (unsigned int)address); |
264 | cx_read(P1_MADDR0); |
265 | |
266 | retval = wait_ready_gpio0_bit1(core, state: 1); |
267 | |
268 | cx_writeb(P1_MDATA3, 0); |
269 | val = (unsigned char)cx_read(P1_MDATA3) << 24; |
270 | cx_writeb(P1_MDATA2, 0); |
271 | val |= (unsigned char)cx_read(P1_MDATA2) << 16; |
272 | cx_writeb(P1_MDATA1, 0); |
273 | val |= (unsigned char)cx_read(P1_MDATA1) << 8; |
274 | cx_writeb(P1_MDATA0, 0); |
275 | val |= (unsigned char)cx_read(P1_MDATA0); |
276 | |
277 | *value = val; |
278 | return retval; |
279 | } |
280 | |
281 | static int register_write(struct cx88_core *core, u32 address, u32 value) |
282 | { |
283 | cx_writeb(P1_RDATA0, (unsigned int)value); |
284 | cx_writeb(P1_RDATA1, (unsigned int)(value >> 8)); |
285 | cx_writeb(P1_RDATA2, (unsigned int)(value >> 16)); |
286 | cx_writeb(P1_RDATA3, (unsigned int)(value >> 24)); |
287 | cx_writeb(P1_RADDR0, (unsigned int)address); |
288 | cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); |
289 | cx_writeb(P1_RRDWR, 1); |
290 | cx_read(P1_RDATA0); |
291 | cx_read(P1_RADDR0); |
292 | |
293 | return wait_ready_gpio0_bit1(core, state: 1); |
294 | } |
295 | |
296 | static int register_read(struct cx88_core *core, u32 address, u32 *value) |
297 | { |
298 | int retval; |
299 | u32 val; |
300 | |
301 | cx_writeb(P1_RADDR0, (unsigned int)address); |
302 | cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); |
303 | cx_writeb(P1_RRDWR, 0); |
304 | cx_read(P1_RADDR0); |
305 | |
306 | retval = wait_ready_gpio0_bit1(core, state: 1); |
307 | val = (unsigned char)cx_read(P1_RDATA0); |
308 | val |= (unsigned char)cx_read(P1_RDATA1) << 8; |
309 | val |= (unsigned char)cx_read(P1_RDATA2) << 16; |
310 | val |= (unsigned char)cx_read(P1_RDATA3) << 24; |
311 | |
312 | *value = val; |
313 | return retval; |
314 | } |
315 | |
316 | /* ------------------------------------------------------------------ */ |
317 | |
318 | static int blackbird_mbox_func(void *priv, u32 command, int in, |
319 | int out, u32 data[CX2341X_MBOX_MAX_DATA]) |
320 | { |
321 | struct cx8802_dev *dev = priv; |
322 | unsigned long timeout; |
323 | u32 value, flag, retval; |
324 | int i; |
325 | |
326 | dprintk(1, "%s: 0x%X\n" , __func__, command); |
327 | |
328 | /* |
329 | * this may not be 100% safe if we can't read any memory location |
330 | * without side effects |
331 | */ |
332 | memory_read(core: dev->core, address: dev->mailbox - 4, value: &value); |
333 | if (value != 0x12345678) { |
334 | dprintk(0, |
335 | "Firmware and/or mailbox pointer not initialized or corrupted\n" ); |
336 | return -EIO; |
337 | } |
338 | |
339 | memory_read(core: dev->core, address: dev->mailbox, value: &flag); |
340 | if (flag) { |
341 | dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n" , flag); |
342 | return -EIO; |
343 | } |
344 | |
345 | flag |= 1; /* tell 'em we're working on it */ |
346 | memory_write(core: dev->core, address: dev->mailbox, value: flag); |
347 | |
348 | /* write command + args + fill remaining with zeros */ |
349 | memory_write(core: dev->core, address: dev->mailbox + 1, value: command); /* command code */ |
350 | /* timeout */ |
351 | memory_write(core: dev->core, address: dev->mailbox + 3, IVTV_API_STD_TIMEOUT); |
352 | for (i = 0; i < in; i++) { |
353 | memory_write(core: dev->core, address: dev->mailbox + 4 + i, value: data[i]); |
354 | dprintk(1, "API Input %d = %d\n" , i, data[i]); |
355 | } |
356 | for (; i < CX2341X_MBOX_MAX_DATA; i++) |
357 | memory_write(core: dev->core, address: dev->mailbox + 4 + i, value: 0); |
358 | |
359 | flag |= 3; /* tell 'em we're done writing */ |
360 | memory_write(core: dev->core, address: dev->mailbox, value: flag); |
361 | |
362 | /* wait for firmware to handle the API command */ |
363 | timeout = jiffies + msecs_to_jiffies(m: 1000); |
364 | for (;;) { |
365 | memory_read(core: dev->core, address: dev->mailbox, value: &flag); |
366 | if (0 != (flag & 4)) |
367 | break; |
368 | if (time_after(jiffies, timeout)) { |
369 | dprintk(0, "ERROR: API Mailbox timeout %x\n" , command); |
370 | return -EIO; |
371 | } |
372 | udelay(10); |
373 | } |
374 | |
375 | /* read output values */ |
376 | for (i = 0; i < out; i++) { |
377 | memory_read(core: dev->core, address: dev->mailbox + 4 + i, value: data + i); |
378 | dprintk(1, "API Output %d = %d\n" , i, data[i]); |
379 | } |
380 | |
381 | memory_read(core: dev->core, address: dev->mailbox + 2, value: &retval); |
382 | dprintk(1, "API result = %d\n" , retval); |
383 | |
384 | flag = 0; |
385 | memory_write(core: dev->core, address: dev->mailbox, value: flag); |
386 | return retval; |
387 | } |
388 | |
389 | /* ------------------------------------------------------------------ */ |
390 | |
391 | /* |
392 | * We don't need to call the API often, so using just one mailbox |
393 | * will probably suffice |
394 | */ |
395 | static int blackbird_api_cmd(struct cx8802_dev *dev, u32 command, |
396 | u32 inputcnt, u32 outputcnt, ...) |
397 | { |
398 | u32 data[CX2341X_MBOX_MAX_DATA]; |
399 | va_list vargs; |
400 | int i, err; |
401 | |
402 | va_start(vargs, outputcnt); |
403 | |
404 | for (i = 0; i < inputcnt; i++) |
405 | data[i] = va_arg(vargs, int); |
406 | |
407 | err = blackbird_mbox_func(priv: dev, command, in: inputcnt, out: outputcnt, data); |
408 | for (i = 0; i < outputcnt; i++) { |
409 | int *vptr = va_arg(vargs, int *); |
410 | *vptr = data[i]; |
411 | } |
412 | va_end(vargs); |
413 | return err; |
414 | } |
415 | |
416 | static int blackbird_find_mailbox(struct cx8802_dev *dev) |
417 | { |
418 | u32 signature[4] = {0x12345678, 0x34567812, 0x56781234, 0x78123456}; |
419 | int signaturecnt = 0; |
420 | u32 value; |
421 | int i; |
422 | |
423 | for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) { |
424 | memory_read(core: dev->core, address: i, value: &value); |
425 | if (value == signature[signaturecnt]) |
426 | signaturecnt++; |
427 | else |
428 | signaturecnt = 0; |
429 | if (signaturecnt == 4) { |
430 | dprintk(1, "Mailbox signature found\n" ); |
431 | return i + 1; |
432 | } |
433 | } |
434 | dprintk(0, "Mailbox signature values not found!\n" ); |
435 | return -EIO; |
436 | } |
437 | |
438 | static int blackbird_load_firmware(struct cx8802_dev *dev) |
439 | { |
440 | static const unsigned char magic[8] = { |
441 | 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa |
442 | }; |
443 | const struct firmware *firmware; |
444 | int i, retval = 0; |
445 | u32 value = 0; |
446 | u32 checksum = 0; |
447 | __le32 *dataptr; |
448 | |
449 | retval = register_write(core: dev->core, IVTV_REG_VPU, value: 0xFFFFFFED); |
450 | retval |= register_write(core: dev->core, IVTV_REG_HW_BLOCKS, |
451 | IVTV_CMD_HW_BLOCKS_RST); |
452 | retval |= register_write(core: dev->core, IVTV_REG_ENC_SDRAM_REFRESH, |
453 | value: 0x80000640); |
454 | retval |= register_write(core: dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, |
455 | value: 0x1A); |
456 | usleep_range(min: 10000, max: 20000); |
457 | retval |= register_write(core: dev->core, IVTV_REG_APU, value: 0); |
458 | |
459 | if (retval < 0) |
460 | dprintk(0, "Error with register_write\n" ); |
461 | |
462 | retval = request_firmware(fw: &firmware, CX2341X_FIRM_ENC_FILENAME, |
463 | device: &dev->pci->dev); |
464 | |
465 | if (retval != 0) { |
466 | pr_err("Hotplug firmware request failed (%s).\n" , |
467 | CX2341X_FIRM_ENC_FILENAME); |
468 | pr_err("Please fix your hotplug setup, the board will not work without firmware loaded!\n" ); |
469 | return -EIO; |
470 | } |
471 | |
472 | if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) { |
473 | pr_err("Firmware size mismatch (have %zd, expected %d)\n" , |
474 | firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE); |
475 | release_firmware(fw: firmware); |
476 | return -EINVAL; |
477 | } |
478 | |
479 | if (memcmp(p: firmware->data, q: magic, size: 8) != 0) { |
480 | pr_err("Firmware magic mismatch, wrong file?\n" ); |
481 | release_firmware(fw: firmware); |
482 | return -EINVAL; |
483 | } |
484 | |
485 | /* transfer to the chip */ |
486 | dprintk(1, "Loading firmware ...\n" ); |
487 | dataptr = (__le32 *)firmware->data; |
488 | for (i = 0; i < (firmware->size >> 2); i++) { |
489 | value = le32_to_cpu(*dataptr); |
490 | checksum += ~value; |
491 | memory_write(core: dev->core, address: i, value); |
492 | dataptr++; |
493 | } |
494 | |
495 | /* read back to verify with the checksum */ |
496 | for (i--; i >= 0; i--) { |
497 | memory_read(core: dev->core, address: i, value: &value); |
498 | checksum -= ~value; |
499 | } |
500 | release_firmware(fw: firmware); |
501 | if (checksum) { |
502 | pr_err("Firmware load might have failed (checksum mismatch).\n" ); |
503 | return -EIO; |
504 | } |
505 | dprintk(0, "Firmware upload successful.\n" ); |
506 | |
507 | retval |= register_write(core: dev->core, IVTV_REG_HW_BLOCKS, |
508 | IVTV_CMD_HW_BLOCKS_RST); |
509 | retval |= register_read(core: dev->core, IVTV_REG_SPU, value: &value); |
510 | retval |= register_write(core: dev->core, IVTV_REG_SPU, value: value & 0xFFFFFFFE); |
511 | usleep_range(min: 10000, max: 20000); |
512 | |
513 | retval |= register_read(core: dev->core, IVTV_REG_VPU, value: &value); |
514 | retval |= register_write(core: dev->core, IVTV_REG_VPU, value: value & 0xFFFFFFE8); |
515 | |
516 | if (retval < 0) |
517 | dprintk(0, "Error with register_write\n" ); |
518 | return 0; |
519 | } |
520 | |
521 | /* |
522 | * Settings used by the windows tv app for PVR2000: |
523 | * ================================================================================================================= |
524 | * Profile | Codec | Resolution | CBR/VBR | Video Qlty | V. Bitrate | Frmrate | Audio Codec | A. Bitrate | A. Mode |
525 | * ----------------------------------------------------------------------------------------------------------------- |
526 | * MPEG-1 | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 2000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo |
527 | * MPEG-2 | MPEG2 | 720x576PAL | VBR | 600 :Good | 4000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo |
528 | * VCD | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 1150 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo |
529 | * DVD | MPEG2 | 720x576PAL | VBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo |
530 | * DB* DVD | MPEG2 | 720x576PAL | CBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo |
531 | * ================================================================================================================= |
532 | * [*] DB: "DirectBurn" |
533 | */ |
534 | |
535 | static void blackbird_codec_settings(struct cx8802_dev *dev) |
536 | { |
537 | struct cx88_core *core = dev->core; |
538 | |
539 | /* assign frame size */ |
540 | blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, inputcnt: 2, outputcnt: 0, |
541 | core->height, core->width); |
542 | |
543 | dev->cxhdl.width = core->width; |
544 | dev->cxhdl.height = core->height; |
545 | cx2341x_handler_set_50hz(cxhdl: &dev->cxhdl, |
546 | is_50hz: dev->core->tvnorm & V4L2_STD_625_50); |
547 | cx2341x_handler_setup(cxhdl: &dev->cxhdl); |
548 | } |
549 | |
550 | static int blackbird_initialize_codec(struct cx8802_dev *dev) |
551 | { |
552 | struct cx88_core *core = dev->core; |
553 | int version; |
554 | int retval; |
555 | |
556 | dprintk(1, "Initialize codec\n" ); |
557 | retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, inputcnt: 0, outputcnt: 0); /* ping */ |
558 | if (retval < 0) { |
559 | /* ping was not successful, reset and upload firmware */ |
560 | cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */ |
561 | cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */ |
562 | retval = blackbird_load_firmware(dev); |
563 | if (retval < 0) |
564 | return retval; |
565 | |
566 | retval = blackbird_find_mailbox(dev); |
567 | if (retval < 0) |
568 | return -1; |
569 | |
570 | dev->mailbox = retval; |
571 | |
572 | /* ping */ |
573 | retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, inputcnt: 0, outputcnt: 0); |
574 | if (retval < 0) { |
575 | dprintk(0, "ERROR: Firmware ping failed!\n" ); |
576 | return -1; |
577 | } |
578 | |
579 | retval = blackbird_api_cmd(dev, CX2341X_ENC_GET_VERSION, |
580 | inputcnt: 0, outputcnt: 1, &version); |
581 | if (retval < 0) { |
582 | dprintk(0, |
583 | "ERROR: Firmware get encoder version failed!\n" ); |
584 | return -1; |
585 | } |
586 | dprintk(0, "Firmware version is 0x%08x\n" , version); |
587 | } |
588 | |
589 | cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */ |
590 | cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */ |
591 | cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */ |
592 | cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */ |
593 | |
594 | blackbird_codec_settings(dev); |
595 | |
596 | blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, inputcnt: 2, outputcnt: 0, |
597 | BLACKBIRD_FIELD1_SAA7115, BLACKBIRD_FIELD2_SAA7115); |
598 | |
599 | blackbird_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, inputcnt: 12, outputcnt: 0, |
600 | BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, |
601 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); |
602 | |
603 | return 0; |
604 | } |
605 | |
606 | static int blackbird_start_codec(struct cx8802_dev *dev) |
607 | { |
608 | struct cx88_core *core = dev->core; |
609 | /* start capturing to the host interface */ |
610 | u32 reg; |
611 | |
612 | int i; |
613 | int lastchange = -1; |
614 | int lastval = 0; |
615 | |
616 | for (i = 0; (i < 10) && (i < (lastchange + 4)); i++) { |
617 | reg = cx_read(AUD_STATUS); |
618 | |
619 | dprintk(1, "AUD_STATUS:%dL: 0x%x\n" , i, reg); |
620 | if ((reg & 0x0F) != lastval) { |
621 | lastval = reg & 0x0F; |
622 | lastchange = i; |
623 | } |
624 | msleep(msecs: 100); |
625 | } |
626 | |
627 | /* unmute audio source */ |
628 | cx_clear(AUD_VOL_CTL, (1 << 6)); |
629 | |
630 | blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, inputcnt: 0, outputcnt: 0); |
631 | |
632 | /* initialize the video input */ |
633 | blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, inputcnt: 0, outputcnt: 0); |
634 | |
635 | cx2341x_handler_set_busy(cxhdl: &dev->cxhdl, busy: 1); |
636 | |
637 | /* start capturing to the host interface */ |
638 | blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, inputcnt: 2, outputcnt: 0, |
639 | BLACKBIRD_MPEG_CAPTURE, BLACKBIRD_RAW_BITS_NONE); |
640 | |
641 | return 0; |
642 | } |
643 | |
644 | static int blackbird_stop_codec(struct cx8802_dev *dev) |
645 | { |
646 | blackbird_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, inputcnt: 3, outputcnt: 0, |
647 | BLACKBIRD_END_NOW, |
648 | BLACKBIRD_MPEG_CAPTURE, |
649 | BLACKBIRD_RAW_BITS_NONE); |
650 | |
651 | cx2341x_handler_set_busy(cxhdl: &dev->cxhdl, busy: 0); |
652 | |
653 | return 0; |
654 | } |
655 | |
656 | /* ------------------------------------------------------------------ */ |
657 | |
658 | static int queue_setup(struct vb2_queue *q, |
659 | unsigned int *num_buffers, unsigned int *num_planes, |
660 | unsigned int sizes[], struct device *alloc_devs[]) |
661 | { |
662 | struct cx8802_dev *dev = q->drv_priv; |
663 | |
664 | *num_planes = 1; |
665 | dev->ts_packet_size = 188 * 4; |
666 | dev->ts_packet_count = 32; |
667 | sizes[0] = dev->ts_packet_size * dev->ts_packet_count; |
668 | return 0; |
669 | } |
670 | |
671 | static int buffer_prepare(struct vb2_buffer *vb) |
672 | { |
673 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
674 | struct cx8802_dev *dev = vb->vb2_queue->drv_priv; |
675 | struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); |
676 | |
677 | return cx8802_buf_prepare(q: vb->vb2_queue, dev, buf); |
678 | } |
679 | |
680 | static void buffer_finish(struct vb2_buffer *vb) |
681 | { |
682 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
683 | struct cx8802_dev *dev = vb->vb2_queue->drv_priv; |
684 | struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); |
685 | struct cx88_riscmem *risc = &buf->risc; |
686 | |
687 | if (risc->cpu) |
688 | dma_free_coherent(dev: &dev->pci->dev, size: risc->size, cpu_addr: risc->cpu, |
689 | dma_handle: risc->dma); |
690 | memset(risc, 0, sizeof(*risc)); |
691 | } |
692 | |
693 | static void buffer_queue(struct vb2_buffer *vb) |
694 | { |
695 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
696 | struct cx8802_dev *dev = vb->vb2_queue->drv_priv; |
697 | struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); |
698 | |
699 | cx8802_buf_queue(dev, buf); |
700 | } |
701 | |
702 | static int start_streaming(struct vb2_queue *q, unsigned int count) |
703 | { |
704 | struct cx8802_dev *dev = q->drv_priv; |
705 | struct cx88_dmaqueue *dmaq = &dev->mpegq; |
706 | struct cx8802_driver *drv; |
707 | struct cx88_buffer *buf; |
708 | unsigned long flags; |
709 | int err; |
710 | |
711 | /* Make sure we can acquire the hardware */ |
712 | drv = cx8802_get_driver(dev, btype: CX88_MPEG_BLACKBIRD); |
713 | if (!drv) { |
714 | dprintk(1, "%s: blackbird driver is not loaded\n" , __func__); |
715 | err = -ENODEV; |
716 | goto fail; |
717 | } |
718 | |
719 | err = drv->request_acquire(drv); |
720 | if (err != 0) { |
721 | dprintk(1, "%s: Unable to acquire hardware, %d\n" , __func__, |
722 | err); |
723 | goto fail; |
724 | } |
725 | |
726 | if (blackbird_initialize_codec(dev) < 0) { |
727 | drv->request_release(drv); |
728 | err = -EINVAL; |
729 | goto fail; |
730 | } |
731 | |
732 | err = blackbird_start_codec(dev); |
733 | if (err == 0) { |
734 | buf = list_entry(dmaq->active.next, struct cx88_buffer, list); |
735 | cx8802_start_dma(dev, q: dmaq, buf); |
736 | return 0; |
737 | } |
738 | |
739 | fail: |
740 | spin_lock_irqsave(&dev->slock, flags); |
741 | while (!list_empty(head: &dmaq->active)) { |
742 | struct cx88_buffer *buf = list_entry(dmaq->active.next, |
743 | struct cx88_buffer, list); |
744 | |
745 | list_del(entry: &buf->list); |
746 | vb2_buffer_done(vb: &buf->vb.vb2_buf, state: VB2_BUF_STATE_QUEUED); |
747 | } |
748 | spin_unlock_irqrestore(lock: &dev->slock, flags); |
749 | return err; |
750 | } |
751 | |
752 | static void stop_streaming(struct vb2_queue *q) |
753 | { |
754 | struct cx8802_dev *dev = q->drv_priv; |
755 | struct cx88_dmaqueue *dmaq = &dev->mpegq; |
756 | struct cx8802_driver *drv = NULL; |
757 | unsigned long flags; |
758 | |
759 | cx8802_cancel_buffers(dev); |
760 | blackbird_stop_codec(dev); |
761 | |
762 | /* Make sure we release the hardware */ |
763 | drv = cx8802_get_driver(dev, btype: CX88_MPEG_BLACKBIRD); |
764 | WARN_ON(!drv); |
765 | if (drv) |
766 | drv->request_release(drv); |
767 | |
768 | spin_lock_irqsave(&dev->slock, flags); |
769 | while (!list_empty(head: &dmaq->active)) { |
770 | struct cx88_buffer *buf = list_entry(dmaq->active.next, |
771 | struct cx88_buffer, list); |
772 | |
773 | list_del(entry: &buf->list); |
774 | vb2_buffer_done(vb: &buf->vb.vb2_buf, state: VB2_BUF_STATE_ERROR); |
775 | } |
776 | spin_unlock_irqrestore(lock: &dev->slock, flags); |
777 | } |
778 | |
779 | static const struct vb2_ops blackbird_qops = { |
780 | .queue_setup = queue_setup, |
781 | .buf_prepare = buffer_prepare, |
782 | .buf_finish = buffer_finish, |
783 | .buf_queue = buffer_queue, |
784 | .wait_prepare = vb2_ops_wait_prepare, |
785 | .wait_finish = vb2_ops_wait_finish, |
786 | .start_streaming = start_streaming, |
787 | .stop_streaming = stop_streaming, |
788 | }; |
789 | |
790 | /* ------------------------------------------------------------------ */ |
791 | |
792 | static int vidioc_querycap(struct file *file, void *priv, |
793 | struct v4l2_capability *cap) |
794 | { |
795 | struct cx8802_dev *dev = video_drvdata(file); |
796 | struct cx88_core *core = dev->core; |
797 | |
798 | strscpy(cap->driver, "cx88_blackbird" , sizeof(cap->driver)); |
799 | return cx88_querycap(file, core, cap); |
800 | } |
801 | |
802 | static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, |
803 | struct v4l2_fmtdesc *f) |
804 | { |
805 | if (f->index != 0) |
806 | return -EINVAL; |
807 | |
808 | f->pixelformat = V4L2_PIX_FMT_MPEG; |
809 | return 0; |
810 | } |
811 | |
812 | static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, |
813 | struct v4l2_format *f) |
814 | { |
815 | struct cx8802_dev *dev = video_drvdata(file); |
816 | struct cx88_core *core = dev->core; |
817 | |
818 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; |
819 | f->fmt.pix.bytesperline = 0; |
820 | f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; |
821 | f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; |
822 | f->fmt.pix.width = core->width; |
823 | f->fmt.pix.height = core->height; |
824 | f->fmt.pix.field = core->field; |
825 | return 0; |
826 | } |
827 | |
828 | static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, |
829 | struct v4l2_format *f) |
830 | { |
831 | struct cx8802_dev *dev = video_drvdata(file); |
832 | struct cx88_core *core = dev->core; |
833 | unsigned int maxw, maxh; |
834 | enum v4l2_field field; |
835 | |
836 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; |
837 | f->fmt.pix.bytesperline = 0; |
838 | f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; |
839 | f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; |
840 | |
841 | maxw = norm_maxw(norm: core->tvnorm); |
842 | maxh = norm_maxh(norm: core->tvnorm); |
843 | |
844 | field = f->fmt.pix.field; |
845 | |
846 | switch (field) { |
847 | case V4L2_FIELD_TOP: |
848 | case V4L2_FIELD_BOTTOM: |
849 | case V4L2_FIELD_INTERLACED: |
850 | case V4L2_FIELD_SEQ_BT: |
851 | case V4L2_FIELD_SEQ_TB: |
852 | break; |
853 | default: |
854 | field = (f->fmt.pix.height > maxh / 2) |
855 | ? V4L2_FIELD_INTERLACED |
856 | : V4L2_FIELD_BOTTOM; |
857 | break; |
858 | } |
859 | if (V4L2_FIELD_HAS_T_OR_B(field)) |
860 | maxh /= 2; |
861 | |
862 | v4l_bound_align_image(width: &f->fmt.pix.width, wmin: 48, wmax: maxw, walign: 2, |
863 | height: &f->fmt.pix.height, hmin: 32, hmax: maxh, halign: 0, salign: 0); |
864 | f->fmt.pix.field = field; |
865 | return 0; |
866 | } |
867 | |
868 | static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, |
869 | struct v4l2_format *f) |
870 | { |
871 | struct cx8802_dev *dev = video_drvdata(file); |
872 | struct cx88_core *core = dev->core; |
873 | |
874 | if (vb2_is_busy(q: &dev->vb2_mpegq)) |
875 | return -EBUSY; |
876 | if (core->v4ldev && (vb2_is_busy(q: &core->v4ldev->vb2_vidq) || |
877 | vb2_is_busy(q: &core->v4ldev->vb2_vbiq))) |
878 | return -EBUSY; |
879 | vidioc_try_fmt_vid_cap(file, priv, f); |
880 | core->width = f->fmt.pix.width; |
881 | core->height = f->fmt.pix.height; |
882 | core->field = f->fmt.pix.field; |
883 | cx88_set_scale(core, width: f->fmt.pix.width, height: f->fmt.pix.height, |
884 | field: f->fmt.pix.field); |
885 | blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, inputcnt: 2, outputcnt: 0, |
886 | f->fmt.pix.height, f->fmt.pix.width); |
887 | return 0; |
888 | } |
889 | |
890 | static int vidioc_s_frequency(struct file *file, void *priv, |
891 | const struct v4l2_frequency *f) |
892 | { |
893 | struct cx8802_dev *dev = video_drvdata(file); |
894 | struct cx88_core *core = dev->core; |
895 | bool streaming; |
896 | |
897 | if (unlikely(core->board.tuner_type == UNSET)) |
898 | return -EINVAL; |
899 | if (unlikely(f->tuner != 0)) |
900 | return -EINVAL; |
901 | streaming = vb2_start_streaming_called(q: &dev->vb2_mpegq); |
902 | if (streaming) |
903 | blackbird_stop_codec(dev); |
904 | |
905 | cx88_set_freq(core, f); |
906 | blackbird_initialize_codec(dev); |
907 | cx88_set_scale(core, width: core->width, height: core->height, field: core->field); |
908 | if (streaming) |
909 | blackbird_start_codec(dev); |
910 | return 0; |
911 | } |
912 | |
913 | static int vidioc_log_status(struct file *file, void *priv) |
914 | { |
915 | struct cx8802_dev *dev = video_drvdata(file); |
916 | struct cx88_core *core = dev->core; |
917 | char name[32 + 2]; |
918 | |
919 | snprintf(buf: name, size: sizeof(name), fmt: "%s/2" , core->name); |
920 | call_all(core, core, log_status); |
921 | v4l2_ctrl_handler_log_status(hdl: &dev->cxhdl.hdl, prefix: name); |
922 | return 0; |
923 | } |
924 | |
925 | static int vidioc_enum_input(struct file *file, void *priv, |
926 | struct v4l2_input *i) |
927 | { |
928 | struct cx8802_dev *dev = video_drvdata(file); |
929 | struct cx88_core *core = dev->core; |
930 | |
931 | return cx88_enum_input(core, i); |
932 | } |
933 | |
934 | static int vidioc_g_frequency(struct file *file, void *priv, |
935 | struct v4l2_frequency *f) |
936 | { |
937 | struct cx8802_dev *dev = video_drvdata(file); |
938 | struct cx88_core *core = dev->core; |
939 | |
940 | if (unlikely(core->board.tuner_type == UNSET)) |
941 | return -EINVAL; |
942 | if (unlikely(f->tuner != 0)) |
943 | return -EINVAL; |
944 | |
945 | f->frequency = core->freq; |
946 | call_all(core, tuner, g_frequency, f); |
947 | |
948 | return 0; |
949 | } |
950 | |
951 | static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) |
952 | { |
953 | struct cx8802_dev *dev = video_drvdata(file); |
954 | struct cx88_core *core = dev->core; |
955 | |
956 | *i = core->input; |
957 | return 0; |
958 | } |
959 | |
960 | static int vidioc_s_input(struct file *file, void *priv, unsigned int i) |
961 | { |
962 | struct cx8802_dev *dev = video_drvdata(file); |
963 | struct cx88_core *core = dev->core; |
964 | |
965 | if (i >= 4) |
966 | return -EINVAL; |
967 | if (!INPUT(i).type) |
968 | return -EINVAL; |
969 | |
970 | cx88_newstation(core); |
971 | cx88_video_mux(core, input: i); |
972 | return 0; |
973 | } |
974 | |
975 | static int vidioc_g_tuner(struct file *file, void *priv, |
976 | struct v4l2_tuner *t) |
977 | { |
978 | struct cx8802_dev *dev = video_drvdata(file); |
979 | struct cx88_core *core = dev->core; |
980 | u32 reg; |
981 | |
982 | if (unlikely(core->board.tuner_type == UNSET)) |
983 | return -EINVAL; |
984 | if (t->index != 0) |
985 | return -EINVAL; |
986 | |
987 | strscpy(t->name, "Television" , sizeof(t->name)); |
988 | t->capability = V4L2_TUNER_CAP_NORM; |
989 | t->rangehigh = 0xffffffffUL; |
990 | call_all(core, tuner, g_tuner, t); |
991 | |
992 | cx88_get_stereo(core, t); |
993 | reg = cx_read(MO_DEVICE_STATUS); |
994 | t->signal = (reg & (1 << 5)) ? 0xffff : 0x0000; |
995 | return 0; |
996 | } |
997 | |
998 | static int vidioc_s_tuner(struct file *file, void *priv, |
999 | const struct v4l2_tuner *t) |
1000 | { |
1001 | struct cx8802_dev *dev = video_drvdata(file); |
1002 | struct cx88_core *core = dev->core; |
1003 | |
1004 | if (core->board.tuner_type == UNSET) |
1005 | return -EINVAL; |
1006 | if (t->index != 0) |
1007 | return -EINVAL; |
1008 | |
1009 | cx88_set_stereo(core, mode: t->audmode, manual: 1); |
1010 | return 0; |
1011 | } |
1012 | |
1013 | static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) |
1014 | { |
1015 | struct cx8802_dev *dev = video_drvdata(file); |
1016 | struct cx88_core *core = dev->core; |
1017 | |
1018 | *tvnorm = core->tvnorm; |
1019 | return 0; |
1020 | } |
1021 | |
1022 | static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) |
1023 | { |
1024 | struct cx8802_dev *dev = video_drvdata(file); |
1025 | struct cx88_core *core = dev->core; |
1026 | |
1027 | return cx88_set_tvnorm(core, norm: id); |
1028 | } |
1029 | |
1030 | static const struct v4l2_file_operations mpeg_fops = { |
1031 | .owner = THIS_MODULE, |
1032 | .open = v4l2_fh_open, |
1033 | .release = vb2_fop_release, |
1034 | .read = vb2_fop_read, |
1035 | .poll = vb2_fop_poll, |
1036 | .mmap = vb2_fop_mmap, |
1037 | .unlocked_ioctl = video_ioctl2, |
1038 | }; |
1039 | |
1040 | static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { |
1041 | .vidioc_querycap = vidioc_querycap, |
1042 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, |
1043 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, |
1044 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, |
1045 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, |
1046 | .vidioc_reqbufs = vb2_ioctl_reqbufs, |
1047 | .vidioc_querybuf = vb2_ioctl_querybuf, |
1048 | .vidioc_qbuf = vb2_ioctl_qbuf, |
1049 | .vidioc_dqbuf = vb2_ioctl_dqbuf, |
1050 | .vidioc_streamon = vb2_ioctl_streamon, |
1051 | .vidioc_streamoff = vb2_ioctl_streamoff, |
1052 | .vidioc_s_frequency = vidioc_s_frequency, |
1053 | .vidioc_log_status = vidioc_log_status, |
1054 | .vidioc_enum_input = vidioc_enum_input, |
1055 | .vidioc_g_frequency = vidioc_g_frequency, |
1056 | .vidioc_g_input = vidioc_g_input, |
1057 | .vidioc_s_input = vidioc_s_input, |
1058 | .vidioc_g_tuner = vidioc_g_tuner, |
1059 | .vidioc_s_tuner = vidioc_s_tuner, |
1060 | .vidioc_g_std = vidioc_g_std, |
1061 | .vidioc_s_std = vidioc_s_std, |
1062 | .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, |
1063 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
1064 | }; |
1065 | |
1066 | static const struct video_device cx8802_mpeg_template = { |
1067 | .name = "cx8802" , |
1068 | .fops = &mpeg_fops, |
1069 | .ioctl_ops = &mpeg_ioctl_ops, |
1070 | .tvnorms = CX88_NORMS, |
1071 | }; |
1072 | |
1073 | /* ------------------------------------------------------------------ */ |
1074 | |
1075 | /* The CX8802 MPEG API will call this when we can use the hardware */ |
1076 | static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv) |
1077 | { |
1078 | struct cx88_core *core = drv->core; |
1079 | int err = 0; |
1080 | |
1081 | switch (core->boardnr) { |
1082 | case CX88_BOARD_HAUPPAUGE_HVR1300: |
1083 | /* |
1084 | * By default, core setup will leave the cx22702 out of reset, |
1085 | * on the bus. |
1086 | * We left the hardware on power up with the cx22702 active. |
1087 | * We're being given access to re-arrange the GPIOs. |
1088 | * Take the bus off the cx22702 and put the cx23416 on it. |
1089 | */ |
1090 | /* Toggle reset on cx22702 leaving i2c active */ |
1091 | cx_set(MO_GP0_IO, 0x00000080); |
1092 | udelay(1000); |
1093 | cx_clear(MO_GP0_IO, 0x00000080); |
1094 | udelay(50); |
1095 | cx_set(MO_GP0_IO, 0x00000080); |
1096 | udelay(1000); |
1097 | /* tri-state the cx22702 pins */ |
1098 | cx_set(MO_GP0_IO, 0x00000004); |
1099 | udelay(1000); |
1100 | break; |
1101 | default: |
1102 | err = -ENODEV; |
1103 | } |
1104 | return err; |
1105 | } |
1106 | |
1107 | /* The CX8802 MPEG API will call this when we need to release the hardware */ |
1108 | static int cx8802_blackbird_advise_release(struct cx8802_driver *drv) |
1109 | { |
1110 | struct cx88_core *core = drv->core; |
1111 | int err = 0; |
1112 | |
1113 | switch (core->boardnr) { |
1114 | case CX88_BOARD_HAUPPAUGE_HVR1300: |
1115 | /* Exit leaving the cx23416 on the bus */ |
1116 | break; |
1117 | default: |
1118 | err = -ENODEV; |
1119 | } |
1120 | return err; |
1121 | } |
1122 | |
1123 | static void blackbird_unregister_video(struct cx8802_dev *dev) |
1124 | { |
1125 | video_unregister_device(vdev: &dev->mpeg_dev); |
1126 | } |
1127 | |
1128 | static int blackbird_register_video(struct cx8802_dev *dev) |
1129 | { |
1130 | int err; |
1131 | |
1132 | cx88_vdev_init(core: dev->core, pci: dev->pci, vfd: &dev->mpeg_dev, |
1133 | template_: &cx8802_mpeg_template, type: "mpeg" ); |
1134 | dev->mpeg_dev.ctrl_handler = &dev->cxhdl.hdl; |
1135 | video_set_drvdata(vdev: &dev->mpeg_dev, data: dev); |
1136 | dev->mpeg_dev.queue = &dev->vb2_mpegq; |
1137 | dev->mpeg_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | |
1138 | V4L2_CAP_VIDEO_CAPTURE; |
1139 | if (dev->core->board.tuner_type != UNSET) |
1140 | dev->mpeg_dev.device_caps |= V4L2_CAP_TUNER; |
1141 | err = video_register_device(vdev: &dev->mpeg_dev, type: VFL_TYPE_VIDEO, nr: -1); |
1142 | if (err < 0) { |
1143 | pr_info("can't register mpeg device\n" ); |
1144 | return err; |
1145 | } |
1146 | pr_info("registered device %s [mpeg]\n" , |
1147 | video_device_node_name(&dev->mpeg_dev)); |
1148 | return 0; |
1149 | } |
1150 | |
1151 | /* ----------------------------------------------------------- */ |
1152 | |
1153 | static int cx8802_blackbird_probe(struct cx8802_driver *drv) |
1154 | { |
1155 | struct cx88_core *core = drv->core; |
1156 | struct cx8802_dev *dev = core->dvbdev; |
1157 | struct vb2_queue *q; |
1158 | int err; |
1159 | |
1160 | dprintk(1, "%s\n" , __func__); |
1161 | dprintk(1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n" , |
1162 | core->boardnr, |
1163 | core->name, |
1164 | core->pci_bus, |
1165 | core->pci_slot); |
1166 | |
1167 | err = -ENODEV; |
1168 | if (!(core->board.mpeg & CX88_MPEG_BLACKBIRD)) |
1169 | goto fail_core; |
1170 | |
1171 | dev->cxhdl.port = CX2341X_PORT_STREAMING; |
1172 | dev->cxhdl.width = core->width; |
1173 | dev->cxhdl.height = core->height; |
1174 | dev->cxhdl.func = blackbird_mbox_func; |
1175 | dev->cxhdl.priv = dev; |
1176 | err = cx2341x_handler_init(cxhdl: &dev->cxhdl, nr_of_controls_hint: 36); |
1177 | if (err) |
1178 | goto fail_core; |
1179 | v4l2_ctrl_add_handler(hdl: &dev->cxhdl.hdl, add: &core->video_hdl, NULL, from_other_dev: false); |
1180 | |
1181 | /* blackbird stuff */ |
1182 | pr_info("cx23416 based mpeg encoder (blackbird reference design)\n" ); |
1183 | host_setup(core: dev->core); |
1184 | |
1185 | blackbird_initialize_codec(dev); |
1186 | |
1187 | /* initial device configuration: needed ? */ |
1188 | // init_controls(core); |
1189 | cx88_set_tvnorm(core, norm: core->tvnorm); |
1190 | cx88_video_mux(core, input: 0); |
1191 | cx2341x_handler_set_50hz(cxhdl: &dev->cxhdl, is_50hz: core->height == 576); |
1192 | cx2341x_handler_setup(cxhdl: &dev->cxhdl); |
1193 | |
1194 | q = &dev->vb2_mpegq; |
1195 | q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
1196 | q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; |
1197 | q->gfp_flags = GFP_DMA32; |
1198 | q->min_queued_buffers = 2; |
1199 | q->drv_priv = dev; |
1200 | q->buf_struct_size = sizeof(struct cx88_buffer); |
1201 | q->ops = &blackbird_qops; |
1202 | q->mem_ops = &vb2_dma_sg_memops; |
1203 | q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; |
1204 | q->lock = &core->lock; |
1205 | q->dev = &dev->pci->dev; |
1206 | |
1207 | err = vb2_queue_init(q); |
1208 | if (err < 0) |
1209 | goto fail_core; |
1210 | |
1211 | blackbird_register_video(dev); |
1212 | |
1213 | return 0; |
1214 | |
1215 | fail_core: |
1216 | return err; |
1217 | } |
1218 | |
1219 | static int cx8802_blackbird_remove(struct cx8802_driver *drv) |
1220 | { |
1221 | struct cx88_core *core = drv->core; |
1222 | struct cx8802_dev *dev = core->dvbdev; |
1223 | |
1224 | /* blackbird */ |
1225 | blackbird_unregister_video(dev: drv->core->dvbdev); |
1226 | v4l2_ctrl_handler_free(hdl: &dev->cxhdl.hdl); |
1227 | |
1228 | return 0; |
1229 | } |
1230 | |
1231 | static struct cx8802_driver cx8802_blackbird_driver = { |
1232 | .type_id = CX88_MPEG_BLACKBIRD, |
1233 | .hw_access = CX8802_DRVCTL_SHARED, |
1234 | .probe = cx8802_blackbird_probe, |
1235 | .remove = cx8802_blackbird_remove, |
1236 | .advise_acquire = cx8802_blackbird_advise_acquire, |
1237 | .advise_release = cx8802_blackbird_advise_release, |
1238 | }; |
1239 | |
1240 | static int __init blackbird_init(void) |
1241 | { |
1242 | pr_info("cx2388x blackbird driver version %s loaded\n" , |
1243 | CX88_VERSION); |
1244 | return cx8802_register_driver(drv: &cx8802_blackbird_driver); |
1245 | } |
1246 | |
1247 | static void __exit blackbird_fini(void) |
1248 | { |
1249 | cx8802_unregister_driver(drv: &cx8802_blackbird_driver); |
1250 | } |
1251 | |
1252 | module_init(blackbird_init); |
1253 | module_exit(blackbird_fini); |
1254 | |