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