xref: /OK3568_Linux_fs/kernel/drivers/media/pci/cx88/cx88-blackbird.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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