Commit | Line | Data |
---|---|---|
55f51efd JS |
1 | /* |
2 | * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) | |
3 | * | |
4 | * Copyright (C) 2001-5, B2C2 inc. | |
5 | * | |
99e44da7 | 6 | * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de> |
55f51efd JS |
7 | * |
8 | * This driver is "hard-coded" to be used with the 1st generation of | |
9 | * Technisat/B2C2's Air2PC ATSC PCI/USB cards/boxes. The pll-programming | |
10 | * (Panasonic CT10S) is located here, which is actually wrong. Unless there is | |
11 | * another device with a BCM3510, this is no problem. | |
12 | * | |
13 | * The driver works also with QAM64 DVB-C, but had an unreasonable high | |
14 | * UNC. (Tested with the Air2PC ATSC 1st generation) | |
15 | * | |
16 | * You'll need a firmware for this driver in order to get it running. It is | |
17 | * called "dvb-fe-bcm3510-01.fw". | |
18 | * | |
19 | * This program is free software; you can redistribute it and/or modify it | |
20 | * under the terms of the GNU General Public License as published by the Free | |
21 | * Software Foundation; either version 2 of the License, or (at your option) | |
22 | * any later version. | |
23 | * | |
24 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
25 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
26 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
27 | * more details. | |
28 | * | |
29 | * You should have received a copy of the GNU General Public License along with | |
30 | * this program; if not, write to the Free Software Foundation, Inc., 675 Mass | |
31 | * Ave, Cambridge, MA 02139, USA. | |
32 | */ | |
33 | ||
34 | #include <linux/init.h> | |
35 | #include <linux/module.h> | |
55f51efd JS |
36 | #include <linux/device.h> |
37 | #include <linux/firmware.h> | |
4e57b681 TS |
38 | #include <linux/jiffies.h> |
39 | #include <linux/string.h> | |
40 | #include <linux/slab.h> | |
3593cab5 | 41 | #include <linux/mutex.h> |
55f51efd JS |
42 | |
43 | #include "dvb_frontend.h" | |
44 | #include "bcm3510.h" | |
45 | #include "bcm3510_priv.h" | |
46 | ||
8393796d MCC |
47 | /* Max transfer size done by bcm3510_do_hab_cmd() function */ |
48 | #define MAX_XFER_SIZE 128 | |
49 | ||
55f51efd JS |
50 | struct bcm3510_state { |
51 | ||
52 | struct i2c_adapter* i2c; | |
55f51efd JS |
53 | const struct bcm3510_config* config; |
54 | struct dvb_frontend frontend; | |
55 | ||
56 | /* demodulator private data */ | |
3593cab5 | 57 | struct mutex hab_mutex; |
55f51efd JS |
58 | u8 firmware_loaded:1; |
59 | ||
60 | unsigned long next_status_check; | |
61 | unsigned long status_check_interval; | |
62 | struct bcm3510_hab_cmd_status1 status1; | |
63 | struct bcm3510_hab_cmd_status2 status2; | |
64 | }; | |
65 | ||
66 | static int debug; | |
67 | module_param(debug, int, 0644); | |
68 | MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c (|-able))."); | |
69 | ||
70 | #define dprintk(level,x...) if (level & debug) printk(x) | |
71 | #define dbufout(b,l,m) {\ | |
72 | int i; \ | |
73 | for (i = 0; i < l; i++) \ | |
9101e622 | 74 | m("%02x ",b[i]); \ |
55f51efd JS |
75 | } |
76 | #define deb_info(args...) dprintk(0x01,args) | |
77 | #define deb_i2c(args...) dprintk(0x02,args) | |
78 | #define deb_hab(args...) dprintk(0x04,args) | |
79 | ||
80 | /* transfer functions */ | |
81 | static int bcm3510_writebytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) | |
82 | { | |
83 | u8 b[256]; | |
84 | int err; | |
85 | struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = len + 1 }; | |
86 | ||
87 | b[0] = reg; | |
88 | memcpy(&b[1],buf,len); | |
89 | ||
90 | deb_i2c("i2c wr %02x: ",reg); | |
91 | dbufout(buf,len,deb_i2c); | |
92 | deb_i2c("\n"); | |
93 | ||
94 | if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { | |
95 | ||
96 | deb_info("%s: i2c write error (addr %02x, reg %02x, err == %i)\n", | |
271ddbf7 | 97 | __func__, state->config->demod_address, reg, err); |
55f51efd JS |
98 | return -EREMOTEIO; |
99 | } | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
104 | static int bcm3510_readbytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) | |
105 | { | |
106 | struct i2c_msg msg[] = { | |
107 | { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, | |
108 | { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } | |
109 | }; | |
110 | int err; | |
111 | ||
112 | memset(buf,0,len); | |
113 | ||
114 | if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) { | |
115 | deb_info("%s: i2c read error (addr %02x, reg %02x, err == %i)\n", | |
271ddbf7 | 116 | __func__, state->config->demod_address, reg, err); |
55f51efd JS |
117 | return -EREMOTEIO; |
118 | } | |
119 | deb_i2c("i2c rd %02x: ",reg); | |
120 | dbufout(buf,len,deb_i2c); | |
121 | deb_i2c("\n"); | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | static int bcm3510_writeB(struct bcm3510_state *state, u8 reg, bcm3510_register_value v) | |
127 | { | |
128 | return bcm3510_writebytes(state,reg,&v.raw,1); | |
129 | } | |
130 | ||
131 | static int bcm3510_readB(struct bcm3510_state *state, u8 reg, bcm3510_register_value *v) | |
132 | { | |
133 | return bcm3510_readbytes(state,reg,&v->raw,1); | |
134 | } | |
135 | ||
136 | /* Host Access Buffer transfers */ | |
137 | static int bcm3510_hab_get_response(struct bcm3510_state *st, u8 *buf, int len) | |
138 | { | |
139 | bcm3510_register_value v; | |
140 | int ret,i; | |
141 | ||
142 | v.HABADR_a6.HABADR = 0; | |
143 | if ((ret = bcm3510_writeB(st,0xa6,v)) < 0) | |
144 | return ret; | |
145 | ||
146 | for (i = 0; i < len; i++) { | |
147 | if ((ret = bcm3510_readB(st,0xa7,&v)) < 0) | |
148 | return ret; | |
149 | buf[i] = v.HABDATA_a7; | |
150 | } | |
151 | return 0; | |
152 | } | |
153 | ||
154 | static int bcm3510_hab_send_request(struct bcm3510_state *st, u8 *buf, int len) | |
155 | { | |
156 | bcm3510_register_value v,hab; | |
157 | int ret,i; | |
158 | unsigned long t; | |
159 | ||
160 | /* Check if any previous HAB request still needs to be serviced by the | |
25985edc | 161 | * Acquisition Processor before sending new request */ |
55f51efd JS |
162 | if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) |
163 | return ret; | |
164 | if (v.HABSTAT_a8.HABR) { | |
165 | deb_info("HAB is running already - clearing it.\n"); | |
166 | v.HABSTAT_a8.HABR = 0; | |
167 | bcm3510_writeB(st,0xa8,v); | |
168 | // return -EBUSY; | |
169 | } | |
170 | ||
171 | /* Send the start HAB Address (automatically incremented after write of | |
172 | * HABDATA) and write the HAB Data */ | |
173 | hab.HABADR_a6.HABADR = 0; | |
174 | if ((ret = bcm3510_writeB(st,0xa6,hab)) < 0) | |
175 | return ret; | |
176 | ||
177 | for (i = 0; i < len; i++) { | |
178 | hab.HABDATA_a7 = buf[i]; | |
179 | if ((ret = bcm3510_writeB(st,0xa7,hab)) < 0) | |
180 | return ret; | |
181 | } | |
182 | ||
183 | /* Set the HABR bit to indicate AP request in progress (LBHABR allows HABR to | |
184 | * be written) */ | |
185 | v.raw = 0; v.HABSTAT_a8.HABR = 1; v.HABSTAT_a8.LDHABR = 1; | |
186 | if ((ret = bcm3510_writeB(st,0xa8,v)) < 0) | |
187 | return ret; | |
188 | ||
189 | /* Polling method: Wait until the AP finishes processing the HAB request */ | |
190 | t = jiffies + 1*HZ; | |
191 | while (time_before(jiffies, t)) { | |
192 | deb_info("waiting for HAB to complete\n"); | |
193 | msleep(10); | |
194 | if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) | |
195 | return ret; | |
196 | ||
197 | if (!v.HABSTAT_a8.HABR) | |
198 | return 0; | |
199 | } | |
200 | ||
201 | deb_info("send_request execution timed out.\n"); | |
202 | return -ETIMEDOUT; | |
203 | } | |
204 | ||
205 | static int bcm3510_do_hab_cmd(struct bcm3510_state *st, u8 cmd, u8 msgid, u8 *obuf, u8 olen, u8 *ibuf, u8 ilen) | |
206 | { | |
8393796d | 207 | u8 ob[MAX_XFER_SIZE], ib[MAX_XFER_SIZE]; |
55f51efd JS |
208 | int ret = 0; |
209 | ||
8393796d MCC |
210 | if (ilen + 2 > sizeof(ib)) { |
211 | deb_hab("do_hab_cmd: ilen=%d is too big!\n", ilen); | |
212 | return -EINVAL; | |
213 | } | |
214 | ||
215 | if (olen + 2 > sizeof(ob)) { | |
216 | deb_hab("do_hab_cmd: olen=%d is too big!\n", olen); | |
217 | return -EINVAL; | |
218 | } | |
219 | ||
55f51efd JS |
220 | ob[0] = cmd; |
221 | ob[1] = msgid; | |
222 | memcpy(&ob[2],obuf,olen); | |
223 | ||
224 | deb_hab("hab snd: "); | |
225 | dbufout(ob,olen+2,deb_hab); | |
226 | deb_hab("\n"); | |
227 | ||
3593cab5 | 228 | if (mutex_lock_interruptible(&st->hab_mutex) < 0) |
55f51efd JS |
229 | return -EAGAIN; |
230 | ||
231 | if ((ret = bcm3510_hab_send_request(st, ob, olen+2)) < 0 || | |
232 | (ret = bcm3510_hab_get_response(st, ib, ilen+2)) < 0) | |
233 | goto error; | |
234 | ||
235 | deb_hab("hab get: "); | |
236 | dbufout(ib,ilen+2,deb_hab); | |
237 | deb_hab("\n"); | |
238 | ||
239 | memcpy(ibuf,&ib[2],ilen); | |
240 | error: | |
3593cab5 | 241 | mutex_unlock(&st->hab_mutex); |
55f51efd JS |
242 | return ret; |
243 | } | |
244 | ||
245 | #if 0 | |
246 | /* not needed, we use a semaphore to prevent HAB races */ | |
247 | static int bcm3510_is_ap_ready(struct bcm3510_state *st) | |
248 | { | |
249 | bcm3510_register_value ap,hab; | |
250 | int ret; | |
251 | ||
252 | if ((ret = bcm3510_readB(st,0xa8,&hab)) < 0 || | |
253 | (ret = bcm3510_readB(st,0xa2,&ap) < 0)) | |
254 | return ret; | |
255 | ||
256 | if (ap.APSTAT1_a2.RESET || ap.APSTAT1_a2.IDLE || ap.APSTAT1_a2.STOP || hab.HABSTAT_a8.HABR) { | |
257 | deb_info("AP is busy\n"); | |
258 | return -EBUSY; | |
259 | } | |
260 | ||
261 | return 0; | |
262 | } | |
263 | #endif | |
264 | ||
265 | static int bcm3510_bert_reset(struct bcm3510_state *st) | |
266 | { | |
267 | bcm3510_register_value b; | |
268 | int ret; | |
269 | ||
f2a33314 | 270 | if ((ret = bcm3510_readB(st,0xfa,&b)) < 0) |
55f51efd JS |
271 | return ret; |
272 | ||
273 | b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); | |
274 | b.BERCTL_fa.RESYNC = 1; bcm3510_writeB(st,0xfa,b); | |
275 | b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); | |
276 | b.BERCTL_fa.CNTCTL = 1; b.BERCTL_fa.BITCNT = 1; bcm3510_writeB(st,0xfa,b); | |
277 | ||
278 | /* clear residual bit counter TODO */ | |
279 | return 0; | |
280 | } | |
281 | ||
282 | static int bcm3510_refresh_state(struct bcm3510_state *st) | |
283 | { | |
284 | if (time_after(jiffies,st->next_status_check)) { | |
285 | bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS1, NULL,0, (u8 *)&st->status1, sizeof(st->status1)); | |
286 | bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS2, NULL,0, (u8 *)&st->status2, sizeof(st->status2)); | |
287 | st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; | |
288 | } | |
289 | return 0; | |
290 | } | |
291 | ||
0df289a2 | 292 | static int bcm3510_read_status(struct dvb_frontend *fe, enum fe_status *status) |
55f51efd JS |
293 | { |
294 | struct bcm3510_state* st = fe->demodulator_priv; | |
295 | bcm3510_refresh_state(st); | |
296 | ||
297 | *status = 0; | |
298 | if (st->status1.STATUS1.RECEIVER_LOCK) | |
299 | *status |= FE_HAS_LOCK | FE_HAS_SYNC; | |
300 | ||
301 | if (st->status1.STATUS1.FEC_LOCK) | |
302 | *status |= FE_HAS_VITERBI; | |
303 | ||
304 | if (st->status1.STATUS1.OUT_PLL_LOCK) | |
305 | *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; | |
306 | ||
307 | if (*status & FE_HAS_LOCK) | |
308 | st->status_check_interval = 1500; | |
309 | else /* more frequently checks if no lock has been achieved yet */ | |
310 | st->status_check_interval = 500; | |
311 | ||
312 | deb_info("real_status: %02x\n",*status); | |
313 | return 0; | |
314 | } | |
315 | ||
316 | static int bcm3510_read_ber(struct dvb_frontend* fe, u32* ber) | |
317 | { | |
318 | struct bcm3510_state* st = fe->demodulator_priv; | |
319 | bcm3510_refresh_state(st); | |
320 | ||
321 | *ber = (st->status2.LDBER0 << 16) | (st->status2.LDBER1 << 8) | st->status2.LDBER2; | |
322 | return 0; | |
323 | } | |
324 | ||
325 | static int bcm3510_read_unc(struct dvb_frontend* fe, u32* unc) | |
326 | { | |
327 | struct bcm3510_state* st = fe->demodulator_priv; | |
328 | bcm3510_refresh_state(st); | |
329 | *unc = (st->status2.LDUERC0 << 8) | st->status2.LDUERC1; | |
330 | return 0; | |
331 | } | |
332 | ||
333 | static int bcm3510_read_signal_strength(struct dvb_frontend* fe, u16* strength) | |
334 | { | |
335 | struct bcm3510_state* st = fe->demodulator_priv; | |
336 | s32 t; | |
337 | ||
338 | bcm3510_refresh_state(st); | |
339 | t = st->status2.SIGNAL; | |
340 | ||
341 | if (t > 190) | |
342 | t = 190; | |
343 | if (t < 90) | |
344 | t = 90; | |
345 | ||
346 | t -= 90; | |
347 | t = t * 0xff / 100; | |
348 | /* normalize if necessary */ | |
349 | *strength = (t << 8) | t; | |
350 | return 0; | |
351 | } | |
352 | ||
353 | static int bcm3510_read_snr(struct dvb_frontend* fe, u16* snr) | |
354 | { | |
355 | struct bcm3510_state* st = fe->demodulator_priv; | |
356 | bcm3510_refresh_state(st); | |
357 | ||
358 | *snr = st->status1.SNR_EST0*1000 + ((st->status1.SNR_EST1*1000) >> 8); | |
359 | return 0; | |
360 | } | |
361 | ||
362 | /* tuner frontend programming */ | |
363 | static int bcm3510_tuner_cmd(struct bcm3510_state* st,u8 bc, u16 n, u8 a) | |
364 | { | |
365 | struct bcm3510_hab_cmd_tune c; | |
366 | memset(&c,0,sizeof(struct bcm3510_hab_cmd_tune)); | |
367 | ||
368 | /* I2C Mode disabled, set 16 control / Data pairs */ | |
369 | c.length = 0x10; | |
370 | c.clock_width = 0; | |
371 | /* CS1, CS0, DATA, CLK bits control the tuner RF_AGC_SEL pin is set to | |
372 | * logic high (as Configuration) */ | |
373 | c.misc = 0x10; | |
374 | /* Set duration of the initial state of TUNCTL = 3.34 micro Sec */ | |
375 | c.TUNCTL_state = 0x40; | |
376 | ||
25985edc | 377 | /* PRESCALER DIVIDE RATIO | BC1_2_3_4; (band switch), 1stosc REFERENCE COUNTER REF_S12 and REF_S11 */ |
55f51efd JS |
378 | c.ctl_dat[0].ctrl.size = BITS_8; |
379 | c.ctl_dat[0].data = 0x80 | bc; | |
380 | ||
381 | /* Control DATA pin, 1stosc REFERENCE COUNTER REF_S10 to REF_S3 */ | |
382 | c.ctl_dat[1].ctrl.size = BITS_8; | |
383 | c.ctl_dat[1].data = 4; | |
384 | ||
385 | /* set CONTROL BIT 1 to 1, 1stosc REFERENCE COUNTER REF_S2 to REF_S1 */ | |
386 | c.ctl_dat[2].ctrl.size = BITS_3; | |
387 | c.ctl_dat[2].data = 0x20; | |
388 | ||
389 | /* control CS0 pin, pulse byte ? */ | |
390 | c.ctl_dat[3].ctrl.size = BITS_3; | |
391 | c.ctl_dat[3].ctrl.clk_off = 1; | |
392 | c.ctl_dat[3].ctrl.cs0 = 1; | |
393 | c.ctl_dat[3].data = 0x40; | |
394 | ||
395 | /* PGM_S18 to PGM_S11 */ | |
396 | c.ctl_dat[4].ctrl.size = BITS_8; | |
397 | c.ctl_dat[4].data = n >> 3; | |
398 | ||
399 | /* PGM_S10 to PGM_S8, SWL_S7 to SWL_S3 */ | |
400 | c.ctl_dat[5].ctrl.size = BITS_8; | |
401 | c.ctl_dat[5].data = ((n & 0x7) << 5) | (a >> 2); | |
402 | ||
403 | /* SWL_S2 and SWL_S1, set CONTROL BIT 2 to 0 */ | |
404 | c.ctl_dat[6].ctrl.size = BITS_3; | |
405 | c.ctl_dat[6].data = (a << 6) & 0xdf; | |
406 | ||
407 | /* control CS0 pin, pulse byte ? */ | |
408 | c.ctl_dat[7].ctrl.size = BITS_3; | |
409 | c.ctl_dat[7].ctrl.clk_off = 1; | |
410 | c.ctl_dat[7].ctrl.cs0 = 1; | |
411 | c.ctl_dat[7].data = 0x40; | |
412 | ||
25985edc | 413 | /* PRESCALER DIVIDE RATIO, 2ndosc REFERENCE COUNTER REF_S12 and REF_S11 */ |
55f51efd JS |
414 | c.ctl_dat[8].ctrl.size = BITS_8; |
415 | c.ctl_dat[8].data = 0x80; | |
416 | ||
417 | /* 2ndosc REFERENCE COUNTER REF_S10 to REF_S3 */ | |
418 | c.ctl_dat[9].ctrl.size = BITS_8; | |
419 | c.ctl_dat[9].data = 0x10; | |
420 | ||
421 | /* set CONTROL BIT 1 to 1, 2ndosc REFERENCE COUNTER REF_S2 to REF_S1 */ | |
422 | c.ctl_dat[10].ctrl.size = BITS_3; | |
423 | c.ctl_dat[10].data = 0x20; | |
424 | ||
425 | /* pulse byte */ | |
426 | c.ctl_dat[11].ctrl.size = BITS_3; | |
427 | c.ctl_dat[11].ctrl.clk_off = 1; | |
428 | c.ctl_dat[11].ctrl.cs1 = 1; | |
429 | c.ctl_dat[11].data = 0x40; | |
430 | ||
431 | /* PGM_S18 to PGM_S11 */ | |
432 | c.ctl_dat[12].ctrl.size = BITS_8; | |
433 | c.ctl_dat[12].data = 0x2a; | |
434 | ||
435 | /* PGM_S10 to PGM_S8 and SWL_S7 to SWL_S3 */ | |
436 | c.ctl_dat[13].ctrl.size = BITS_8; | |
437 | c.ctl_dat[13].data = 0x8e; | |
438 | ||
439 | /* SWL_S2 and SWL_S1 and set CONTROL BIT 2 to 0 */ | |
440 | c.ctl_dat[14].ctrl.size = BITS_3; | |
441 | c.ctl_dat[14].data = 0; | |
442 | ||
443 | /* Pulse Byte */ | |
444 | c.ctl_dat[15].ctrl.size = BITS_3; | |
445 | c.ctl_dat[15].ctrl.clk_off = 1; | |
446 | c.ctl_dat[15].ctrl.cs1 = 1; | |
447 | c.ctl_dat[15].data = 0x40; | |
448 | ||
449 | return bcm3510_do_hab_cmd(st,CMD_TUNE, MSGID_TUNE,(u8 *) &c,sizeof(c), NULL, 0); | |
450 | } | |
451 | ||
452 | static int bcm3510_set_freq(struct bcm3510_state* st,u32 freq) | |
453 | { | |
454 | u8 bc,a; | |
455 | u16 n; | |
456 | s32 YIntercept,Tfvco1; | |
457 | ||
458 | freq /= 1000; | |
459 | ||
460 | deb_info("%dkHz:",freq); | |
461 | /* set Band Switch */ | |
462 | if (freq <= 168000) | |
463 | bc = 0x1c; | |
464 | else if (freq <= 378000) | |
465 | bc = 0x2c; | |
466 | else | |
467 | bc = 0x30; | |
468 | ||
469 | if (freq >= 470000) { | |
470 | freq -= 470001; | |
471 | YIntercept = 18805; | |
472 | } else if (freq >= 90000) { | |
473 | freq -= 90001; | |
474 | YIntercept = 15005; | |
475 | } else if (freq >= 76000){ | |
476 | freq -= 76001; | |
477 | YIntercept = 14865; | |
478 | } else { | |
479 | freq -= 54001; | |
480 | YIntercept = 14645; | |
481 | } | |
482 | ||
483 | Tfvco1 = (((freq/6000)*60 + YIntercept)*4)/10; | |
484 | ||
485 | n = Tfvco1 >> 6; | |
486 | a = Tfvco1 & 0x3f; | |
487 | ||
488 | deb_info(" BC1_2_3_4: %x, N: %x A: %x\n", bc, n, a); | |
489 | if (n >= 16 && n <= 2047) | |
490 | return bcm3510_tuner_cmd(st,bc,n,a); | |
491 | ||
492 | return -EINVAL; | |
493 | } | |
494 | ||
a27378c6 | 495 | static int bcm3510_set_frontend(struct dvb_frontend *fe) |
55f51efd | 496 | { |
a27378c6 | 497 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
55f51efd JS |
498 | struct bcm3510_state* st = fe->demodulator_priv; |
499 | struct bcm3510_hab_cmd_ext_acquire cmd; | |
500 | struct bcm3510_hab_cmd_bert_control bert; | |
501 | int ret; | |
502 | ||
503 | memset(&cmd,0,sizeof(cmd)); | |
a27378c6 | 504 | switch (c->modulation) { |
55f51efd JS |
505 | case QAM_256: |
506 | cmd.ACQUIRE0.MODE = 0x1; | |
507 | cmd.ACQUIRE1.SYM_RATE = 0x1; | |
508 | cmd.ACQUIRE1.IF_FREQ = 0x1; | |
509 | break; | |
510 | case QAM_64: | |
511 | cmd.ACQUIRE0.MODE = 0x2; | |
512 | cmd.ACQUIRE1.SYM_RATE = 0x2; | |
513 | cmd.ACQUIRE1.IF_FREQ = 0x1; | |
514 | break; | |
a27378c6 MCC |
515 | #if 0 |
516 | case QAM_256: | |
55f51efd JS |
517 | cmd.ACQUIRE0.MODE = 0x3; |
518 | break; | |
519 | case QAM_128: | |
520 | cmd.ACQUIRE0.MODE = 0x4; | |
521 | break; | |
522 | case QAM_64: | |
523 | cmd.ACQUIRE0.MODE = 0x5; | |
524 | break; | |
525 | case QAM_32: | |
526 | cmd.ACQUIRE0.MODE = 0x6; | |
527 | break; | |
528 | case QAM_16: | |
529 | cmd.ACQUIRE0.MODE = 0x7; | |
a27378c6 MCC |
530 | break; |
531 | #endif | |
55f51efd JS |
532 | case VSB_8: |
533 | cmd.ACQUIRE0.MODE = 0x8; | |
534 | cmd.ACQUIRE1.SYM_RATE = 0x0; | |
535 | cmd.ACQUIRE1.IF_FREQ = 0x0; | |
536 | break; | |
537 | case VSB_16: | |
538 | cmd.ACQUIRE0.MODE = 0x9; | |
539 | cmd.ACQUIRE1.SYM_RATE = 0x0; | |
540 | cmd.ACQUIRE1.IF_FREQ = 0x0; | |
17992979 | 541 | break; |
55f51efd JS |
542 | default: |
543 | return -EINVAL; | |
c2c1b415 | 544 | } |
55f51efd JS |
545 | cmd.ACQUIRE0.OFFSET = 0; |
546 | cmd.ACQUIRE0.NTSCSWEEP = 1; | |
547 | cmd.ACQUIRE0.FA = 1; | |
548 | cmd.ACQUIRE0.BW = 0; | |
549 | ||
550 | /* if (enableOffset) { | |
551 | cmd.IF_OFFSET0 = xx; | |
552 | cmd.IF_OFFSET1 = xx; | |
553 | ||
554 | cmd.SYM_OFFSET0 = xx; | |
555 | cmd.SYM_OFFSET1 = xx; | |
556 | if (enableNtscSweep) { | |
557 | cmd.NTSC_OFFSET0; | |
558 | cmd.NTSC_OFFSET1; | |
559 | } | |
560 | } */ | |
561 | bcm3510_do_hab_cmd(st, CMD_ACQUIRE, MSGID_EXT_TUNER_ACQUIRE, (u8 *) &cmd, sizeof(cmd), NULL, 0); | |
562 | ||
563 | /* doing it with different MSGIDs, data book and source differs */ | |
564 | bert.BE = 0; | |
565 | bert.unused = 0; | |
566 | bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_CONTROL, (u8 *) &bert, sizeof(bert), NULL, 0); | |
567 | bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_SET, (u8 *) &bert, sizeof(bert), NULL, 0); | |
568 | ||
569 | bcm3510_bert_reset(st); | |
570 | ||
a27378c6 MCC |
571 | ret = bcm3510_set_freq(st, c->frequency); |
572 | if (ret < 0) | |
55f51efd JS |
573 | return ret; |
574 | ||
575 | memset(&st->status1,0,sizeof(st->status1)); | |
576 | memset(&st->status2,0,sizeof(st->status2)); | |
577 | st->status_check_interval = 500; | |
578 | ||
579 | /* Give the AP some time */ | |
580 | msleep(200); | |
581 | ||
582 | return 0; | |
583 | } | |
584 | ||
585 | static int bcm3510_sleep(struct dvb_frontend* fe) | |
586 | { | |
587 | return 0; | |
588 | } | |
589 | ||
590 | static int bcm3510_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) | |
591 | { | |
592 | s->min_delay_ms = 1000; | |
593 | s->step_size = 0; | |
594 | s->max_drift = 0; | |
595 | return 0; | |
596 | } | |
597 | ||
598 | static void bcm3510_release(struct dvb_frontend* fe) | |
599 | { | |
600 | struct bcm3510_state* state = fe->demodulator_priv; | |
601 | kfree(state); | |
602 | } | |
603 | ||
604 | /* firmware download: | |
605 | * firmware file is build up like this: | |
606 | * 16bit addr, 16bit length, 8byte of length | |
607 | */ | |
608 | #define BCM3510_DEFAULT_FIRMWARE "dvb-fe-bcm3510-01.fw" | |
609 | ||
bc179153 DW |
610 | static int bcm3510_write_ram(struct bcm3510_state *st, u16 addr, const u8 *b, |
611 | u16 len) | |
55f51efd JS |
612 | { |
613 | int ret = 0,i; | |
614 | bcm3510_register_value vH, vL,vD; | |
615 | ||
616 | vH.MADRH_a9 = addr >> 8; | |
617 | vL.MADRL_aa = addr; | |
618 | if ((ret = bcm3510_writeB(st,0xa9,vH)) < 0) return ret; | |
619 | if ((ret = bcm3510_writeB(st,0xaa,vL)) < 0) return ret; | |
620 | ||
621 | for (i = 0; i < len; i++) { | |
622 | vD.MDATA_ab = b[i]; | |
623 | if ((ret = bcm3510_writeB(st,0xab,vD)) < 0) | |
624 | return ret; | |
625 | } | |
626 | ||
627 | return 0; | |
628 | } | |
629 | ||
630 | static int bcm3510_download_firmware(struct dvb_frontend* fe) | |
631 | { | |
632 | struct bcm3510_state* st = fe->demodulator_priv; | |
633 | const struct firmware *fw; | |
634 | u16 addr,len; | |
bc179153 | 635 | const u8 *b; |
55f51efd JS |
636 | int ret,i; |
637 | ||
638 | deb_info("requesting firmware\n"); | |
639 | if ((ret = st->config->request_firmware(fe, &fw, BCM3510_DEFAULT_FIRMWARE)) < 0) { | |
640 | err("could not load firmware (%s): %d",BCM3510_DEFAULT_FIRMWARE,ret); | |
641 | return ret; | |
642 | } | |
35f30f36 | 643 | deb_info("got firmware: %zu\n", fw->size); |
55f51efd JS |
644 | |
645 | b = fw->data; | |
646 | for (i = 0; i < fw->size;) { | |
fba16a1e HV |
647 | addr = le16_to_cpu(*((__le16 *)&b[i])); |
648 | len = le16_to_cpu(*((__le16 *)&b[i+2])); | |
e18828e4 | 649 | deb_info("firmware chunk, addr: 0x%04x, len: 0x%04x, total length: 0x%04zx\n",addr,len,fw->size); |
55f51efd JS |
650 | if ((ret = bcm3510_write_ram(st,addr,&b[i+4],len)) < 0) { |
651 | err("firmware download failed: %d\n",ret); | |
652 | return ret; | |
653 | } | |
654 | i += 4 + len; | |
655 | } | |
656 | release_firmware(fw); | |
657 | deb_info("firmware download successfully completed\n"); | |
658 | return 0; | |
659 | } | |
660 | ||
661 | static int bcm3510_check_firmware_version(struct bcm3510_state *st) | |
662 | { | |
663 | struct bcm3510_hab_cmd_get_version_info ver; | |
664 | bcm3510_do_hab_cmd(st,CMD_GET_VERSION_INFO,MSGID_GET_VERSION_INFO,NULL,0,(u8*)&ver,sizeof(ver)); | |
665 | ||
666 | deb_info("Version information: 0x%02x 0x%02x 0x%02x 0x%02x\n", | |
667 | ver.microcode_version, ver.script_version, ver.config_version, ver.demod_version); | |
668 | ||
669 | if (ver.script_version == BCM3510_DEF_SCRIPT_VERSION && | |
670 | ver.config_version == BCM3510_DEF_CONFIG_VERSION && | |
671 | ver.demod_version == BCM3510_DEF_DEMOD_VERSION) | |
672 | return 0; | |
673 | ||
674 | deb_info("version check failed\n"); | |
675 | return -ENODEV; | |
676 | } | |
677 | ||
678 | /* (un)resetting the AP */ | |
679 | static int bcm3510_reset(struct bcm3510_state *st) | |
680 | { | |
681 | int ret; | |
682 | unsigned long t; | |
683 | bcm3510_register_value v; | |
684 | ||
685 | bcm3510_readB(st,0xa0,&v); v.HCTL1_a0.RESET = 1; | |
686 | if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) | |
687 | return ret; | |
688 | ||
361f2e8d | 689 | t = jiffies + 3*HZ; |
55f51efd JS |
690 | while (time_before(jiffies, t)) { |
691 | msleep(10); | |
692 | if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) | |
693 | return ret; | |
694 | ||
695 | if (v.APSTAT1_a2.RESET) | |
696 | return 0; | |
697 | } | |
698 | deb_info("reset timed out\n"); | |
699 | return -ETIMEDOUT; | |
700 | } | |
701 | ||
702 | static int bcm3510_clear_reset(struct bcm3510_state *st) | |
703 | { | |
704 | bcm3510_register_value v; | |
705 | int ret; | |
706 | unsigned long t; | |
707 | ||
708 | v.raw = 0; | |
709 | if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) | |
710 | return ret; | |
711 | ||
361f2e8d | 712 | t = jiffies + 3*HZ; |
55f51efd JS |
713 | while (time_before(jiffies, t)) { |
714 | msleep(10); | |
715 | if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) | |
716 | return ret; | |
717 | ||
718 | /* verify that reset is cleared */ | |
719 | if (!v.APSTAT1_a2.RESET) | |
720 | return 0; | |
721 | } | |
722 | deb_info("reset clear timed out\n"); | |
723 | return -ETIMEDOUT; | |
724 | } | |
725 | ||
726 | static int bcm3510_init_cold(struct bcm3510_state *st) | |
727 | { | |
728 | int ret; | |
729 | bcm3510_register_value v; | |
730 | ||
731 | /* read Acquisation Processor status register and check it is not in RUN mode */ | |
732 | if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) | |
733 | return ret; | |
734 | if (v.APSTAT1_a2.RUN) { | |
735 | deb_info("AP is already running - firmware already loaded.\n"); | |
736 | return 0; | |
737 | } | |
738 | ||
739 | deb_info("reset?\n"); | |
740 | if ((ret = bcm3510_reset(st)) < 0) | |
741 | return ret; | |
742 | ||
743 | deb_info("tristate?\n"); | |
744 | /* tri-state */ | |
745 | v.TSTCTL_2e.CTL = 0; | |
746 | if ((ret = bcm3510_writeB(st,0x2e,v)) < 0) | |
747 | return ret; | |
748 | ||
749 | deb_info("firmware?\n"); | |
750 | if ((ret = bcm3510_download_firmware(&st->frontend)) < 0 || | |
751 | (ret = bcm3510_clear_reset(st)) < 0) | |
752 | return ret; | |
753 | ||
754 | /* anything left here to Let the acquisition processor begin execution at program counter 0000 ??? */ | |
755 | ||
756 | return 0; | |
757 | } | |
758 | ||
759 | static int bcm3510_init(struct dvb_frontend* fe) | |
760 | { | |
761 | struct bcm3510_state* st = fe->demodulator_priv; | |
762 | bcm3510_register_value j; | |
763 | struct bcm3510_hab_cmd_set_agc c; | |
764 | int ret; | |
765 | ||
766 | if ((ret = bcm3510_readB(st,0xca,&j)) < 0) | |
767 | return ret; | |
768 | ||
769 | deb_info("JDEC: %02x\n",j.raw); | |
770 | ||
771 | switch (j.JDEC_ca.JDEC) { | |
772 | case JDEC_WAIT_AT_RAM: | |
773 | deb_info("attempting to download firmware\n"); | |
774 | if ((ret = bcm3510_init_cold(st)) < 0) | |
775 | return ret; | |
06eeefe8 MCC |
776 | /* fall-through */ |
777 | case JDEC_EEPROM_LOAD_WAIT: | |
55f51efd JS |
778 | deb_info("firmware is loaded\n"); |
779 | bcm3510_check_firmware_version(st); | |
780 | break; | |
781 | default: | |
782 | return -ENODEV; | |
783 | } | |
784 | ||
785 | memset(&c,0,1); | |
786 | c.SEL = 1; | |
787 | bcm3510_do_hab_cmd(st,CMD_AUTO_PARAM,MSGID_SET_RF_AGC_SEL,(u8 *)&c,sizeof(c),NULL,0); | |
788 | ||
789 | return 0; | |
790 | } | |
791 | ||
792 | ||
bd336e63 | 793 | static const struct dvb_frontend_ops bcm3510_ops; |
55f51efd JS |
794 | |
795 | struct dvb_frontend* bcm3510_attach(const struct bcm3510_config *config, | |
796 | struct i2c_adapter *i2c) | |
797 | { | |
798 | struct bcm3510_state* state = NULL; | |
799 | int ret; | |
800 | bcm3510_register_value v; | |
801 | ||
802 | /* allocate memory for the internal state */ | |
7408187d | 803 | state = kzalloc(sizeof(struct bcm3510_state), GFP_KERNEL); |
55f51efd JS |
804 | if (state == NULL) |
805 | goto error; | |
55f51efd JS |
806 | |
807 | /* setup the state */ | |
808 | ||
809 | state->config = config; | |
810 | state->i2c = i2c; | |
55f51efd JS |
811 | |
812 | /* create dvb_frontend */ | |
dea74869 | 813 | memcpy(&state->frontend.ops, &bcm3510_ops, sizeof(struct dvb_frontend_ops)); |
55f51efd JS |
814 | state->frontend.demodulator_priv = state; |
815 | ||
3593cab5 | 816 | mutex_init(&state->hab_mutex); |
55f51efd JS |
817 | |
818 | if ((ret = bcm3510_readB(state,0xe0,&v)) < 0) | |
819 | goto error; | |
820 | ||
821 | deb_info("Revision: 0x%1x, Layer: 0x%1x.\n",v.REVID_e0.REV,v.REVID_e0.LAYER); | |
822 | ||
823 | if ((v.REVID_e0.REV != 0x1 && v.REVID_e0.LAYER != 0xb) && /* cold */ | |
824 | (v.REVID_e0.REV != 0x8 && v.REVID_e0.LAYER != 0x0)) /* warm */ | |
825 | goto error; | |
826 | ||
827 | info("Revision: 0x%1x, Layer: 0x%1x.",v.REVID_e0.REV,v.REVID_e0.LAYER); | |
828 | ||
829 | bcm3510_reset(state); | |
830 | ||
831 | return &state->frontend; | |
832 | ||
833 | error: | |
834 | kfree(state); | |
835 | return NULL; | |
836 | } | |
837 | EXPORT_SYMBOL(bcm3510_attach); | |
838 | ||
bd336e63 | 839 | static const struct dvb_frontend_ops bcm3510_ops = { |
a27378c6 | 840 | .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, |
55f51efd JS |
841 | .info = { |
842 | .name = "Broadcom BCM3510 VSB/QAM frontend", | |
55f51efd JS |
843 | .frequency_min = 54000000, |
844 | .frequency_max = 803000000, | |
9101e622 | 845 | /* stepsize is just a guess */ |
55f51efd JS |
846 | .frequency_stepsize = 0, |
847 | .caps = | |
848 | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | | |
849 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | | |
850 | FE_CAN_8VSB | FE_CAN_16VSB | | |
851 | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 | |
852 | }, | |
853 | ||
854 | .release = bcm3510_release, | |
855 | ||
856 | .init = bcm3510_init, | |
857 | .sleep = bcm3510_sleep, | |
858 | ||
a27378c6 | 859 | .set_frontend = bcm3510_set_frontend, |
55f51efd JS |
860 | .get_tune_settings = bcm3510_get_tune_settings, |
861 | ||
862 | .read_status = bcm3510_read_status, | |
863 | .read_ber = bcm3510_read_ber, | |
864 | .read_signal_strength = bcm3510_read_signal_strength, | |
865 | .read_snr = bcm3510_read_snr, | |
866 | .read_ucblocks = bcm3510_read_unc, | |
867 | }; | |
868 | ||
869 | MODULE_DESCRIPTION("Broadcom BCM3510 ATSC (8VSB/16VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver"); | |
99e44da7 | 870 | MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); |
55f51efd | 871 | MODULE_LICENSE("GPL"); |