xref: /openbmc/linux/drivers/media/pci/cx88/cx88-mpeg.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b285192aSMauro Carvalho Chehab /*
3b285192aSMauro Carvalho Chehab  *
4b285192aSMauro Carvalho Chehab  *  Support for the mpeg transport stream transfers
5b285192aSMauro Carvalho Chehab  *  PCI function #2 of the cx2388x.
6b285192aSMauro Carvalho Chehab  *
7b285192aSMauro Carvalho Chehab  *    (c) 2004 Jelle Foks <jelle@foks.us>
8b285192aSMauro Carvalho Chehab  *    (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
9b285192aSMauro Carvalho Chehab  *    (c) 2004 Gerd Knorr <kraxel@bytesex.org>
10b285192aSMauro Carvalho Chehab  */
11b285192aSMauro Carvalho Chehab 
1265bc2fe8SMauro Carvalho Chehab #include "cx88.h"
1365bc2fe8SMauro Carvalho Chehab 
14b285192aSMauro Carvalho Chehab #include <linux/module.h>
15b285192aSMauro Carvalho Chehab #include <linux/slab.h>
16b285192aSMauro Carvalho Chehab #include <linux/init.h>
17b285192aSMauro Carvalho Chehab #include <linux/device.h>
18b285192aSMauro Carvalho Chehab #include <linux/dma-mapping.h>
19b285192aSMauro Carvalho Chehab #include <linux/interrupt.h>
20399426caSMauro Carvalho Chehab #include <linux/delay.h>
21b285192aSMauro Carvalho Chehab 
22b285192aSMauro Carvalho Chehab /* ------------------------------------------------------------------ */
23b285192aSMauro Carvalho Chehab 
24b285192aSMauro Carvalho Chehab MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards");
25b285192aSMauro Carvalho Chehab MODULE_AUTHOR("Jelle Foks <jelle@foks.us>");
26b285192aSMauro Carvalho Chehab MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
27b285192aSMauro Carvalho Chehab MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
28b285192aSMauro Carvalho Chehab MODULE_LICENSE("GPL");
29b285192aSMauro Carvalho Chehab MODULE_VERSION(CX88_VERSION);
30b285192aSMauro Carvalho Chehab 
31b285192aSMauro Carvalho Chehab static unsigned int debug;
32b285192aSMauro Carvalho Chehab module_param(debug, int, 0644);
33b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "enable debug messages [mpeg]");
34b285192aSMauro Carvalho Chehab 
35db613710SMauro Carvalho Chehab #define dprintk(level, fmt, arg...) do {				\
36db613710SMauro Carvalho Chehab 	if (debug + 1 > level)						\
3765bc2fe8SMauro Carvalho Chehab 		printk(KERN_DEBUG pr_fmt("%s: mpeg:" fmt),		\
3865bc2fe8SMauro Carvalho Chehab 			__func__, ##arg);				\
39db613710SMauro Carvalho Chehab } while (0)
40b285192aSMauro Carvalho Chehab 
41b285192aSMauro Carvalho Chehab #if defined(CONFIG_MODULES) && defined(MODULE)
request_module_async(struct work_struct * work)42b285192aSMauro Carvalho Chehab static void request_module_async(struct work_struct *work)
43b285192aSMauro Carvalho Chehab {
44399426caSMauro Carvalho Chehab 	struct cx8802_dev *dev = container_of(work, struct cx8802_dev,
45399426caSMauro Carvalho Chehab 					      request_module_wk);
46b285192aSMauro Carvalho Chehab 
47b285192aSMauro Carvalho Chehab 	if (dev->core->board.mpeg & CX88_MPEG_DVB)
48b285192aSMauro Carvalho Chehab 		request_module("cx88-dvb");
49b285192aSMauro Carvalho Chehab 	if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD)
50b285192aSMauro Carvalho Chehab 		request_module("cx88-blackbird");
51b285192aSMauro Carvalho Chehab }
52b285192aSMauro Carvalho Chehab 
request_modules(struct cx8802_dev * dev)53b285192aSMauro Carvalho Chehab static void request_modules(struct cx8802_dev *dev)
54b285192aSMauro Carvalho Chehab {
55b285192aSMauro Carvalho Chehab 	INIT_WORK(&dev->request_module_wk, request_module_async);
56b285192aSMauro Carvalho Chehab 	schedule_work(&dev->request_module_wk);
57b285192aSMauro Carvalho Chehab }
58b285192aSMauro Carvalho Chehab 
flush_request_modules(struct cx8802_dev * dev)59b285192aSMauro Carvalho Chehab static void flush_request_modules(struct cx8802_dev *dev)
60b285192aSMauro Carvalho Chehab {
610b8e74c6SLinus Torvalds 	flush_work(&dev->request_module_wk);
62b285192aSMauro Carvalho Chehab }
63b285192aSMauro Carvalho Chehab #else
64b285192aSMauro Carvalho Chehab #define request_modules(dev)
65b285192aSMauro Carvalho Chehab #define flush_request_modules(dev)
66b285192aSMauro Carvalho Chehab #endif /* CONFIG_MODULES */
67b285192aSMauro Carvalho Chehab 
68b285192aSMauro Carvalho Chehab static LIST_HEAD(cx8802_devlist);
69b285192aSMauro Carvalho Chehab static DEFINE_MUTEX(cx8802_mutex);
70b285192aSMauro Carvalho Chehab /* ------------------------------------------------------------------ */
71b285192aSMauro Carvalho Chehab 
cx8802_start_dma(struct cx8802_dev * dev,struct cx88_dmaqueue * q,struct cx88_buffer * buf)720b6b6302SHans Verkuil int cx8802_start_dma(struct cx8802_dev    *dev,
73b285192aSMauro Carvalho Chehab 		     struct cx88_dmaqueue *q,
74b285192aSMauro Carvalho Chehab 		     struct cx88_buffer   *buf)
75b285192aSMauro Carvalho Chehab {
76b285192aSMauro Carvalho Chehab 	struct cx88_core *core = dev->core;
77b285192aSMauro Carvalho Chehab 
7865bc2fe8SMauro Carvalho Chehab 	dprintk(1, "w: %d, h: %d, f: %d\n",
79ccd6f1d4SHans Verkuil 		core->width, core->height, core->field);
80b285192aSMauro Carvalho Chehab 
81b285192aSMauro Carvalho Chehab 	/* setup fifo + format */
82b285192aSMauro Carvalho Chehab 	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28],
83b285192aSMauro Carvalho Chehab 				dev->ts_packet_size, buf->risc.dma);
84b285192aSMauro Carvalho Chehab 
85b285192aSMauro Carvalho Chehab 	/* write TS length to chip */
860b6b6302SHans Verkuil 	cx_write(MO_TS_LNGTH, dev->ts_packet_size);
87b285192aSMauro Carvalho Chehab 
88399426caSMauro Carvalho Chehab 	/*
89399426caSMauro Carvalho Chehab 	 * FIXME: this needs a review.
90399426caSMauro Carvalho Chehab 	 * also: move to cx88-blackbird + cx88-dvb source files?
91399426caSMauro Carvalho Chehab 	 */
92b285192aSMauro Carvalho Chehab 
93b285192aSMauro Carvalho Chehab 	dprintk(1, "core->active_type_id = 0x%08x\n", core->active_type_id);
94b285192aSMauro Carvalho Chehab 
95b285192aSMauro Carvalho Chehab 	if ((core->active_type_id == CX88_MPEG_DVB) &&
96b285192aSMauro Carvalho Chehab 	    (core->board.mpeg & CX88_MPEG_DVB)) {
97b285192aSMauro Carvalho Chehab 		dprintk(1, "cx8802_start_dma doing .dvb\n");
98b285192aSMauro Carvalho Chehab 		/* negedge driven & software reset */
99b285192aSMauro Carvalho Chehab 		cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
100b285192aSMauro Carvalho Chehab 		udelay(100);
101b285192aSMauro Carvalho Chehab 		cx_write(MO_PINMUX_IO, 0x00);
102b285192aSMauro Carvalho Chehab 		cx_write(TS_HW_SOP_CNTRL, 0x47 << 16 | 188 << 4 | 0x01);
103b285192aSMauro Carvalho Chehab 		switch (core->boardnr) {
104b285192aSMauro Carvalho Chehab 		case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
105b285192aSMauro Carvalho Chehab 		case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
106b285192aSMauro Carvalho Chehab 		case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
107b285192aSMauro Carvalho Chehab 		case CX88_BOARD_PCHDTV_HD5500:
108b285192aSMauro Carvalho Chehab 			cx_write(TS_SOP_STAT, 1 << 13);
109b285192aSMauro Carvalho Chehab 			break;
110b285192aSMauro Carvalho Chehab 		case CX88_BOARD_SAMSUNG_SMT_7020:
111b285192aSMauro Carvalho Chehab 			cx_write(TS_SOP_STAT, 0x00);
112b285192aSMauro Carvalho Chehab 			break;
113b285192aSMauro Carvalho Chehab 		case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
114b285192aSMauro Carvalho Chehab 		case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
115399426caSMauro Carvalho Chehab 			/* Enable MPEG parallel IO and video signal pins */
116399426caSMauro Carvalho Chehab 			cx_write(MO_PINMUX_IO, 0x88);
117b285192aSMauro Carvalho Chehab 			udelay(100);
118b285192aSMauro Carvalho Chehab 			break;
119b285192aSMauro Carvalho Chehab 		case CX88_BOARD_HAUPPAUGE_HVR1300:
120b285192aSMauro Carvalho Chehab 			/* Enable MPEG parallel IO and video signal pins */
121b285192aSMauro Carvalho Chehab 			cx_write(MO_PINMUX_IO, 0x88);
122b285192aSMauro Carvalho Chehab 			cx_write(TS_SOP_STAT, 0);
123b285192aSMauro Carvalho Chehab 			cx_write(TS_VALERR_CNTRL, 0);
124b285192aSMauro Carvalho Chehab 			break;
125b285192aSMauro Carvalho Chehab 		case CX88_BOARD_PINNACLE_PCTV_HD_800i:
126b285192aSMauro Carvalho Chehab 			/* Enable MPEG parallel IO and video signal pins */
127b285192aSMauro Carvalho Chehab 			cx_write(MO_PINMUX_IO, 0x88);
128b285192aSMauro Carvalho Chehab 			cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4));
129b285192aSMauro Carvalho Chehab 			dev->ts_gen_cntrl = 5;
130b285192aSMauro Carvalho Chehab 			cx_write(TS_SOP_STAT, 0);
131b285192aSMauro Carvalho Chehab 			cx_write(TS_VALERR_CNTRL, 0);
132b285192aSMauro Carvalho Chehab 			udelay(100);
133b285192aSMauro Carvalho Chehab 			break;
134b285192aSMauro Carvalho Chehab 		default:
135b285192aSMauro Carvalho Chehab 			cx_write(TS_SOP_STAT, 0x00);
136b285192aSMauro Carvalho Chehab 			break;
137b285192aSMauro Carvalho Chehab 		}
138b285192aSMauro Carvalho Chehab 		cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);
139b285192aSMauro Carvalho Chehab 		udelay(100);
140b285192aSMauro Carvalho Chehab 	} else if ((core->active_type_id == CX88_MPEG_BLACKBIRD) &&
141b285192aSMauro Carvalho Chehab 		(core->board.mpeg & CX88_MPEG_BLACKBIRD)) {
142b285192aSMauro Carvalho Chehab 		dprintk(1, "cx8802_start_dma doing .blackbird\n");
143b285192aSMauro Carvalho Chehab 		cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
144b285192aSMauro Carvalho Chehab 
145399426caSMauro Carvalho Chehab 		/* punctured clock TS & posedge driven & software reset */
146399426caSMauro Carvalho Chehab 		cx_write(TS_GEN_CNTRL, 0x46);
147b285192aSMauro Carvalho Chehab 		udelay(100);
148b285192aSMauro Carvalho Chehab 
149b285192aSMauro Carvalho Chehab 		cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */
150b285192aSMauro Carvalho Chehab 		cx_write(TS_VALERR_CNTRL, 0x2000);
151b285192aSMauro Carvalho Chehab 
152399426caSMauro Carvalho Chehab 		/* punctured clock TS & posedge driven */
153399426caSMauro Carvalho Chehab 		cx_write(TS_GEN_CNTRL, 0x06);
154b285192aSMauro Carvalho Chehab 		udelay(100);
155b285192aSMauro Carvalho Chehab 	} else {
15665bc2fe8SMauro Carvalho Chehab 		pr_err("%s() Failed. Unsupported value in .mpeg (0x%08x)\n",
15765bc2fe8SMauro Carvalho Chehab 		       __func__, core->board.mpeg);
158b285192aSMauro Carvalho Chehab 		return -EINVAL;
159b285192aSMauro Carvalho Chehab 	}
160b285192aSMauro Carvalho Chehab 
161b285192aSMauro Carvalho Chehab 	/* reset counter */
162b285192aSMauro Carvalho Chehab 	cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET);
1639450684bSHans Verkuil 	q->count = 0;
164b285192aSMauro Carvalho Chehab 
165*56cb61f7SDaniel González Cabanelas 	/* clear interrupt status register */
166*56cb61f7SDaniel González Cabanelas 	cx_write(MO_TS_INTSTAT,  0x1f1111);
167*56cb61f7SDaniel González Cabanelas 
168b285192aSMauro Carvalho Chehab 	/* enable irqs */
169b285192aSMauro Carvalho Chehab 	dprintk(1, "setting the interrupt mask\n");
170b285192aSMauro Carvalho Chehab 	cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT);
171b285192aSMauro Carvalho Chehab 	cx_set(MO_TS_INTMSK,  0x1f0011);
172b285192aSMauro Carvalho Chehab 
173b285192aSMauro Carvalho Chehab 	/* start dma */
174b285192aSMauro Carvalho Chehab 	cx_set(MO_DEV_CNTRL2, (1 << 5));
175b285192aSMauro Carvalho Chehab 	cx_set(MO_TS_DMACNTRL, 0x11);
176b285192aSMauro Carvalho Chehab 	return 0;
177b285192aSMauro Carvalho Chehab }
178399426caSMauro Carvalho Chehab EXPORT_SYMBOL(cx8802_start_dma);
179b285192aSMauro Carvalho Chehab 
cx8802_stop_dma(struct cx8802_dev * dev)180b285192aSMauro Carvalho Chehab static int cx8802_stop_dma(struct cx8802_dev *dev)
181b285192aSMauro Carvalho Chehab {
182b285192aSMauro Carvalho Chehab 	struct cx88_core *core = dev->core;
1837b61ba8fSMauro Carvalho Chehab 
18465bc2fe8SMauro Carvalho Chehab 	dprintk(1, "\n");
185b285192aSMauro Carvalho Chehab 
186b285192aSMauro Carvalho Chehab 	/* stop dma */
187b285192aSMauro Carvalho Chehab 	cx_clear(MO_TS_DMACNTRL, 0x11);
188b285192aSMauro Carvalho Chehab 
189b285192aSMauro Carvalho Chehab 	/* disable irqs */
190b285192aSMauro Carvalho Chehab 	cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT);
191b285192aSMauro Carvalho Chehab 	cx_clear(MO_TS_INTMSK, 0x1f0011);
192b285192aSMauro Carvalho Chehab 
193b285192aSMauro Carvalho Chehab 	/* Reset the controller */
194b285192aSMauro Carvalho Chehab 	cx_write(TS_GEN_CNTRL, 0xcd);
195b285192aSMauro Carvalho Chehab 	return 0;
196b285192aSMauro Carvalho Chehab }
197b285192aSMauro Carvalho Chehab 
cx8802_restart_queue(struct cx8802_dev * dev,struct cx88_dmaqueue * q)198b285192aSMauro Carvalho Chehab static int cx8802_restart_queue(struct cx8802_dev    *dev,
199b285192aSMauro Carvalho Chehab 				struct cx88_dmaqueue *q)
200b285192aSMauro Carvalho Chehab {
201b285192aSMauro Carvalho Chehab 	struct cx88_buffer *buf;
202b285192aSMauro Carvalho Chehab 
20365bc2fe8SMauro Carvalho Chehab 	dprintk(1, "\n");
204b285192aSMauro Carvalho Chehab 	if (list_empty(&q->active))
205b285192aSMauro Carvalho Chehab 		return 0;
206b285192aSMauro Carvalho Chehab 
2070b6b6302SHans Verkuil 	buf = list_entry(q->active.next, struct cx88_buffer, list);
208b285192aSMauro Carvalho Chehab 	dprintk(2, "restart_queue [%p/%d]: restart dma\n",
2092d700715SJunghak Sung 		buf, buf->vb.vb2_buf.index);
210b285192aSMauro Carvalho Chehab 	cx8802_start_dma(dev, q, buf);
211b285192aSMauro Carvalho Chehab 	return 0;
212b285192aSMauro Carvalho Chehab }
213b285192aSMauro Carvalho Chehab 
214b285192aSMauro Carvalho Chehab /* ------------------------------------------------------------------ */
215b285192aSMauro Carvalho Chehab 
cx8802_buf_prepare(struct vb2_queue * q,struct cx8802_dev * dev,struct cx88_buffer * buf)2160b6b6302SHans Verkuil int cx8802_buf_prepare(struct vb2_queue *q, struct cx8802_dev *dev,
217ccd6f1d4SHans Verkuil 		       struct cx88_buffer *buf)
218b285192aSMauro Carvalho Chehab {
219b285192aSMauro Carvalho Chehab 	int size = dev->ts_packet_size * dev->ts_packet_count;
2202d700715SJunghak Sung 	struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0);
2215e7045e3SHans Verkuil 	struct cx88_riscmem *risc = &buf->risc;
222b285192aSMauro Carvalho Chehab 	int rc;
223b285192aSMauro Carvalho Chehab 
2242d700715SJunghak Sung 	if (vb2_plane_size(&buf->vb.vb2_buf, 0) < size)
225b285192aSMauro Carvalho Chehab 		return -EINVAL;
2262d700715SJunghak Sung 	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
227b285192aSMauro Carvalho Chehab 
2285e7045e3SHans Verkuil 	rc = cx88_risc_databuffer(dev->pci, risc, sgt->sgl,
2290b6b6302SHans Verkuil 				  dev->ts_packet_size, dev->ts_packet_count, 0);
230999b3cebSHans Verkuil 	if (rc) {
2315e7045e3SHans Verkuil 		if (risc->cpu)
23200ae4ebcSChristophe JAILLET 			dma_free_coherent(&dev->pci->dev, risc->size,
233399426caSMauro Carvalho Chehab 					  risc->cpu, risc->dma);
2345e7045e3SHans Verkuil 		memset(risc, 0, sizeof(*risc));
235999b3cebSHans Verkuil 		return rc;
236999b3cebSHans Verkuil 	}
237b285192aSMauro Carvalho Chehab 	return 0;
238b285192aSMauro Carvalho Chehab }
239399426caSMauro Carvalho Chehab EXPORT_SYMBOL(cx8802_buf_prepare);
240b285192aSMauro Carvalho Chehab 
cx8802_buf_queue(struct cx8802_dev * dev,struct cx88_buffer * buf)241b285192aSMauro Carvalho Chehab void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
242b285192aSMauro Carvalho Chehab {
243b285192aSMauro Carvalho Chehab 	struct cx88_buffer    *prev;
244b285192aSMauro Carvalho Chehab 	struct cx88_dmaqueue  *cx88q = &dev->mpegq;
245b285192aSMauro Carvalho Chehab 
24665bc2fe8SMauro Carvalho Chehab 	dprintk(1, "\n");
2470b6b6302SHans Verkuil 	/* add jump to start */
2480b6b6302SHans Verkuil 	buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 8);
2490b6b6302SHans Verkuil 	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
2500b6b6302SHans Verkuil 	buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 8);
251b285192aSMauro Carvalho Chehab 
252b285192aSMauro Carvalho Chehab 	if (list_empty(&cx88q->active)) {
253b285192aSMauro Carvalho Chehab 		dprintk(1, "queue is empty - first active\n");
2540b6b6302SHans Verkuil 		list_add_tail(&buf->list, &cx88q->active);
255b285192aSMauro Carvalho Chehab 		dprintk(1, "[%p/%d] %s - first active\n",
2562d700715SJunghak Sung 			buf, buf->vb.vb2_buf.index, __func__);
257b285192aSMauro Carvalho Chehab 
258b285192aSMauro Carvalho Chehab 	} else {
2590b6b6302SHans Verkuil 		buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
260b285192aSMauro Carvalho Chehab 		dprintk(1, "queue is not empty - append to active\n");
2610b6b6302SHans Verkuil 		prev = list_entry(cx88q->active.prev, struct cx88_buffer, list);
2620b6b6302SHans Verkuil 		list_add_tail(&buf->list, &cx88q->active);
263b285192aSMauro Carvalho Chehab 		prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
264b285192aSMauro Carvalho Chehab 		dprintk(1, "[%p/%d] %s - append to active\n",
2652d700715SJunghak Sung 			buf, buf->vb.vb2_buf.index, __func__);
266b285192aSMauro Carvalho Chehab 	}
267b285192aSMauro Carvalho Chehab }
268399426caSMauro Carvalho Chehab EXPORT_SYMBOL(cx8802_buf_queue);
269b285192aSMauro Carvalho Chehab 
270b285192aSMauro Carvalho Chehab /* ----------------------------------------------------------- */
271b285192aSMauro Carvalho Chehab 
do_cancel_buffers(struct cx8802_dev * dev)2720b6b6302SHans Verkuil static void do_cancel_buffers(struct cx8802_dev *dev)
273b285192aSMauro Carvalho Chehab {
274b285192aSMauro Carvalho Chehab 	struct cx88_dmaqueue *q = &dev->mpegq;
275b285192aSMauro Carvalho Chehab 	struct cx88_buffer *buf;
276b285192aSMauro Carvalho Chehab 	unsigned long flags;
277b285192aSMauro Carvalho Chehab 
278b285192aSMauro Carvalho Chehab 	spin_lock_irqsave(&dev->slock, flags);
279b285192aSMauro Carvalho Chehab 	while (!list_empty(&q->active)) {
2800b6b6302SHans Verkuil 		buf = list_entry(q->active.next, struct cx88_buffer, list);
2810b6b6302SHans Verkuil 		list_del(&buf->list);
2822d700715SJunghak Sung 		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
283b285192aSMauro Carvalho Chehab 	}
284b285192aSMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->slock, flags);
285b285192aSMauro Carvalho Chehab }
286b285192aSMauro Carvalho Chehab 
cx8802_cancel_buffers(struct cx8802_dev * dev)287b285192aSMauro Carvalho Chehab void cx8802_cancel_buffers(struct cx8802_dev *dev)
288b285192aSMauro Carvalho Chehab {
28965bc2fe8SMauro Carvalho Chehab 	dprintk(1, "\n");
290b285192aSMauro Carvalho Chehab 	cx8802_stop_dma(dev);
2910b6b6302SHans Verkuil 	do_cancel_buffers(dev);
292b285192aSMauro Carvalho Chehab }
293399426caSMauro Carvalho Chehab EXPORT_SYMBOL(cx8802_cancel_buffers);
294b285192aSMauro Carvalho Chehab 
295b285192aSMauro Carvalho Chehab static const char *cx88_mpeg_irqs[32] = {
296b285192aSMauro Carvalho Chehab 	"ts_risci1", NULL, NULL, NULL,
297b285192aSMauro Carvalho Chehab 	"ts_risci2", NULL, NULL, NULL,
298b285192aSMauro Carvalho Chehab 	"ts_oflow",  NULL, NULL, NULL,
299b285192aSMauro Carvalho Chehab 	"ts_sync",   NULL, NULL, NULL,
300b285192aSMauro Carvalho Chehab 	"opc_err", "par_err", "rip_err", "pci_abort",
301b285192aSMauro Carvalho Chehab 	"ts_err?",
302b285192aSMauro Carvalho Chehab };
303b285192aSMauro Carvalho Chehab 
cx8802_mpeg_irq(struct cx8802_dev * dev)304b285192aSMauro Carvalho Chehab static void cx8802_mpeg_irq(struct cx8802_dev *dev)
305b285192aSMauro Carvalho Chehab {
306b285192aSMauro Carvalho Chehab 	struct cx88_core *core = dev->core;
307b285192aSMauro Carvalho Chehab 	u32 status, mask, count;
308b285192aSMauro Carvalho Chehab 
30965bc2fe8SMauro Carvalho Chehab 	dprintk(1, "\n");
310b285192aSMauro Carvalho Chehab 	status = cx_read(MO_TS_INTSTAT);
311b285192aSMauro Carvalho Chehab 	mask   = cx_read(MO_TS_INTMSK);
312b285192aSMauro Carvalho Chehab 	if (0 == (status & mask))
313b285192aSMauro Carvalho Chehab 		return;
314b285192aSMauro Carvalho Chehab 
315b285192aSMauro Carvalho Chehab 	cx_write(MO_TS_INTSTAT, status);
316b285192aSMauro Carvalho Chehab 
317b285192aSMauro Carvalho Chehab 	if (debug || (status & mask & ~0xff))
31865bc2fe8SMauro Carvalho Chehab 		cx88_print_irqbits("irq mpeg ",
319b285192aSMauro Carvalho Chehab 				   cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs),
320b285192aSMauro Carvalho Chehab 				   status, mask);
321b285192aSMauro Carvalho Chehab 
322b285192aSMauro Carvalho Chehab 	/* risc op code error */
323b285192aSMauro Carvalho Chehab 	if (status & (1 << 16)) {
32465bc2fe8SMauro Carvalho Chehab 		pr_warn("mpeg risc op code error\n");
325b285192aSMauro Carvalho Chehab 		cx_clear(MO_TS_DMACNTRL, 0x11);
326399426caSMauro Carvalho Chehab 		cx88_sram_channel_dump(dev->core,
327399426caSMauro Carvalho Chehab 				       &cx88_sram_channels[SRAM_CH28]);
328b285192aSMauro Carvalho Chehab 	}
329b285192aSMauro Carvalho Chehab 
330b285192aSMauro Carvalho Chehab 	/* risc1 y */
331b285192aSMauro Carvalho Chehab 	if (status & 0x01) {
332b285192aSMauro Carvalho Chehab 		dprintk(1, "wake up\n");
333b285192aSMauro Carvalho Chehab 		spin_lock(&dev->slock);
334b285192aSMauro Carvalho Chehab 		count = cx_read(MO_TS_GPCNT);
335b285192aSMauro Carvalho Chehab 		cx88_wakeup(dev->core, &dev->mpegq, count);
336b285192aSMauro Carvalho Chehab 		spin_unlock(&dev->slock);
337b285192aSMauro Carvalho Chehab 	}
338b285192aSMauro Carvalho Chehab 
339b285192aSMauro Carvalho Chehab 	/* other general errors */
340b285192aSMauro Carvalho Chehab 	if (status & 0x1f0100) {
341b285192aSMauro Carvalho Chehab 		dprintk(0, "general errors: 0x%08x\n", status & 0x1f0100);
342b285192aSMauro Carvalho Chehab 		spin_lock(&dev->slock);
343b285192aSMauro Carvalho Chehab 		cx8802_stop_dma(dev);
344b285192aSMauro Carvalho Chehab 		spin_unlock(&dev->slock);
345b285192aSMauro Carvalho Chehab 	}
346b285192aSMauro Carvalho Chehab }
347b285192aSMauro Carvalho Chehab 
348b285192aSMauro Carvalho Chehab #define MAX_IRQ_LOOP 10
349b285192aSMauro Carvalho Chehab 
cx8802_irq(int irq,void * dev_id)350b285192aSMauro Carvalho Chehab static irqreturn_t cx8802_irq(int irq, void *dev_id)
351b285192aSMauro Carvalho Chehab {
352b285192aSMauro Carvalho Chehab 	struct cx8802_dev *dev = dev_id;
353b285192aSMauro Carvalho Chehab 	struct cx88_core *core = dev->core;
354b285192aSMauro Carvalho Chehab 	u32 status;
355b285192aSMauro Carvalho Chehab 	int loop, handled = 0;
356b285192aSMauro Carvalho Chehab 
357b285192aSMauro Carvalho Chehab 	for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
358b285192aSMauro Carvalho Chehab 		status = cx_read(MO_PCI_INTSTAT) &
359b285192aSMauro Carvalho Chehab 			(core->pci_irqmask | PCI_INT_TSINT);
3607b61ba8fSMauro Carvalho Chehab 		if (status == 0)
361b285192aSMauro Carvalho Chehab 			goto out;
362b285192aSMauro Carvalho Chehab 		dprintk(1, "cx8802_irq\n");
363b285192aSMauro Carvalho Chehab 		dprintk(1, "    loop: %d/%d\n", loop, MAX_IRQ_LOOP);
364b285192aSMauro Carvalho Chehab 		dprintk(1, "    status: %d\n", status);
365b285192aSMauro Carvalho Chehab 		handled = 1;
366b285192aSMauro Carvalho Chehab 		cx_write(MO_PCI_INTSTAT, status);
367b285192aSMauro Carvalho Chehab 
368b285192aSMauro Carvalho Chehab 		if (status & core->pci_irqmask)
369b285192aSMauro Carvalho Chehab 			cx88_core_irq(core, status);
370b285192aSMauro Carvalho Chehab 		if (status & PCI_INT_TSINT)
371b285192aSMauro Carvalho Chehab 			cx8802_mpeg_irq(dev);
372c2c1b415SPeter Senna Tschudin 	}
3737b61ba8fSMauro Carvalho Chehab 	if (loop == MAX_IRQ_LOOP) {
374b285192aSMauro Carvalho Chehab 		dprintk(0, "clearing mask\n");
37565bc2fe8SMauro Carvalho Chehab 		pr_warn("irq loop -- clearing mask\n");
376b285192aSMauro Carvalho Chehab 		cx_write(MO_PCI_INTMSK, 0);
377b285192aSMauro Carvalho Chehab 	}
378b285192aSMauro Carvalho Chehab 
379b285192aSMauro Carvalho Chehab  out:
380b285192aSMauro Carvalho Chehab 	return IRQ_RETVAL(handled);
381b285192aSMauro Carvalho Chehab }
382b285192aSMauro Carvalho Chehab 
cx8802_init_common(struct cx8802_dev * dev)383b285192aSMauro Carvalho Chehab static int cx8802_init_common(struct cx8802_dev *dev)
384b285192aSMauro Carvalho Chehab {
385b285192aSMauro Carvalho Chehab 	struct cx88_core *core = dev->core;
386b285192aSMauro Carvalho Chehab 	int err;
387b285192aSMauro Carvalho Chehab 
388b285192aSMauro Carvalho Chehab 	/* pci init */
389b285192aSMauro Carvalho Chehab 	if (pci_enable_device(dev->pci))
390b285192aSMauro Carvalho Chehab 		return -EIO;
391b285192aSMauro Carvalho Chehab 	pci_set_master(dev->pci);
39200ae4ebcSChristophe JAILLET 	err = dma_set_mask(&dev->pci->dev, DMA_BIT_MASK(32));
3931a47de6eSChristoph Hellwig 	if (err) {
39465bc2fe8SMauro Carvalho Chehab 		pr_err("Oops: no 32bit PCI DMA ???\n");
395b285192aSMauro Carvalho Chehab 		return -EIO;
396b285192aSMauro Carvalho Chehab 	}
397b285192aSMauro Carvalho Chehab 
398b285192aSMauro Carvalho Chehab 	dev->pci_rev = dev->pci->revision;
399b285192aSMauro Carvalho Chehab 	pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER,  &dev->pci_lat);
40065bc2fe8SMauro Carvalho Chehab 	pr_info("found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n",
401b285192aSMauro Carvalho Chehab 		pci_name(dev->pci), dev->pci_rev, dev->pci->irq,
40265bc2fe8SMauro Carvalho Chehab 		dev->pci_lat,
40365bc2fe8SMauro Carvalho Chehab 		(unsigned long long)pci_resource_start(dev->pci, 0));
404b285192aSMauro Carvalho Chehab 
405b285192aSMauro Carvalho Chehab 	/* initialize driver struct */
406b285192aSMauro Carvalho Chehab 	spin_lock_init(&dev->slock);
407b285192aSMauro Carvalho Chehab 
408b285192aSMauro Carvalho Chehab 	/* init dma queue */
409b285192aSMauro Carvalho Chehab 	INIT_LIST_HEAD(&dev->mpegq.active);
410b285192aSMauro Carvalho Chehab 
411b285192aSMauro Carvalho Chehab 	/* get irq */
412b285192aSMauro Carvalho Chehab 	err = request_irq(dev->pci->irq, cx8802_irq,
4133e018fe4SMichael Opdenacker 			  IRQF_SHARED, dev->core->name, dev);
414b285192aSMauro Carvalho Chehab 	if (err < 0) {
41565bc2fe8SMauro Carvalho Chehab 		pr_err("can't get IRQ %d\n", dev->pci->irq);
416b285192aSMauro Carvalho Chehab 		return err;
417b285192aSMauro Carvalho Chehab 	}
418b285192aSMauro Carvalho Chehab 	cx_set(MO_PCI_INTMSK, core->pci_irqmask);
419b285192aSMauro Carvalho Chehab 
420b285192aSMauro Carvalho Chehab 	/* everything worked */
421b285192aSMauro Carvalho Chehab 	pci_set_drvdata(dev->pci, dev);
422b285192aSMauro Carvalho Chehab 	return 0;
423b285192aSMauro Carvalho Chehab }
424b285192aSMauro Carvalho Chehab 
cx8802_fini_common(struct cx8802_dev * dev)425b285192aSMauro Carvalho Chehab static void cx8802_fini_common(struct cx8802_dev *dev)
426b285192aSMauro Carvalho Chehab {
42765bc2fe8SMauro Carvalho Chehab 	dprintk(2, "\n");
428b285192aSMauro Carvalho Chehab 	cx8802_stop_dma(dev);
429b285192aSMauro Carvalho Chehab 	pci_disable_device(dev->pci);
430b285192aSMauro Carvalho Chehab 
431b285192aSMauro Carvalho Chehab 	/* unregister stuff */
432b285192aSMauro Carvalho Chehab 	free_irq(dev->pci->irq, dev);
433b285192aSMauro Carvalho Chehab }
434b285192aSMauro Carvalho Chehab 
435b285192aSMauro Carvalho Chehab /* ----------------------------------------------------------- */
436b285192aSMauro Carvalho Chehab 
cx8802_suspend_common(struct pci_dev * pci_dev,pm_message_t state)437b285192aSMauro Carvalho Chehab static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
438b285192aSMauro Carvalho Chehab {
439b285192aSMauro Carvalho Chehab 	struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
4405ddfbbb9SAlexey Khoroshilov 	unsigned long flags;
441b285192aSMauro Carvalho Chehab 
442b285192aSMauro Carvalho Chehab 	/* stop mpeg dma */
4435ddfbbb9SAlexey Khoroshilov 	spin_lock_irqsave(&dev->slock, flags);
444b285192aSMauro Carvalho Chehab 	if (!list_empty(&dev->mpegq.active)) {
445b285192aSMauro Carvalho Chehab 		dprintk(2, "suspend\n");
44665bc2fe8SMauro Carvalho Chehab 		pr_info("suspend mpeg\n");
447b285192aSMauro Carvalho Chehab 		cx8802_stop_dma(dev);
448b285192aSMauro Carvalho Chehab 	}
4495ddfbbb9SAlexey Khoroshilov 	spin_unlock_irqrestore(&dev->slock, flags);
450b285192aSMauro Carvalho Chehab 
451b285192aSMauro Carvalho Chehab 	/* FIXME -- shutdown device */
452b285192aSMauro Carvalho Chehab 	cx88_shutdown(dev->core);
453b285192aSMauro Carvalho Chehab 
454b285192aSMauro Carvalho Chehab 	pci_save_state(pci_dev);
455399426caSMauro Carvalho Chehab 	if (pci_set_power_state(pci_dev,
456399426caSMauro Carvalho Chehab 				pci_choose_state(pci_dev, state)) != 0) {
457b285192aSMauro Carvalho Chehab 		pci_disable_device(pci_dev);
458b285192aSMauro Carvalho Chehab 		dev->state.disabled = 1;
459b285192aSMauro Carvalho Chehab 	}
460b285192aSMauro Carvalho Chehab 	return 0;
461b285192aSMauro Carvalho Chehab }
462b285192aSMauro Carvalho Chehab 
cx8802_resume_common(struct pci_dev * pci_dev)463b285192aSMauro Carvalho Chehab static int cx8802_resume_common(struct pci_dev *pci_dev)
464b285192aSMauro Carvalho Chehab {
465b285192aSMauro Carvalho Chehab 	struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
4665ddfbbb9SAlexey Khoroshilov 	unsigned long flags;
467b285192aSMauro Carvalho Chehab 	int err;
468b285192aSMauro Carvalho Chehab 
469b285192aSMauro Carvalho Chehab 	if (dev->state.disabled) {
470b285192aSMauro Carvalho Chehab 		err = pci_enable_device(pci_dev);
471b285192aSMauro Carvalho Chehab 		if (err) {
47265bc2fe8SMauro Carvalho Chehab 			pr_err("can't enable device\n");
473b285192aSMauro Carvalho Chehab 			return err;
474b285192aSMauro Carvalho Chehab 		}
475b285192aSMauro Carvalho Chehab 		dev->state.disabled = 0;
476b285192aSMauro Carvalho Chehab 	}
477b285192aSMauro Carvalho Chehab 	err = pci_set_power_state(pci_dev, PCI_D0);
478b285192aSMauro Carvalho Chehab 	if (err) {
47965bc2fe8SMauro Carvalho Chehab 		pr_err("can't enable device\n");
480b285192aSMauro Carvalho Chehab 		pci_disable_device(pci_dev);
481b285192aSMauro Carvalho Chehab 		dev->state.disabled = 1;
482b285192aSMauro Carvalho Chehab 
483b285192aSMauro Carvalho Chehab 		return err;
484b285192aSMauro Carvalho Chehab 	}
485b285192aSMauro Carvalho Chehab 	pci_restore_state(pci_dev);
486b285192aSMauro Carvalho Chehab 
487b285192aSMauro Carvalho Chehab 	/* FIXME: re-initialize hardware */
488b285192aSMauro Carvalho Chehab 	cx88_reset(dev->core);
489b285192aSMauro Carvalho Chehab 
490b285192aSMauro Carvalho Chehab 	/* restart video+vbi capture */
4915ddfbbb9SAlexey Khoroshilov 	spin_lock_irqsave(&dev->slock, flags);
492b285192aSMauro Carvalho Chehab 	if (!list_empty(&dev->mpegq.active)) {
49365bc2fe8SMauro Carvalho Chehab 		pr_info("resume mpeg\n");
494b285192aSMauro Carvalho Chehab 		cx8802_restart_queue(dev, &dev->mpegq);
495b285192aSMauro Carvalho Chehab 	}
4965ddfbbb9SAlexey Khoroshilov 	spin_unlock_irqrestore(&dev->slock, flags);
497b285192aSMauro Carvalho Chehab 
498b285192aSMauro Carvalho Chehab 	return 0;
499b285192aSMauro Carvalho Chehab }
500b285192aSMauro Carvalho Chehab 
cx8802_get_driver(struct cx8802_dev * dev,enum cx88_board_type btype)501399426caSMauro Carvalho Chehab struct cx8802_driver *cx8802_get_driver(struct cx8802_dev *dev,
502399426caSMauro Carvalho Chehab 					enum cx88_board_type btype)
503b285192aSMauro Carvalho Chehab {
504b285192aSMauro Carvalho Chehab 	struct cx8802_driver *d;
505b285192aSMauro Carvalho Chehab 
506b285192aSMauro Carvalho Chehab 	list_for_each_entry(d, &dev->drvlist, drvlist)
507b285192aSMauro Carvalho Chehab 		if (d->type_id == btype)
508b285192aSMauro Carvalho Chehab 			return d;
509b285192aSMauro Carvalho Chehab 
510b285192aSMauro Carvalho Chehab 	return NULL;
511b285192aSMauro Carvalho Chehab }
512399426caSMauro Carvalho Chehab EXPORT_SYMBOL(cx8802_get_driver);
513b285192aSMauro Carvalho Chehab 
514b285192aSMauro Carvalho Chehab /* Driver asked for hardware access. */
cx8802_request_acquire(struct cx8802_driver * drv)515b285192aSMauro Carvalho Chehab static int cx8802_request_acquire(struct cx8802_driver *drv)
516b285192aSMauro Carvalho Chehab {
517b285192aSMauro Carvalho Chehab 	struct cx88_core *core = drv->core;
518b285192aSMauro Carvalho Chehab 	unsigned int	i;
519b285192aSMauro Carvalho Chehab 
520b285192aSMauro Carvalho Chehab 	/* Fail a request for hardware if the device is busy. */
521b285192aSMauro Carvalho Chehab 	if (core->active_type_id != CX88_BOARD_NONE &&
522b285192aSMauro Carvalho Chehab 	    core->active_type_id != drv->type_id)
523b285192aSMauro Carvalho Chehab 		return -EBUSY;
524b285192aSMauro Carvalho Chehab 
525b285192aSMauro Carvalho Chehab 	if (drv->type_id == CX88_MPEG_DVB) {
526b285192aSMauro Carvalho Chehab 		/* When switching to DVB, always set the input to the tuner */
527b285192aSMauro Carvalho Chehab 		core->last_analog_input = core->input;
528b285192aSMauro Carvalho Chehab 		core->input = 0;
529b285192aSMauro Carvalho Chehab 		for (i = 0;
530ca1cfc3fSXu Wang 		     i < ARRAY_SIZE(core->board.input);
531b285192aSMauro Carvalho Chehab 		     i++) {
532b285192aSMauro Carvalho Chehab 			if (core->board.input[i].type == CX88_VMUX_DVB) {
533b285192aSMauro Carvalho Chehab 				core->input = i;
534b285192aSMauro Carvalho Chehab 				break;
535b285192aSMauro Carvalho Chehab 			}
536b285192aSMauro Carvalho Chehab 		}
537b285192aSMauro Carvalho Chehab 	}
538b285192aSMauro Carvalho Chehab 
539399426caSMauro Carvalho Chehab 	if (drv->advise_acquire) {
540b285192aSMauro Carvalho Chehab 		core->active_ref++;
541b285192aSMauro Carvalho Chehab 		if (core->active_type_id == CX88_BOARD_NONE) {
542b285192aSMauro Carvalho Chehab 			core->active_type_id = drv->type_id;
543b285192aSMauro Carvalho Chehab 			drv->advise_acquire(drv);
544b285192aSMauro Carvalho Chehab 		}
545b285192aSMauro Carvalho Chehab 
54665bc2fe8SMauro Carvalho Chehab 		dprintk(1, "Post acquire GPIO=%x\n", cx_read(MO_GP0_IO));
547b285192aSMauro Carvalho Chehab 	}
548b285192aSMauro Carvalho Chehab 
549b285192aSMauro Carvalho Chehab 	return 0;
550b285192aSMauro Carvalho Chehab }
551b285192aSMauro Carvalho Chehab 
552b285192aSMauro Carvalho Chehab /* Driver asked to release hardware. */
cx8802_request_release(struct cx8802_driver * drv)553b285192aSMauro Carvalho Chehab static int cx8802_request_release(struct cx8802_driver *drv)
554b285192aSMauro Carvalho Chehab {
555b285192aSMauro Carvalho Chehab 	struct cx88_core *core = drv->core;
556b285192aSMauro Carvalho Chehab 
557399426caSMauro Carvalho Chehab 	if (drv->advise_release && --core->active_ref == 0) {
558b285192aSMauro Carvalho Chehab 		if (drv->type_id == CX88_MPEG_DVB) {
559399426caSMauro Carvalho Chehab 			/*
560399426caSMauro Carvalho Chehab 			 * If the DVB driver is releasing, reset the input
561399426caSMauro Carvalho Chehab 			 * state to the last configured analog input
562399426caSMauro Carvalho Chehab 			 */
563b285192aSMauro Carvalho Chehab 			core->input = core->last_analog_input;
564b285192aSMauro Carvalho Chehab 		}
565b285192aSMauro Carvalho Chehab 
566b285192aSMauro Carvalho Chehab 		drv->advise_release(drv);
567b285192aSMauro Carvalho Chehab 		core->active_type_id = CX88_BOARD_NONE;
56865bc2fe8SMauro Carvalho Chehab 		dprintk(1, "Post release GPIO=%x\n", cx_read(MO_GP0_IO));
569b285192aSMauro Carvalho Chehab 	}
570b285192aSMauro Carvalho Chehab 
571b285192aSMauro Carvalho Chehab 	return 0;
572b285192aSMauro Carvalho Chehab }
573b285192aSMauro Carvalho Chehab 
cx8802_check_driver(struct cx8802_driver * drv)574b285192aSMauro Carvalho Chehab static int cx8802_check_driver(struct cx8802_driver *drv)
575b285192aSMauro Carvalho Chehab {
576399426caSMauro Carvalho Chehab 	if (!drv)
577b285192aSMauro Carvalho Chehab 		return -ENODEV;
578b285192aSMauro Carvalho Chehab 
579b285192aSMauro Carvalho Chehab 	if ((drv->type_id != CX88_MPEG_DVB) &&
580b285192aSMauro Carvalho Chehab 	    (drv->type_id != CX88_MPEG_BLACKBIRD))
581b285192aSMauro Carvalho Chehab 		return -EINVAL;
582b285192aSMauro Carvalho Chehab 
583b285192aSMauro Carvalho Chehab 	if ((drv->hw_access != CX8802_DRVCTL_SHARED) &&
584b285192aSMauro Carvalho Chehab 	    (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE))
585b285192aSMauro Carvalho Chehab 		return -EINVAL;
586b285192aSMauro Carvalho Chehab 
587399426caSMauro Carvalho Chehab 	if ((!drv->probe) ||
588399426caSMauro Carvalho Chehab 	    (!drv->remove) ||
589399426caSMauro Carvalho Chehab 	    (!drv->advise_acquire) ||
590399426caSMauro Carvalho Chehab 	    (!drv->advise_release))
591b285192aSMauro Carvalho Chehab 		return -EINVAL;
592b285192aSMauro Carvalho Chehab 
593b285192aSMauro Carvalho Chehab 	return 0;
594b285192aSMauro Carvalho Chehab }
595b285192aSMauro Carvalho Chehab 
cx8802_register_driver(struct cx8802_driver * drv)596b285192aSMauro Carvalho Chehab int cx8802_register_driver(struct cx8802_driver *drv)
597b285192aSMauro Carvalho Chehab {
598b285192aSMauro Carvalho Chehab 	struct cx8802_dev *dev;
599b285192aSMauro Carvalho Chehab 	struct cx8802_driver *driver;
600b285192aSMauro Carvalho Chehab 	int err, i = 0;
601b285192aSMauro Carvalho Chehab 
60265bc2fe8SMauro Carvalho Chehab 	pr_info("registering cx8802 driver, type: %s access: %s\n",
603b285192aSMauro Carvalho Chehab 		drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
604399426caSMauro Carvalho Chehab 		drv->hw_access == CX8802_DRVCTL_SHARED ?
605399426caSMauro Carvalho Chehab 				  "shared" : "exclusive");
606b285192aSMauro Carvalho Chehab 
607399426caSMauro Carvalho Chehab 	err = cx8802_check_driver(drv);
608399426caSMauro Carvalho Chehab 	if (err) {
60965bc2fe8SMauro Carvalho Chehab 		pr_err("cx8802_driver is invalid\n");
610b285192aSMauro Carvalho Chehab 		return err;
611b285192aSMauro Carvalho Chehab 	}
612b285192aSMauro Carvalho Chehab 
613b285192aSMauro Carvalho Chehab 	mutex_lock(&cx8802_mutex);
614b285192aSMauro Carvalho Chehab 
615b285192aSMauro Carvalho Chehab 	list_for_each_entry(dev, &cx8802_devlist, devlist) {
61665bc2fe8SMauro Carvalho Chehab 		pr_info("subsystem: %04x:%04x, board: %s [card=%d]\n",
61765bc2fe8SMauro Carvalho Chehab 			dev->pci->subsystem_vendor,
618b285192aSMauro Carvalho Chehab 			dev->pci->subsystem_device, dev->core->board.name,
619b285192aSMauro Carvalho Chehab 			dev->core->boardnr);
620b285192aSMauro Carvalho Chehab 
621b285192aSMauro Carvalho Chehab 		/* Bring up a new struct for each driver instance */
622b285192aSMauro Carvalho Chehab 		driver = kzalloc(sizeof(*drv), GFP_KERNEL);
623399426caSMauro Carvalho Chehab 		if (!driver) {
624b285192aSMauro Carvalho Chehab 			err = -ENOMEM;
625b285192aSMauro Carvalho Chehab 			goto out;
626b285192aSMauro Carvalho Chehab 		}
627b285192aSMauro Carvalho Chehab 
628b285192aSMauro Carvalho Chehab 		/* Snapshot of the driver registration data */
629b285192aSMauro Carvalho Chehab 		drv->core = dev->core;
630b285192aSMauro Carvalho Chehab 		drv->suspend = cx8802_suspend_common;
631b285192aSMauro Carvalho Chehab 		drv->resume = cx8802_resume_common;
632b285192aSMauro Carvalho Chehab 		drv->request_acquire = cx8802_request_acquire;
633b285192aSMauro Carvalho Chehab 		drv->request_release = cx8802_request_release;
634b285192aSMauro Carvalho Chehab 		memcpy(driver, drv, sizeof(*driver));
635b285192aSMauro Carvalho Chehab 
636b285192aSMauro Carvalho Chehab 		mutex_lock(&drv->core->lock);
637b285192aSMauro Carvalho Chehab 		err = drv->probe(driver);
638b285192aSMauro Carvalho Chehab 		if (err == 0) {
639b285192aSMauro Carvalho Chehab 			i++;
640b285192aSMauro Carvalho Chehab 			list_add_tail(&driver->drvlist, &dev->drvlist);
641b285192aSMauro Carvalho Chehab 		} else {
64265bc2fe8SMauro Carvalho Chehab 			pr_err("cx8802 probe failed, err = %d\n", err);
643b285192aSMauro Carvalho Chehab 		}
644b285192aSMauro Carvalho Chehab 		mutex_unlock(&drv->core->lock);
645b285192aSMauro Carvalho Chehab 	}
646b285192aSMauro Carvalho Chehab 
647b285192aSMauro Carvalho Chehab 	err = i ? 0 : -ENODEV;
648b285192aSMauro Carvalho Chehab out:
649b285192aSMauro Carvalho Chehab 	mutex_unlock(&cx8802_mutex);
650b285192aSMauro Carvalho Chehab 	return err;
651b285192aSMauro Carvalho Chehab }
652399426caSMauro Carvalho Chehab EXPORT_SYMBOL(cx8802_register_driver);
653b285192aSMauro Carvalho Chehab 
cx8802_unregister_driver(struct cx8802_driver * drv)654b285192aSMauro Carvalho Chehab int cx8802_unregister_driver(struct cx8802_driver *drv)
655b285192aSMauro Carvalho Chehab {
656b285192aSMauro Carvalho Chehab 	struct cx8802_dev *dev;
657b285192aSMauro Carvalho Chehab 	struct cx8802_driver *d, *dtmp;
658b285192aSMauro Carvalho Chehab 	int err = 0;
659b285192aSMauro Carvalho Chehab 
66065bc2fe8SMauro Carvalho Chehab 	pr_info("unregistering cx8802 driver, type: %s access: %s\n",
661b285192aSMauro Carvalho Chehab 		drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
662399426caSMauro Carvalho Chehab 		drv->hw_access == CX8802_DRVCTL_SHARED ?
663399426caSMauro Carvalho Chehab 				  "shared" : "exclusive");
664b285192aSMauro Carvalho Chehab 
665b285192aSMauro Carvalho Chehab 	mutex_lock(&cx8802_mutex);
666b285192aSMauro Carvalho Chehab 
667b285192aSMauro Carvalho Chehab 	list_for_each_entry(dev, &cx8802_devlist, devlist) {
66865bc2fe8SMauro Carvalho Chehab 		pr_info("subsystem: %04x:%04x, board: %s [card=%d]\n",
66965bc2fe8SMauro Carvalho Chehab 			dev->pci->subsystem_vendor,
670b285192aSMauro Carvalho Chehab 			dev->pci->subsystem_device, dev->core->board.name,
671b285192aSMauro Carvalho Chehab 			dev->core->boardnr);
672b285192aSMauro Carvalho Chehab 
673b285192aSMauro Carvalho Chehab 		mutex_lock(&dev->core->lock);
674b285192aSMauro Carvalho Chehab 
675b285192aSMauro Carvalho Chehab 		list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) {
676b285192aSMauro Carvalho Chehab 			/* only unregister the correct driver type */
677b285192aSMauro Carvalho Chehab 			if (d->type_id != drv->type_id)
678b285192aSMauro Carvalho Chehab 				continue;
679b285192aSMauro Carvalho Chehab 
680b285192aSMauro Carvalho Chehab 			err = d->remove(d);
681b285192aSMauro Carvalho Chehab 			if (err == 0) {
682b285192aSMauro Carvalho Chehab 				list_del(&d->drvlist);
683b285192aSMauro Carvalho Chehab 				kfree(d);
684b285192aSMauro Carvalho Chehab 			} else
68565bc2fe8SMauro Carvalho Chehab 				pr_err("cx8802 driver remove failed (%d)\n",
68665bc2fe8SMauro Carvalho Chehab 				       err);
687b285192aSMauro Carvalho Chehab 		}
688b285192aSMauro Carvalho Chehab 
689b285192aSMauro Carvalho Chehab 		mutex_unlock(&dev->core->lock);
690b285192aSMauro Carvalho Chehab 	}
691b285192aSMauro Carvalho Chehab 
692b285192aSMauro Carvalho Chehab 	mutex_unlock(&cx8802_mutex);
693b285192aSMauro Carvalho Chehab 
694b285192aSMauro Carvalho Chehab 	return err;
695b285192aSMauro Carvalho Chehab }
696399426caSMauro Carvalho Chehab EXPORT_SYMBOL(cx8802_unregister_driver);
697b285192aSMauro Carvalho Chehab 
698b285192aSMauro Carvalho Chehab /* ----------------------------------------------------------- */
cx8802_probe(struct pci_dev * pci_dev,const struct pci_device_id * pci_id)6994c62e976SGreg Kroah-Hartman static int cx8802_probe(struct pci_dev *pci_dev,
700b285192aSMauro Carvalho Chehab 			const struct pci_device_id *pci_id)
701b285192aSMauro Carvalho Chehab {
702b285192aSMauro Carvalho Chehab 	struct cx8802_dev *dev;
703b285192aSMauro Carvalho Chehab 	struct cx88_core  *core;
704b285192aSMauro Carvalho Chehab 	int err;
705b285192aSMauro Carvalho Chehab 
706b285192aSMauro Carvalho Chehab 	/* general setup */
707b285192aSMauro Carvalho Chehab 	core = cx88_core_get(pci_dev);
708399426caSMauro Carvalho Chehab 	if (!core)
709b285192aSMauro Carvalho Chehab 		return -EINVAL;
710b285192aSMauro Carvalho Chehab 
71165bc2fe8SMauro Carvalho Chehab 	pr_info("cx2388x 8802 Driver Manager\n");
712b285192aSMauro Carvalho Chehab 
713b285192aSMauro Carvalho Chehab 	err = -ENODEV;
714b285192aSMauro Carvalho Chehab 	if (!core->board.mpeg)
715b285192aSMauro Carvalho Chehab 		goto fail_core;
716b285192aSMauro Carvalho Chehab 
717b285192aSMauro Carvalho Chehab 	err = -ENOMEM;
718b285192aSMauro Carvalho Chehab 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
719399426caSMauro Carvalho Chehab 	if (!dev)
720b285192aSMauro Carvalho Chehab 		goto fail_core;
721b285192aSMauro Carvalho Chehab 	dev->pci = pci_dev;
722b285192aSMauro Carvalho Chehab 	dev->core = core;
723b285192aSMauro Carvalho Chehab 
724b285192aSMauro Carvalho Chehab 	/* Maintain a reference so cx88-video can query the 8802 device. */
725b285192aSMauro Carvalho Chehab 	core->dvbdev = dev;
726b285192aSMauro Carvalho Chehab 
727b285192aSMauro Carvalho Chehab 	err = cx8802_init_common(dev);
728b285192aSMauro Carvalho Chehab 	if (err != 0)
7292bc46b3aSHans Verkuil 		goto fail_dev;
730b285192aSMauro Carvalho Chehab 
731b285192aSMauro Carvalho Chehab 	INIT_LIST_HEAD(&dev->drvlist);
732b285192aSMauro Carvalho Chehab 	mutex_lock(&cx8802_mutex);
733b285192aSMauro Carvalho Chehab 	list_add_tail(&dev->devlist, &cx8802_devlist);
734b285192aSMauro Carvalho Chehab 	mutex_unlock(&cx8802_mutex);
735b285192aSMauro Carvalho Chehab 
736b285192aSMauro Carvalho Chehab 	/* now autoload cx88-dvb or cx88-blackbird */
737b285192aSMauro Carvalho Chehab 	request_modules(dev);
738b285192aSMauro Carvalho Chehab 	return 0;
739b285192aSMauro Carvalho Chehab 
74096df988bSChristian Engelmayer  fail_dev:
741b285192aSMauro Carvalho Chehab 	kfree(dev);
742b285192aSMauro Carvalho Chehab  fail_core:
743b285192aSMauro Carvalho Chehab 	core->dvbdev = NULL;
744b285192aSMauro Carvalho Chehab 	cx88_core_put(core, pci_dev);
745b285192aSMauro Carvalho Chehab 	return err;
746b285192aSMauro Carvalho Chehab }
747b285192aSMauro Carvalho Chehab 
cx8802_remove(struct pci_dev * pci_dev)7484c62e976SGreg Kroah-Hartman static void cx8802_remove(struct pci_dev *pci_dev)
749b285192aSMauro Carvalho Chehab {
750b285192aSMauro Carvalho Chehab 	struct cx8802_dev *dev;
751b285192aSMauro Carvalho Chehab 
752b285192aSMauro Carvalho Chehab 	dev = pci_get_drvdata(pci_dev);
753b285192aSMauro Carvalho Chehab 
754b285192aSMauro Carvalho Chehab 	dprintk(1, "%s\n", __func__);
755b285192aSMauro Carvalho Chehab 
756b285192aSMauro Carvalho Chehab 	flush_request_modules(dev);
757b285192aSMauro Carvalho Chehab 
758b285192aSMauro Carvalho Chehab 	mutex_lock(&dev->core->lock);
759b285192aSMauro Carvalho Chehab 
760b285192aSMauro Carvalho Chehab 	if (!list_empty(&dev->drvlist)) {
761b285192aSMauro Carvalho Chehab 		struct cx8802_driver *drv, *tmp;
762b285192aSMauro Carvalho Chehab 		int err;
763b285192aSMauro Carvalho Chehab 
76465bc2fe8SMauro Carvalho Chehab 		pr_warn("Trying to remove cx8802 driver while cx8802 sub-drivers still loaded?!\n");
765b285192aSMauro Carvalho Chehab 
766b285192aSMauro Carvalho Chehab 		list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) {
767b285192aSMauro Carvalho Chehab 			err = drv->remove(drv);
768b285192aSMauro Carvalho Chehab 			if (err == 0) {
769b285192aSMauro Carvalho Chehab 				list_del(&drv->drvlist);
770b285192aSMauro Carvalho Chehab 			} else
77165bc2fe8SMauro Carvalho Chehab 				pr_err("cx8802 driver remove failed (%d)\n",
77265bc2fe8SMauro Carvalho Chehab 				       err);
773b285192aSMauro Carvalho Chehab 			kfree(drv);
774b285192aSMauro Carvalho Chehab 		}
775b285192aSMauro Carvalho Chehab 	}
776b285192aSMauro Carvalho Chehab 
777b285192aSMauro Carvalho Chehab 	mutex_unlock(&dev->core->lock);
778b285192aSMauro Carvalho Chehab 
779b285192aSMauro Carvalho Chehab 	/* Destroy any 8802 reference. */
780b285192aSMauro Carvalho Chehab 	dev->core->dvbdev = NULL;
781b285192aSMauro Carvalho Chehab 
782b285192aSMauro Carvalho Chehab 	/* common */
783b285192aSMauro Carvalho Chehab 	cx8802_fini_common(dev);
784b285192aSMauro Carvalho Chehab 	cx88_core_put(dev->core, dev->pci);
785b285192aSMauro Carvalho Chehab 	kfree(dev);
786b285192aSMauro Carvalho Chehab }
787b285192aSMauro Carvalho Chehab 
788b285192aSMauro Carvalho Chehab static const struct pci_device_id cx8802_pci_tbl[] = {
789b285192aSMauro Carvalho Chehab 	{
790b285192aSMauro Carvalho Chehab 		.vendor       = 0x14f1,
791b285192aSMauro Carvalho Chehab 		.device       = 0x8802,
792b285192aSMauro Carvalho Chehab 		.subvendor    = PCI_ANY_ID,
793b285192aSMauro Carvalho Chehab 		.subdevice    = PCI_ANY_ID,
794b285192aSMauro Carvalho Chehab 	}, {
795b285192aSMauro Carvalho Chehab 		/* --- end of list --- */
796b285192aSMauro Carvalho Chehab 	}
797b285192aSMauro Carvalho Chehab };
798b285192aSMauro Carvalho Chehab MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
799b285192aSMauro Carvalho Chehab 
800b285192aSMauro Carvalho Chehab static struct pci_driver cx8802_pci_driver = {
801b285192aSMauro Carvalho Chehab 	.name     = "cx88-mpeg driver manager",
802b285192aSMauro Carvalho Chehab 	.id_table = cx8802_pci_tbl,
803b285192aSMauro Carvalho Chehab 	.probe    = cx8802_probe,
8044c62e976SGreg Kroah-Hartman 	.remove   = cx8802_remove,
805b285192aSMauro Carvalho Chehab };
806b285192aSMauro Carvalho Chehab 
80738b25adcSSachin Kamat module_pci_driver(cx8802_pci_driver);
808