diff options
author | Andreas Oberritter <obi@linuxtv.org> | 2005-07-07 20:57:46 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-07 21:23:55 -0400 |
commit | c7cadb3a02b5803c2f251b5cd84fbdc8fbec05e9 (patch) | |
tree | 92b557ca6ec2890ac34f7e2595025e2b8f6a11e3 /drivers/media/dvb | |
parent | 48e4cc2d210e817e808ac9db598ce3fb5d09c205 (diff) |
[PATCH] dvb: add Pluto2 driver
Add driver for the Satelco Easywatch Mobile DVB-T card (based on Pluto2 chip).
Signed-off-by: Andreas Oberritter <obi@linuxtv.org>
Signed-off-by: Johannes Stezenbach <js@linuxtv.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/media/dvb')
-rw-r--r-- | drivers/media/dvb/Kconfig | 4 | ||||
-rw-r--r-- | drivers/media/dvb/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/dvb/pluto2/Kconfig | 16 | ||||
-rw-r--r-- | drivers/media/dvb/pluto2/Makefile | 3 | ||||
-rw-r--r-- | drivers/media/dvb/pluto2/pluto2.c | 809 |
5 files changed, 833 insertions, 1 deletions
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 01387f883cdf..3f0ec6be03ae 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig | |||
@@ -40,6 +40,10 @@ comment "Supported BT878 Adapters" | |||
40 | depends on DVB_CORE && PCI | 40 | depends on DVB_CORE && PCI |
41 | source "drivers/media/dvb/bt8xx/Kconfig" | 41 | source "drivers/media/dvb/bt8xx/Kconfig" |
42 | 42 | ||
43 | comment "Supported Pluto2 Adapters" | ||
44 | depends on DVB_CORE && PCI | ||
45 | source "drivers/media/dvb/pluto2/Kconfig" | ||
46 | |||
43 | comment "Supported DVB Frontends" | 47 | comment "Supported DVB Frontends" |
44 | depends on DVB_CORE | 48 | depends on DVB_CORE |
45 | source "drivers/media/dvb/frontends/Kconfig" | 49 | source "drivers/media/dvb/frontends/Kconfig" |
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index 3c6ff1619103..a7ad0841e6fc 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile | |||
@@ -2,4 +2,4 @@ | |||
2 | # Makefile for the kernel multimedia device drivers. | 2 | # Makefile for the kernel multimedia device drivers. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ | 5 | obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ |
diff --git a/drivers/media/dvb/pluto2/Kconfig b/drivers/media/dvb/pluto2/Kconfig new file mode 100644 index 000000000000..f02842be0d60 --- /dev/null +++ b/drivers/media/dvb/pluto2/Kconfig | |||
@@ -0,0 +1,16 @@ | |||
1 | config DVB_PLUTO2 | ||
2 | tristate "Pluto2 cards" | ||
3 | depends on DVB_CORE && PCI | ||
4 | select I2C | ||
5 | select I2C_ALGOBIT | ||
6 | select DVB_TDA1004X | ||
7 | help | ||
8 | Support for PCI cards based on the Pluto2 FPGA like the Satelco | ||
9 | Easywatch Mobile Terrestrial DVB-T Receiver. | ||
10 | |||
11 | Since these cards have no MPEG decoder onboard, they transmit | ||
12 | only compressed MPEG data over the PCI bus, so you need | ||
13 | an external software decoder to watch TV on your computer. | ||
14 | |||
15 | Say Y or M if you own such a device and want to use it. | ||
16 | |||
diff --git a/drivers/media/dvb/pluto2/Makefile b/drivers/media/dvb/pluto2/Makefile new file mode 100644 index 000000000000..86ca84b2be6e --- /dev/null +++ b/drivers/media/dvb/pluto2/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | obj-$(CONFIG_DVB_PLUTO2) = pluto2.o | ||
2 | |||
3 | EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ | ||
diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c new file mode 100644 index 000000000000..706e0bcb5ede --- /dev/null +++ b/drivers/media/dvb/pluto2/pluto2.c | |||
@@ -0,0 +1,809 @@ | |||
1 | /* | ||
2 | * pluto2.c - Satelco Easywatch Mobile Terrestrial Receiver [DVB-T] | ||
3 | * | ||
4 | * Copyright (C) 2005 Andreas Oberritter <obi@linuxtv.org> | ||
5 | * | ||
6 | * based on pluto2.c 1.10 - http://instinct-wp8.no-ip.org/pluto/ | ||
7 | * by Dany Salman <salmandany@yahoo.fr> | ||
8 | * Copyright (c) 2004 TDF | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/i2c.h> | ||
27 | #include <linux/i2c-algo-bit.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/pci.h> | ||
32 | #include <linux/dma-mapping.h> | ||
33 | |||
34 | #include "demux.h" | ||
35 | #include "dmxdev.h" | ||
36 | #include "dvb_demux.h" | ||
37 | #include "dvb_frontend.h" | ||
38 | #include "dvb_net.h" | ||
39 | #include "dvbdev.h" | ||
40 | #include "tda1004x.h" | ||
41 | |||
42 | #define DRIVER_NAME "pluto2" | ||
43 | |||
44 | #define REG_PIDn(n) ((n) << 2) /* PID n pattern registers */ | ||
45 | #define REG_PCAR 0x0020 /* PC address register */ | ||
46 | #define REG_TSCR 0x0024 /* TS ctrl & status */ | ||
47 | #define REG_MISC 0x0028 /* miscellaneous */ | ||
48 | #define REG_MMAC 0x002c /* MSB MAC address */ | ||
49 | #define REG_IMAC 0x0030 /* ISB MAC address */ | ||
50 | #define REG_LMAC 0x0034 /* LSB MAC address */ | ||
51 | #define REG_SPID 0x0038 /* SPI data */ | ||
52 | #define REG_SLCS 0x003c /* serial links ctrl/status */ | ||
53 | |||
54 | #define PID0_NOFIL (0x0001 << 16) | ||
55 | #define PIDn_ENP (0x0001 << 15) | ||
56 | #define PID0_END (0x0001 << 14) | ||
57 | #define PID0_AFIL (0x0001 << 13) | ||
58 | #define PIDn_PID (0x1fff << 0) | ||
59 | |||
60 | #define TSCR_NBPACKETS (0x00ff << 24) | ||
61 | #define TSCR_DEM (0x0001 << 17) | ||
62 | #define TSCR_DE (0x0001 << 16) | ||
63 | #define TSCR_RSTN (0x0001 << 15) | ||
64 | #define TSCR_MSKO (0x0001 << 14) | ||
65 | #define TSCR_MSKA (0x0001 << 13) | ||
66 | #define TSCR_MSKL (0x0001 << 12) | ||
67 | #define TSCR_OVR (0x0001 << 11) | ||
68 | #define TSCR_AFUL (0x0001 << 10) | ||
69 | #define TSCR_LOCK (0x0001 << 9) | ||
70 | #define TSCR_IACK (0x0001 << 8) | ||
71 | #define TSCR_ADEF (0x007f << 0) | ||
72 | |||
73 | #define MISC_DVR (0x0fff << 4) | ||
74 | #define MISC_ALED (0x0001 << 3) | ||
75 | #define MISC_FRST (0x0001 << 2) | ||
76 | #define MISC_LED1 (0x0001 << 1) | ||
77 | #define MISC_LED0 (0x0001 << 0) | ||
78 | |||
79 | #define SPID_SPIDR (0x00ff << 0) | ||
80 | |||
81 | #define SLCS_SCL (0x0001 << 7) | ||
82 | #define SLCS_SDA (0x0001 << 6) | ||
83 | #define SLCS_CSN (0x0001 << 2) | ||
84 | #define SLCS_OVR (0x0001 << 1) | ||
85 | #define SLCS_SWC (0x0001 << 0) | ||
86 | |||
87 | #define TS_DMA_PACKETS (8) | ||
88 | #define TS_DMA_BYTES (188 * TS_DMA_PACKETS) | ||
89 | |||
90 | #define I2C_ADDR_TDA10046 0x10 | ||
91 | #define I2C_ADDR_TUA6034 0xc2 | ||
92 | #define NHWFILTERS 8 | ||
93 | |||
94 | struct pluto { | ||
95 | /* pci */ | ||
96 | struct pci_dev *pdev; | ||
97 | u8 __iomem *io_mem; | ||
98 | |||
99 | /* dvb */ | ||
100 | struct dmx_frontend hw_frontend; | ||
101 | struct dmx_frontend mem_frontend; | ||
102 | struct dmxdev dmxdev; | ||
103 | struct dvb_adapter dvb_adapter; | ||
104 | struct dvb_demux demux; | ||
105 | struct dvb_frontend *fe; | ||
106 | struct dvb_net dvbnet; | ||
107 | unsigned int full_ts_users; | ||
108 | unsigned int users; | ||
109 | |||
110 | /* i2c */ | ||
111 | struct i2c_algo_bit_data i2c_bit; | ||
112 | struct i2c_adapter i2c_adap; | ||
113 | unsigned int i2cbug; | ||
114 | |||
115 | /* irq */ | ||
116 | unsigned int overflow; | ||
117 | |||
118 | /* dma */ | ||
119 | dma_addr_t dma_addr; | ||
120 | u8 dma_buf[TS_DMA_BYTES]; | ||
121 | u8 dummy[4096]; | ||
122 | }; | ||
123 | |||
124 | static inline struct pluto *feed_to_pluto(struct dvb_demux_feed *feed) | ||
125 | { | ||
126 | return container_of(feed->demux, struct pluto, demux); | ||
127 | } | ||
128 | |||
129 | static inline struct pluto *frontend_to_pluto(struct dvb_frontend *fe) | ||
130 | { | ||
131 | return container_of(fe->dvb, struct pluto, dvb_adapter); | ||
132 | } | ||
133 | |||
134 | static inline u32 pluto_readreg(struct pluto *pluto, u32 reg) | ||
135 | { | ||
136 | return readl(&pluto->io_mem[reg]); | ||
137 | } | ||
138 | |||
139 | static inline void pluto_writereg(struct pluto *pluto, u32 reg, u32 val) | ||
140 | { | ||
141 | writel(val, &pluto->io_mem[reg]); | ||
142 | } | ||
143 | |||
144 | static inline void pluto_rw(struct pluto *pluto, u32 reg, u32 mask, u32 bits) | ||
145 | { | ||
146 | u32 val = readl(&pluto->io_mem[reg]); | ||
147 | val &= ~mask; | ||
148 | val |= bits; | ||
149 | writel(val, &pluto->io_mem[reg]); | ||
150 | } | ||
151 | |||
152 | static void pluto_setsda(void *data, int state) | ||
153 | { | ||
154 | struct pluto *pluto = data; | ||
155 | |||
156 | if (state) | ||
157 | pluto_rw(pluto, REG_SLCS, SLCS_SDA, SLCS_SDA); | ||
158 | else | ||
159 | pluto_rw(pluto, REG_SLCS, SLCS_SDA, 0); | ||
160 | } | ||
161 | |||
162 | static void pluto_setscl(void *data, int state) | ||
163 | { | ||
164 | struct pluto *pluto = data; | ||
165 | |||
166 | if (state) | ||
167 | pluto_rw(pluto, REG_SLCS, SLCS_SCL, SLCS_SCL); | ||
168 | else | ||
169 | pluto_rw(pluto, REG_SLCS, SLCS_SCL, 0); | ||
170 | |||
171 | /* try to detect i2c_inb() to workaround hardware bug: | ||
172 | * reset SDA to high after SCL has been set to low */ | ||
173 | if ((state) && (pluto->i2cbug == 0)) { | ||
174 | pluto->i2cbug = 1; | ||
175 | } else { | ||
176 | if ((!state) && (pluto->i2cbug == 1)) | ||
177 | pluto_setsda(pluto, 1); | ||
178 | pluto->i2cbug = 0; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | static int pluto_getsda(void *data) | ||
183 | { | ||
184 | struct pluto *pluto = data; | ||
185 | |||
186 | return pluto_readreg(pluto, REG_SLCS) & SLCS_SDA; | ||
187 | } | ||
188 | |||
189 | static int pluto_getscl(void *data) | ||
190 | { | ||
191 | struct pluto *pluto = data; | ||
192 | |||
193 | return pluto_readreg(pluto, REG_SLCS) & SLCS_SCL; | ||
194 | } | ||
195 | |||
196 | static void pluto_reset_frontend(struct pluto *pluto, int reenable) | ||
197 | { | ||
198 | u32 val = pluto_readreg(pluto, REG_MISC); | ||
199 | |||
200 | if (val & MISC_FRST) { | ||
201 | val &= ~MISC_FRST; | ||
202 | pluto_writereg(pluto, REG_MISC, val); | ||
203 | } | ||
204 | if (reenable) { | ||
205 | val |= MISC_FRST; | ||
206 | pluto_writereg(pluto, REG_MISC, val); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | static void pluto_reset_ts(struct pluto *pluto, int reenable) | ||
211 | { | ||
212 | u32 val = pluto_readreg(pluto, REG_TSCR); | ||
213 | |||
214 | if (val & TSCR_RSTN) { | ||
215 | val &= ~TSCR_RSTN; | ||
216 | pluto_writereg(pluto, REG_TSCR, val); | ||
217 | } | ||
218 | if (reenable) { | ||
219 | val |= TSCR_RSTN; | ||
220 | pluto_writereg(pluto, REG_TSCR, val); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | static void pluto_set_dma_addr(struct pluto *pluto) | ||
225 | { | ||
226 | pluto_writereg(pluto, REG_PCAR, cpu_to_le32(pluto->dma_addr)); | ||
227 | } | ||
228 | |||
229 | static int __devinit pluto_dma_map(struct pluto *pluto) | ||
230 | { | ||
231 | pluto->dma_addr = pci_map_single(pluto->pdev, pluto->dma_buf, | ||
232 | TS_DMA_BYTES, PCI_DMA_FROMDEVICE); | ||
233 | |||
234 | return pci_dma_mapping_error(pluto->dma_addr); | ||
235 | } | ||
236 | |||
237 | static void pluto_dma_unmap(struct pluto *pluto) | ||
238 | { | ||
239 | pci_unmap_single(pluto->pdev, pluto->dma_addr, | ||
240 | TS_DMA_BYTES, PCI_DMA_FROMDEVICE); | ||
241 | } | ||
242 | |||
243 | static int pluto_start_feed(struct dvb_demux_feed *f) | ||
244 | { | ||
245 | struct pluto *pluto = feed_to_pluto(f); | ||
246 | |||
247 | /* enable PID filtering */ | ||
248 | if (pluto->users++ == 0) | ||
249 | pluto_rw(pluto, REG_PIDn(0), PID0_AFIL | PID0_NOFIL, 0); | ||
250 | |||
251 | if ((f->pid < 0x2000) && (f->index < NHWFILTERS)) | ||
252 | pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, PIDn_ENP | f->pid); | ||
253 | else if (pluto->full_ts_users++ == 0) | ||
254 | pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, PID0_NOFIL); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static int pluto_stop_feed(struct dvb_demux_feed *f) | ||
260 | { | ||
261 | struct pluto *pluto = feed_to_pluto(f); | ||
262 | |||
263 | /* disable PID filtering */ | ||
264 | if (--pluto->users == 0) | ||
265 | pluto_rw(pluto, REG_PIDn(0), PID0_AFIL, PID0_AFIL); | ||
266 | |||
267 | if ((f->pid < 0x2000) && (f->index < NHWFILTERS)) | ||
268 | pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, 0x1fff); | ||
269 | else if (--pluto->full_ts_users == 0) | ||
270 | pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, 0); | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static void pluto_dma_end(struct pluto *pluto, unsigned int nbpackets) | ||
276 | { | ||
277 | /* synchronize the DMA transfer with the CPU | ||
278 | * first so that we see updated contents. */ | ||
279 | pci_dma_sync_single_for_cpu(pluto->pdev, pluto->dma_addr, | ||
280 | TS_DMA_BYTES, PCI_DMA_FROMDEVICE); | ||
281 | |||
282 | /* Workaround for broken hardware: | ||
283 | * [1] On startup NBPACKETS seems to contain an uninitialized value, | ||
284 | * but no packets have been transfered. | ||
285 | * [2] Sometimes (actually very often) NBPACKETS stays at zero | ||
286 | * although one packet has been transfered. | ||
287 | */ | ||
288 | if ((nbpackets == 0) || (nbpackets > TS_DMA_PACKETS)) { | ||
289 | unsigned int i = 0, valid; | ||
290 | while (pluto->dma_buf[i] == 0x47) | ||
291 | i += 188; | ||
292 | valid = i / 188; | ||
293 | if (nbpackets != valid) { | ||
294 | dev_err(&pluto->pdev->dev, "nbpackets=%u valid=%u\n", | ||
295 | nbpackets, valid); | ||
296 | nbpackets = valid; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | dvb_dmx_swfilter_packets(&pluto->demux, pluto->dma_buf, nbpackets); | ||
301 | |||
302 | /* clear the dma buffer. this is needed to be able to identify | ||
303 | * new valid ts packets above */ | ||
304 | memset(pluto->dma_buf, 0, nbpackets * 188); | ||
305 | |||
306 | /* reset the dma address */ | ||
307 | pluto_set_dma_addr(pluto); | ||
308 | |||
309 | /* sync the buffer and give it back to the card */ | ||
310 | pci_dma_sync_single_for_device(pluto->pdev, pluto->dma_addr, | ||
311 | TS_DMA_BYTES, PCI_DMA_FROMDEVICE); | ||
312 | } | ||
313 | |||
314 | static irqreturn_t pluto_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
315 | { | ||
316 | struct pluto *pluto = dev_id; | ||
317 | u32 tscr; | ||
318 | |||
319 | /* check whether an interrupt occured on this device */ | ||
320 | tscr = pluto_readreg(pluto, REG_TSCR); | ||
321 | if (!(tscr & (TSCR_DE | TSCR_OVR))) | ||
322 | return IRQ_NONE; | ||
323 | |||
324 | if (tscr == 0xffffffff) { | ||
325 | // FIXME: maybe recover somehow | ||
326 | dev_err(&pluto->pdev->dev, "card hung up :(\n"); | ||
327 | return IRQ_HANDLED; | ||
328 | } | ||
329 | |||
330 | /* dma end interrupt */ | ||
331 | if (tscr & TSCR_DE) { | ||
332 | pluto_dma_end(pluto, (tscr & TSCR_NBPACKETS) >> 24); | ||
333 | /* overflow interrupt */ | ||
334 | if (tscr & TSCR_OVR) | ||
335 | pluto->overflow++; | ||
336 | if (pluto->overflow) { | ||
337 | dev_err(&pluto->pdev->dev, "overflow irq (%d)\n", | ||
338 | pluto->overflow); | ||
339 | pluto_reset_ts(pluto, 1); | ||
340 | pluto->overflow = 0; | ||
341 | } | ||
342 | } else if (tscr & TSCR_OVR) { | ||
343 | pluto->overflow++; | ||
344 | } | ||
345 | |||
346 | /* ACK the interrupt */ | ||
347 | pluto_writereg(pluto, REG_TSCR, tscr | TSCR_IACK); | ||
348 | |||
349 | return IRQ_HANDLED; | ||
350 | } | ||
351 | |||
352 | static void __devinit pluto_enable_irqs(struct pluto *pluto) | ||
353 | { | ||
354 | u32 val = pluto_readreg(pluto, REG_TSCR); | ||
355 | |||
356 | /* set the number of packets */ | ||
357 | val &= ~TSCR_ADEF; | ||
358 | val |= TS_DMA_PACKETS / 2; | ||
359 | /* disable AFUL and LOCK interrupts */ | ||
360 | val |= (TSCR_MSKA | TSCR_MSKL); | ||
361 | /* enable DMA and OVERFLOW interrupts */ | ||
362 | val &= ~(TSCR_DEM | TSCR_MSKO); | ||
363 | /* clear pending interrupts */ | ||
364 | val |= TSCR_IACK; | ||
365 | |||
366 | pluto_writereg(pluto, REG_TSCR, val); | ||
367 | } | ||
368 | |||
369 | static void pluto_disable_irqs(struct pluto *pluto) | ||
370 | { | ||
371 | u32 val = pluto_readreg(pluto, REG_TSCR); | ||
372 | |||
373 | /* disable all interrupts */ | ||
374 | val |= (TSCR_DEM | TSCR_MSKO | TSCR_MSKA | TSCR_MSKL); | ||
375 | /* clear pending interrupts */ | ||
376 | val |= TSCR_IACK; | ||
377 | |||
378 | pluto_writereg(pluto, REG_TSCR, val); | ||
379 | } | ||
380 | |||
381 | static int __devinit pluto_hw_init(struct pluto *pluto) | ||
382 | { | ||
383 | pluto_reset_frontend(pluto, 1); | ||
384 | |||
385 | /* set automatic LED control by FPGA */ | ||
386 | pluto_rw(pluto, REG_MISC, MISC_ALED, MISC_ALED); | ||
387 | |||
388 | /* set data endianess */ | ||
389 | #ifdef __LITTLE_ENDIAN | ||
390 | pluto_rw(pluto, REG_PIDn(0), PID0_END, PID0_END); | ||
391 | #else | ||
392 | pluto_rw(pluto, REG_PIDn(0), PID0_END, 0); | ||
393 | #endif | ||
394 | /* map DMA and set address */ | ||
395 | pluto_dma_map(pluto); | ||
396 | pluto_set_dma_addr(pluto); | ||
397 | |||
398 | /* enable interrupts */ | ||
399 | pluto_enable_irqs(pluto); | ||
400 | |||
401 | /* reset TS logic */ | ||
402 | pluto_reset_ts(pluto, 1); | ||
403 | |||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | static void pluto_hw_exit(struct pluto *pluto) | ||
408 | { | ||
409 | /* disable interrupts */ | ||
410 | pluto_disable_irqs(pluto); | ||
411 | |||
412 | pluto_reset_ts(pluto, 0); | ||
413 | |||
414 | /* LED: disable automatic control, enable yellow, disable green */ | ||
415 | pluto_rw(pluto, REG_MISC, MISC_ALED | MISC_LED1 | MISC_LED0, MISC_LED1); | ||
416 | |||
417 | /* unmap DMA */ | ||
418 | pluto_dma_unmap(pluto); | ||
419 | |||
420 | pluto_reset_frontend(pluto, 0); | ||
421 | } | ||
422 | |||
423 | static inline u32 divide(u32 numerator, u32 denominator) | ||
424 | { | ||
425 | if (denominator == 0) | ||
426 | return ~0; | ||
427 | |||
428 | return (numerator + denominator / 2) / denominator; | ||
429 | } | ||
430 | |||
431 | /* LG Innotek TDTE-E001P (Infineon TUA6034) */ | ||
432 | static int lg_tdtpe001p_pll_set(struct dvb_frontend *fe, | ||
433 | struct dvb_frontend_parameters *p) | ||
434 | { | ||
435 | struct pluto *pluto = frontend_to_pluto(fe); | ||
436 | struct i2c_msg msg; | ||
437 | int ret; | ||
438 | u8 buf[4]; | ||
439 | u32 div; | ||
440 | |||
441 | // Fref = 166.667 Hz | ||
442 | // Fref * 3 = 500.000 Hz | ||
443 | // IF = 36166667 | ||
444 | // IF / Fref = 217 | ||
445 | //div = divide(p->frequency + 36166667, 166667); | ||
446 | div = divide(p->frequency * 3, 500000) + 217; | ||
447 | buf[0] = (div >> 8) & 0x7f; | ||
448 | buf[1] = (div >> 0) & 0xff; | ||
449 | |||
450 | if (p->frequency < 611000000) | ||
451 | buf[2] = 0xb4; | ||
452 | else if (p->frequency < 811000000) | ||
453 | buf[2] = 0xbc; | ||
454 | else | ||
455 | buf[2] = 0xf4; | ||
456 | |||
457 | // VHF: 174-230 MHz | ||
458 | // center: 350 MHz | ||
459 | // UHF: 470-862 MHz | ||
460 | if (p->frequency < 350000000) | ||
461 | buf[3] = 0x02; | ||
462 | else | ||
463 | buf[3] = 0x04; | ||
464 | |||
465 | if (p->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) | ||
466 | buf[3] |= 0x08; | ||
467 | |||
468 | if (sizeof(buf) == 6) { | ||
469 | buf[4] = buf[2]; | ||
470 | buf[4] &= ~0x1c; | ||
471 | buf[4] |= 0x18; | ||
472 | |||
473 | buf[5] = (0 << 7) | (2 << 4); | ||
474 | } | ||
475 | |||
476 | msg.addr = I2C_ADDR_TUA6034 >> 1; | ||
477 | msg.flags = 0; | ||
478 | msg.buf = buf; | ||
479 | msg.len = sizeof(buf); | ||
480 | |||
481 | ret = i2c_transfer(&pluto->i2c_adap, &msg, 1); | ||
482 | if (ret < 0) | ||
483 | return ret; | ||
484 | else if (ret == 0) | ||
485 | return -EREMOTEIO; | ||
486 | |||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | static int pluto2_request_firmware(struct dvb_frontend *fe, | ||
491 | const struct firmware **fw, char *name) | ||
492 | { | ||
493 | struct pluto *pluto = frontend_to_pluto(fe); | ||
494 | |||
495 | return request_firmware(fw, name, &pluto->pdev->dev); | ||
496 | } | ||
497 | |||
498 | static struct tda1004x_config pluto2_fe_config __devinitdata = { | ||
499 | .demod_address = I2C_ADDR_TDA10046 >> 1, | ||
500 | .invert = 1, | ||
501 | .invert_oclk = 0, | ||
502 | .xtal_freq = TDA10046_XTAL_16M, | ||
503 | .agc_config = TDA10046_AGC_DEFAULT, | ||
504 | .if_freq = TDA10046_FREQ_3617, | ||
505 | .pll_set = lg_tdtpe001p_pll_set, | ||
506 | .pll_sleep = NULL, | ||
507 | .request_firmware = pluto2_request_firmware, | ||
508 | }; | ||
509 | |||
510 | static int __devinit frontend_init(struct pluto *pluto) | ||
511 | { | ||
512 | int ret; | ||
513 | |||
514 | pluto->fe = tda10046_attach(&pluto2_fe_config, &pluto->i2c_adap); | ||
515 | if (!pluto->fe) { | ||
516 | dev_err(&pluto->pdev->dev, "could not attach frontend\n"); | ||
517 | return -ENODEV; | ||
518 | } | ||
519 | |||
520 | ret = dvb_register_frontend(&pluto->dvb_adapter, pluto->fe); | ||
521 | if (ret < 0) { | ||
522 | if (pluto->fe->ops->release) | ||
523 | pluto->fe->ops->release(pluto->fe); | ||
524 | return ret; | ||
525 | } | ||
526 | |||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | static void __devinit pluto_read_rev(struct pluto *pluto) | ||
531 | { | ||
532 | u32 val = pluto_readreg(pluto, REG_MISC) & MISC_DVR; | ||
533 | dev_info(&pluto->pdev->dev, "board revision %d.%d\n", | ||
534 | (val >> 12) & 0x0f, (val >> 4) & 0xff); | ||
535 | } | ||
536 | |||
537 | static void __devinit pluto_read_mac(struct pluto *pluto, u8 *mac) | ||
538 | { | ||
539 | u32 val = pluto_readreg(pluto, REG_MMAC); | ||
540 | mac[0] = (val >> 8) & 0xff; | ||
541 | mac[1] = (val >> 0) & 0xff; | ||
542 | |||
543 | val = pluto_readreg(pluto, REG_IMAC); | ||
544 | mac[2] = (val >> 8) & 0xff; | ||
545 | mac[3] = (val >> 0) & 0xff; | ||
546 | |||
547 | val = pluto_readreg(pluto, REG_LMAC); | ||
548 | mac[4] = (val >> 8) & 0xff; | ||
549 | mac[5] = (val >> 0) & 0xff; | ||
550 | |||
551 | dev_info(&pluto->pdev->dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
552 | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); | ||
553 | } | ||
554 | |||
555 | static int __devinit pluto_read_serial(struct pluto *pluto) | ||
556 | { | ||
557 | struct pci_dev *pdev = pluto->pdev; | ||
558 | unsigned int i, j; | ||
559 | u8 __iomem *cis; | ||
560 | |||
561 | cis = pci_iomap(pdev, 1, 0); | ||
562 | if (!cis) | ||
563 | return -EIO; | ||
564 | |||
565 | dev_info(&pdev->dev, "S/N "); | ||
566 | |||
567 | for (i = 0xe0; i < 0x100; i += 4) { | ||
568 | u32 val = readl(&cis[i]); | ||
569 | for (j = 0; j < 32; j += 8) { | ||
570 | if ((val & 0xff) == 0xff) | ||
571 | goto out; | ||
572 | printk("%c", val & 0xff); | ||
573 | val >>= 8; | ||
574 | } | ||
575 | } | ||
576 | out: | ||
577 | printk("\n"); | ||
578 | pci_iounmap(pdev, cis); | ||
579 | |||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static int __devinit pluto2_probe(struct pci_dev *pdev, | ||
584 | const struct pci_device_id *ent) | ||
585 | { | ||
586 | struct pluto *pluto; | ||
587 | struct dvb_adapter *dvb_adapter; | ||
588 | struct dvb_demux *dvbdemux; | ||
589 | struct dmx_demux *dmx; | ||
590 | int ret = -ENOMEM; | ||
591 | |||
592 | pluto = kmalloc(sizeof(struct pluto), GFP_KERNEL); | ||
593 | if (!pluto) | ||
594 | goto out; | ||
595 | |||
596 | memset(pluto, 0, sizeof(struct pluto)); | ||
597 | pluto->pdev = pdev; | ||
598 | |||
599 | ret = pci_enable_device(pdev); | ||
600 | if (ret < 0) | ||
601 | goto err_kfree; | ||
602 | |||
603 | /* enable interrupts */ | ||
604 | pci_write_config_dword(pdev, 0x6c, 0x8000); | ||
605 | |||
606 | ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK); | ||
607 | if (ret < 0) | ||
608 | goto err_pci_disable_device; | ||
609 | |||
610 | pci_set_master(pdev); | ||
611 | |||
612 | ret = pci_request_regions(pdev, DRIVER_NAME); | ||
613 | if (ret < 0) | ||
614 | goto err_pci_disable_device; | ||
615 | |||
616 | pluto->io_mem = pci_iomap(pdev, 0, 0x40); | ||
617 | if (!pluto->io_mem) { | ||
618 | ret = -EIO; | ||
619 | goto err_pci_release_regions; | ||
620 | } | ||
621 | |||
622 | pci_set_drvdata(pdev, pluto); | ||
623 | |||
624 | ret = request_irq(pdev->irq, pluto_irq, SA_SHIRQ, DRIVER_NAME, pluto); | ||
625 | if (ret < 0) | ||
626 | goto err_pci_iounmap; | ||
627 | |||
628 | ret = pluto_hw_init(pluto); | ||
629 | if (ret < 0) | ||
630 | goto err_free_irq; | ||
631 | |||
632 | /* i2c */ | ||
633 | i2c_set_adapdata(&pluto->i2c_adap, pluto); | ||
634 | strcpy(pluto->i2c_adap.name, DRIVER_NAME); | ||
635 | pluto->i2c_adap.owner = THIS_MODULE; | ||
636 | pluto->i2c_adap.id = I2C_ALGO_BIT; | ||
637 | pluto->i2c_adap.class = I2C_CLASS_TV_DIGITAL; | ||
638 | pluto->i2c_adap.dev.parent = &pdev->dev; | ||
639 | pluto->i2c_adap.algo_data = &pluto->i2c_bit; | ||
640 | pluto->i2c_bit.data = pluto; | ||
641 | pluto->i2c_bit.setsda = pluto_setsda; | ||
642 | pluto->i2c_bit.setscl = pluto_setscl; | ||
643 | pluto->i2c_bit.getsda = pluto_getsda; | ||
644 | pluto->i2c_bit.getscl = pluto_getscl; | ||
645 | pluto->i2c_bit.udelay = 10; | ||
646 | pluto->i2c_bit.timeout = 10; | ||
647 | |||
648 | /* Raise SCL and SDA */ | ||
649 | pluto_setsda(pluto, 1); | ||
650 | pluto_setscl(pluto, 1); | ||
651 | |||
652 | ret = i2c_bit_add_bus(&pluto->i2c_adap); | ||
653 | if (ret < 0) | ||
654 | goto err_pluto_hw_exit; | ||
655 | |||
656 | /* dvb */ | ||
657 | ret = dvb_register_adapter(&pluto->dvb_adapter, DRIVER_NAME, THIS_MODULE); | ||
658 | if (ret < 0) | ||
659 | goto err_i2c_bit_del_bus; | ||
660 | |||
661 | dvb_adapter = &pluto->dvb_adapter; | ||
662 | |||
663 | pluto_read_rev(pluto); | ||
664 | pluto_read_serial(pluto); | ||
665 | pluto_read_mac(pluto, dvb_adapter->proposed_mac); | ||
666 | |||
667 | dvbdemux = &pluto->demux; | ||
668 | dvbdemux->filternum = 256; | ||
669 | dvbdemux->feednum = 256; | ||
670 | dvbdemux->start_feed = pluto_start_feed; | ||
671 | dvbdemux->stop_feed = pluto_stop_feed; | ||
672 | dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | | ||
673 | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); | ||
674 | ret = dvb_dmx_init(dvbdemux); | ||
675 | if (ret < 0) | ||
676 | goto err_dvb_unregister_adapter; | ||
677 | |||
678 | dmx = &dvbdemux->dmx; | ||
679 | |||
680 | pluto->hw_frontend.source = DMX_FRONTEND_0; | ||
681 | pluto->mem_frontend.source = DMX_MEMORY_FE; | ||
682 | pluto->dmxdev.filternum = NHWFILTERS; | ||
683 | pluto->dmxdev.demux = dmx; | ||
684 | |||
685 | ret = dvb_dmxdev_init(&pluto->dmxdev, dvb_adapter); | ||
686 | if (ret < 0) | ||
687 | goto err_dvb_dmx_release; | ||
688 | |||
689 | ret = dmx->add_frontend(dmx, &pluto->hw_frontend); | ||
690 | if (ret < 0) | ||
691 | goto err_dvb_dmxdev_release; | ||
692 | |||
693 | ret = dmx->add_frontend(dmx, &pluto->mem_frontend); | ||
694 | if (ret < 0) | ||
695 | goto err_remove_hw_frontend; | ||
696 | |||
697 | ret = dmx->connect_frontend(dmx, &pluto->hw_frontend); | ||
698 | if (ret < 0) | ||
699 | goto err_remove_mem_frontend; | ||
700 | |||
701 | ret = frontend_init(pluto); | ||
702 | if (ret < 0) | ||
703 | goto err_disconnect_frontend; | ||
704 | |||
705 | dvb_net_init(dvb_adapter, &pluto->dvbnet, dmx); | ||
706 | out: | ||
707 | return ret; | ||
708 | |||
709 | err_disconnect_frontend: | ||
710 | dmx->disconnect_frontend(dmx); | ||
711 | err_remove_mem_frontend: | ||
712 | dmx->remove_frontend(dmx, &pluto->mem_frontend); | ||
713 | err_remove_hw_frontend: | ||
714 | dmx->remove_frontend(dmx, &pluto->hw_frontend); | ||
715 | err_dvb_dmxdev_release: | ||
716 | dvb_dmxdev_release(&pluto->dmxdev); | ||
717 | err_dvb_dmx_release: | ||
718 | dvb_dmx_release(dvbdemux); | ||
719 | err_dvb_unregister_adapter: | ||
720 | dvb_unregister_adapter(dvb_adapter); | ||
721 | err_i2c_bit_del_bus: | ||
722 | i2c_bit_del_bus(&pluto->i2c_adap); | ||
723 | err_pluto_hw_exit: | ||
724 | pluto_hw_exit(pluto); | ||
725 | err_free_irq: | ||
726 | free_irq(pdev->irq, pluto); | ||
727 | err_pci_iounmap: | ||
728 | pci_iounmap(pdev, pluto->io_mem); | ||
729 | err_pci_release_regions: | ||
730 | pci_release_regions(pdev); | ||
731 | err_pci_disable_device: | ||
732 | pci_disable_device(pdev); | ||
733 | err_kfree: | ||
734 | pci_set_drvdata(pdev, NULL); | ||
735 | kfree(pluto); | ||
736 | goto out; | ||
737 | } | ||
738 | |||
739 | static void __devexit pluto2_remove(struct pci_dev *pdev) | ||
740 | { | ||
741 | struct pluto *pluto = pci_get_drvdata(pdev); | ||
742 | struct dvb_adapter *dvb_adapter = &pluto->dvb_adapter; | ||
743 | struct dvb_demux *dvbdemux = &pluto->demux; | ||
744 | struct dmx_demux *dmx = &dvbdemux->dmx; | ||
745 | |||
746 | dmx->close(dmx); | ||
747 | dvb_net_release(&pluto->dvbnet); | ||
748 | if (pluto->fe) | ||
749 | dvb_unregister_frontend(pluto->fe); | ||
750 | |||
751 | dmx->disconnect_frontend(dmx); | ||
752 | dmx->remove_frontend(dmx, &pluto->mem_frontend); | ||
753 | dmx->remove_frontend(dmx, &pluto->hw_frontend); | ||
754 | dvb_dmxdev_release(&pluto->dmxdev); | ||
755 | dvb_dmx_release(dvbdemux); | ||
756 | dvb_unregister_adapter(dvb_adapter); | ||
757 | i2c_bit_del_bus(&pluto->i2c_adap); | ||
758 | pluto_hw_exit(pluto); | ||
759 | free_irq(pdev->irq, pluto); | ||
760 | pci_iounmap(pdev, pluto->io_mem); | ||
761 | pci_release_regions(pdev); | ||
762 | pci_disable_device(pdev); | ||
763 | pci_set_drvdata(pdev, NULL); | ||
764 | kfree(pluto); | ||
765 | } | ||
766 | |||
767 | #ifndef PCI_VENDOR_ID_SCM | ||
768 | #define PCI_VENDOR_ID_SCM 0x0432 | ||
769 | #endif | ||
770 | #ifndef PCI_DEVICE_ID_PLUTO2 | ||
771 | #define PCI_DEVICE_ID_PLUTO2 0x0001 | ||
772 | #endif | ||
773 | |||
774 | static struct pci_device_id pluto2_id_table[] __devinitdata = { | ||
775 | { | ||
776 | .vendor = PCI_VENDOR_ID_SCM, | ||
777 | .device = PCI_DEVICE_ID_PLUTO2, | ||
778 | .subvendor = PCI_ANY_ID, | ||
779 | .subdevice = PCI_ANY_ID, | ||
780 | }, { | ||
781 | /* empty */ | ||
782 | }, | ||
783 | }; | ||
784 | |||
785 | MODULE_DEVICE_TABLE(pci, pluto2_id_table); | ||
786 | |||
787 | static struct pci_driver pluto2_driver = { | ||
788 | .name = DRIVER_NAME, | ||
789 | .id_table = pluto2_id_table, | ||
790 | .probe = pluto2_probe, | ||
791 | .remove = __devexit_p(pluto2_remove), | ||
792 | }; | ||
793 | |||
794 | static int __init pluto2_init(void) | ||
795 | { | ||
796 | return pci_register_driver(&pluto2_driver); | ||
797 | } | ||
798 | |||
799 | static void __exit pluto2_exit(void) | ||
800 | { | ||
801 | pci_unregister_driver(&pluto2_driver); | ||
802 | } | ||
803 | |||
804 | module_init(pluto2_init); | ||
805 | module_exit(pluto2_exit); | ||
806 | |||
807 | MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>"); | ||
808 | MODULE_DESCRIPTION("Pluto2 driver"); | ||
809 | MODULE_LICENSE("GPL"); | ||