1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
3*4882a593Smuzhiyun * flexcop.c - main module part
4*4882a593Smuzhiyun * Copyright (C) 2004-9 Patrick Boettcher <patrick.boettcher@posteo.de>
5*4882a593Smuzhiyun * based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Acknowledgements:
8*4882a593Smuzhiyun * John Jurrius from BBTI, Inc. for extensive support
9*4882a593Smuzhiyun * with code examples and data books
10*4882a593Smuzhiyun * Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting)
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * Contributions to the skystar2-driver have been done by
13*4882a593Smuzhiyun * Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes)
14*4882a593Smuzhiyun * Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code)
15*4882a593Smuzhiyun * Uwe Bugla, uwe.bugla at gmx.de (doing tests, restyling code, writing docu)
16*4882a593Smuzhiyun * Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac
17*4882a593Smuzhiyun * filtering)
18*4882a593Smuzhiyun *
19*4882a593Smuzhiyun * This program is free software; you can redistribute it and/or
20*4882a593Smuzhiyun * modify it under the terms of the GNU Lesser General Public License
21*4882a593Smuzhiyun * as published by the Free Software Foundation; either version 2.1
22*4882a593Smuzhiyun * of the License, or (at your option) any later version.
23*4882a593Smuzhiyun *
24*4882a593Smuzhiyun * This program is distributed in the hope that it will be useful,
25*4882a593Smuzhiyun * but WITHOUT ANY WARRANTY; without even the implied warranty of
26*4882a593Smuzhiyun * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27*4882a593Smuzhiyun * GNU General Public License for more details.
28*4882a593Smuzhiyun */
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #include "flexcop.h"
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun #define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip"
33*4882a593Smuzhiyun #define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@posteo.de"
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
36*4882a593Smuzhiyun #define DEBSTATUS ""
37*4882a593Smuzhiyun #else
38*4882a593Smuzhiyun #define DEBSTATUS " (debugging is not enabled)"
39*4882a593Smuzhiyun #endif
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun int b2c2_flexcop_debug;
42*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(b2c2_flexcop_debug);
43*4882a593Smuzhiyun module_param_named(debug, b2c2_flexcop_debug, int, 0644);
44*4882a593Smuzhiyun MODULE_PARM_DESC(debug,
45*4882a593Smuzhiyun "set debug level (1=info,2=tuner,4=i2c,8=ts,16=sram,32=reg,64=i2cdump (|-able))."
46*4882a593Smuzhiyun DEBSTATUS);
47*4882a593Smuzhiyun #undef DEBSTATUS
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun /* global zero for ibi values */
52*4882a593Smuzhiyun flexcop_ibi_value ibi_zero;
53*4882a593Smuzhiyun
flexcop_dvb_start_feed(struct dvb_demux_feed * dvbdmxfeed)54*4882a593Smuzhiyun static int flexcop_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun struct flexcop_device *fc = dvbdmxfeed->demux->priv;
57*4882a593Smuzhiyun return flexcop_pid_feed_control(fc, dvbdmxfeed, 1);
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun
flexcop_dvb_stop_feed(struct dvb_demux_feed * dvbdmxfeed)60*4882a593Smuzhiyun static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun struct flexcop_device *fc = dvbdmxfeed->demux->priv;
63*4882a593Smuzhiyun return flexcop_pid_feed_control(fc, dvbdmxfeed, 0);
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
flexcop_dvb_init(struct flexcop_device * fc)66*4882a593Smuzhiyun static int flexcop_dvb_init(struct flexcop_device *fc)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun int ret = dvb_register_adapter(&fc->dvb_adapter,
69*4882a593Smuzhiyun "FlexCop Digital TV device", fc->owner,
70*4882a593Smuzhiyun fc->dev, adapter_nr);
71*4882a593Smuzhiyun if (ret < 0) {
72*4882a593Smuzhiyun err("error registering DVB adapter");
73*4882a593Smuzhiyun return ret;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun fc->dvb_adapter.priv = fc;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING
78*4882a593Smuzhiyun | DMX_MEMORY_BASED_FILTERING);
79*4882a593Smuzhiyun fc->demux.priv = fc;
80*4882a593Smuzhiyun fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED;
81*4882a593Smuzhiyun fc->demux.start_feed = flexcop_dvb_start_feed;
82*4882a593Smuzhiyun fc->demux.stop_feed = flexcop_dvb_stop_feed;
83*4882a593Smuzhiyun fc->demux.write_to_decoder = NULL;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun ret = dvb_dmx_init(&fc->demux);
86*4882a593Smuzhiyun if (ret < 0) {
87*4882a593Smuzhiyun err("dvb_dmx failed: error %d", ret);
88*4882a593Smuzhiyun goto err_dmx;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun fc->hw_frontend.source = DMX_FRONTEND_0;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun fc->dmxdev.filternum = fc->demux.feednum;
94*4882a593Smuzhiyun fc->dmxdev.demux = &fc->demux.dmx;
95*4882a593Smuzhiyun fc->dmxdev.capabilities = 0;
96*4882a593Smuzhiyun ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter);
97*4882a593Smuzhiyun if (ret < 0) {
98*4882a593Smuzhiyun err("dvb_dmxdev_init failed: error %d", ret);
99*4882a593Smuzhiyun goto err_dmx_dev;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend);
103*4882a593Smuzhiyun if (ret < 0) {
104*4882a593Smuzhiyun err("adding hw_frontend to dmx failed: error %d", ret);
105*4882a593Smuzhiyun goto err_dmx_add_hw_frontend;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun fc->mem_frontend.source = DMX_MEMORY_FE;
109*4882a593Smuzhiyun ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend);
110*4882a593Smuzhiyun if (ret < 0) {
111*4882a593Smuzhiyun err("adding mem_frontend to dmx failed: error %d", ret);
112*4882a593Smuzhiyun goto err_dmx_add_mem_frontend;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend);
116*4882a593Smuzhiyun if (ret < 0) {
117*4882a593Smuzhiyun err("connect frontend failed: error %d", ret);
118*4882a593Smuzhiyun goto err_connect_frontend;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun ret = dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx);
122*4882a593Smuzhiyun if (ret < 0) {
123*4882a593Smuzhiyun err("dvb_net_init failed: error %d", ret);
124*4882a593Smuzhiyun goto err_net;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun fc->init_state |= FC_STATE_DVB_INIT;
128*4882a593Smuzhiyun return 0;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun err_net:
131*4882a593Smuzhiyun fc->demux.dmx.disconnect_frontend(&fc->demux.dmx);
132*4882a593Smuzhiyun err_connect_frontend:
133*4882a593Smuzhiyun fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->mem_frontend);
134*4882a593Smuzhiyun err_dmx_add_mem_frontend:
135*4882a593Smuzhiyun fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->hw_frontend);
136*4882a593Smuzhiyun err_dmx_add_hw_frontend:
137*4882a593Smuzhiyun dvb_dmxdev_release(&fc->dmxdev);
138*4882a593Smuzhiyun err_dmx_dev:
139*4882a593Smuzhiyun dvb_dmx_release(&fc->demux);
140*4882a593Smuzhiyun err_dmx:
141*4882a593Smuzhiyun dvb_unregister_adapter(&fc->dvb_adapter);
142*4882a593Smuzhiyun return ret;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
flexcop_dvb_exit(struct flexcop_device * fc)145*4882a593Smuzhiyun static void flexcop_dvb_exit(struct flexcop_device *fc)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun if (fc->init_state & FC_STATE_DVB_INIT) {
148*4882a593Smuzhiyun dvb_net_release(&fc->dvbnet);
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun fc->demux.dmx.close(&fc->demux.dmx);
151*4882a593Smuzhiyun fc->demux.dmx.remove_frontend(&fc->demux.dmx,
152*4882a593Smuzhiyun &fc->mem_frontend);
153*4882a593Smuzhiyun fc->demux.dmx.remove_frontend(&fc->demux.dmx,
154*4882a593Smuzhiyun &fc->hw_frontend);
155*4882a593Smuzhiyun dvb_dmxdev_release(&fc->dmxdev);
156*4882a593Smuzhiyun dvb_dmx_release(&fc->demux);
157*4882a593Smuzhiyun dvb_unregister_adapter(&fc->dvb_adapter);
158*4882a593Smuzhiyun deb_info("deinitialized dvb stuff\n");
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun fc->init_state &= ~FC_STATE_DVB_INIT;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun /* these methods are necessary to achieve the long-term-goal of hiding the
164*4882a593Smuzhiyun * struct flexcop_device from the bus-parts */
flexcop_pass_dmx_data(struct flexcop_device * fc,u8 * buf,u32 len)165*4882a593Smuzhiyun void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun dvb_dmx_swfilter(&fc->demux, buf, len);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun EXPORT_SYMBOL(flexcop_pass_dmx_data);
170*4882a593Smuzhiyun
flexcop_pass_dmx_packets(struct flexcop_device * fc,u8 * buf,u32 no)171*4882a593Smuzhiyun void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun dvb_dmx_swfilter_packets(&fc->demux, buf, no);
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun EXPORT_SYMBOL(flexcop_pass_dmx_packets);
176*4882a593Smuzhiyun
flexcop_reset(struct flexcop_device * fc)177*4882a593Smuzhiyun static void flexcop_reset(struct flexcop_device *fc)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun flexcop_ibi_value v210, v204;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun /* reset the flexcop itself */
182*4882a593Smuzhiyun fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun v210.raw = 0;
185*4882a593Smuzhiyun v210.sw_reset_210.reset_block_000 = 1;
186*4882a593Smuzhiyun v210.sw_reset_210.reset_block_100 = 1;
187*4882a593Smuzhiyun v210.sw_reset_210.reset_block_200 = 1;
188*4882a593Smuzhiyun v210.sw_reset_210.reset_block_300 = 1;
189*4882a593Smuzhiyun v210.sw_reset_210.reset_block_400 = 1;
190*4882a593Smuzhiyun v210.sw_reset_210.reset_block_500 = 1;
191*4882a593Smuzhiyun v210.sw_reset_210.reset_block_600 = 1;
192*4882a593Smuzhiyun v210.sw_reset_210.reset_block_700 = 1;
193*4882a593Smuzhiyun v210.sw_reset_210.Block_reset_enable = 0xb2;
194*4882a593Smuzhiyun v210.sw_reset_210.Special_controls = 0xc259;
195*4882a593Smuzhiyun fc->write_ibi_reg(fc,sw_reset_210,v210);
196*4882a593Smuzhiyun msleep(1);
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun /* reset the periphical devices */
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun v204 = fc->read_ibi_reg(fc,misc_204);
201*4882a593Smuzhiyun v204.misc_204.Per_reset_sig = 0;
202*4882a593Smuzhiyun fc->write_ibi_reg(fc,misc_204,v204);
203*4882a593Smuzhiyun msleep(1);
204*4882a593Smuzhiyun v204.misc_204.Per_reset_sig = 1;
205*4882a593Smuzhiyun fc->write_ibi_reg(fc,misc_204,v204);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
flexcop_reset_block_300(struct flexcop_device * fc)208*4882a593Smuzhiyun void flexcop_reset_block_300(struct flexcop_device *fc)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun flexcop_ibi_value v208_save = fc->read_ibi_reg(fc, ctrl_208),
211*4882a593Smuzhiyun v210 = fc->read_ibi_reg(fc, sw_reset_210);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun deb_rdump("208: %08x, 210: %08x\n", v208_save.raw, v210.raw);
214*4882a593Smuzhiyun fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun v210.sw_reset_210.reset_block_300 = 1;
217*4882a593Smuzhiyun v210.sw_reset_210.Block_reset_enable = 0xb2;
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun fc->write_ibi_reg(fc,sw_reset_210,v210);
220*4882a593Smuzhiyun fc->write_ibi_reg(fc,ctrl_208,v208_save);
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
flexcop_device_kmalloc(size_t bus_specific_len)223*4882a593Smuzhiyun struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun void *bus;
226*4882a593Smuzhiyun struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device),
227*4882a593Smuzhiyun GFP_KERNEL);
228*4882a593Smuzhiyun if (!fc) {
229*4882a593Smuzhiyun err("no memory");
230*4882a593Smuzhiyun return NULL;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun bus = kzalloc(bus_specific_len, GFP_KERNEL);
234*4882a593Smuzhiyun if (!bus) {
235*4882a593Smuzhiyun err("no memory");
236*4882a593Smuzhiyun kfree(fc);
237*4882a593Smuzhiyun return NULL;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun fc->bus_specific = bus;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun return fc;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun EXPORT_SYMBOL(flexcop_device_kmalloc);
245*4882a593Smuzhiyun
flexcop_device_kfree(struct flexcop_device * fc)246*4882a593Smuzhiyun void flexcop_device_kfree(struct flexcop_device *fc)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun kfree(fc->bus_specific);
249*4882a593Smuzhiyun kfree(fc);
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun EXPORT_SYMBOL(flexcop_device_kfree);
252*4882a593Smuzhiyun
flexcop_device_initialize(struct flexcop_device * fc)253*4882a593Smuzhiyun int flexcop_device_initialize(struct flexcop_device *fc)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun int ret;
256*4882a593Smuzhiyun ibi_zero.raw = 0;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun flexcop_reset(fc);
259*4882a593Smuzhiyun flexcop_determine_revision(fc);
260*4882a593Smuzhiyun flexcop_sram_init(fc);
261*4882a593Smuzhiyun flexcop_hw_filter_init(fc);
262*4882a593Smuzhiyun flexcop_smc_ctrl(fc, 0);
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun ret = flexcop_dvb_init(fc);
265*4882a593Smuzhiyun if (ret)
266*4882a593Smuzhiyun goto error;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun /* i2c has to be done before doing EEProm stuff -
269*4882a593Smuzhiyun * because the EEProm is accessed via i2c */
270*4882a593Smuzhiyun ret = flexcop_i2c_init(fc);
271*4882a593Smuzhiyun if (ret)
272*4882a593Smuzhiyun goto error;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun /* do the MAC address reading after initializing the dvb_adapter */
275*4882a593Smuzhiyun if (fc->get_mac_addr(fc, 0) == 0) {
276*4882a593Smuzhiyun u8 *b = fc->dvb_adapter.proposed_mac;
277*4882a593Smuzhiyun info("MAC address = %pM", b);
278*4882a593Smuzhiyun flexcop_set_mac_filter(fc,b);
279*4882a593Smuzhiyun flexcop_mac_filter_ctrl(fc,1);
280*4882a593Smuzhiyun } else
281*4882a593Smuzhiyun warn("reading of MAC address failed.\n");
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun ret = flexcop_frontend_init(fc);
284*4882a593Smuzhiyun if (ret)
285*4882a593Smuzhiyun goto error;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun flexcop_device_name(fc,"initialization of","complete");
288*4882a593Smuzhiyun return 0;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun error:
291*4882a593Smuzhiyun flexcop_device_exit(fc);
292*4882a593Smuzhiyun return ret;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun EXPORT_SYMBOL(flexcop_device_initialize);
295*4882a593Smuzhiyun
flexcop_device_exit(struct flexcop_device * fc)296*4882a593Smuzhiyun void flexcop_device_exit(struct flexcop_device *fc)
297*4882a593Smuzhiyun {
298*4882a593Smuzhiyun flexcop_frontend_exit(fc);
299*4882a593Smuzhiyun flexcop_i2c_exit(fc);
300*4882a593Smuzhiyun flexcop_dvb_exit(fc);
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun EXPORT_SYMBOL(flexcop_device_exit);
303*4882a593Smuzhiyun
flexcop_module_init(void)304*4882a593Smuzhiyun static int flexcop_module_init(void)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun info(DRIVER_NAME " loaded successfully");
307*4882a593Smuzhiyun return 0;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun
flexcop_module_cleanup(void)310*4882a593Smuzhiyun static void flexcop_module_cleanup(void)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun info(DRIVER_NAME " unloaded successfully");
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun module_init(flexcop_module_init);
316*4882a593Smuzhiyun module_exit(flexcop_module_cleanup);
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun MODULE_AUTHOR(DRIVER_AUTHOR);
319*4882a593Smuzhiyun MODULE_DESCRIPTION(DRIVER_NAME);
320*4882a593Smuzhiyun MODULE_LICENSE("GPL");
321