1 /* 2 * Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver 3 * 4 * Copyright (C) 2010 Mauro Carvalho Chehab <mchehab@redhat.com> 5 * Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com> 6 * 7 * FIXME: Need to port to DVB v5.2 API 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License as 11 * published by the Free Software Foundation version 2. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 */ 18 19 #include <linux/kernel.h> 20 #include <asm/div64.h> 21 22 #include "dvb_frontend.h" 23 #include "mb86a20s.h" 24 25 static int debug = 1; 26 module_param(debug, int, 0644); 27 MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); 28 29 #define rc(args...) do { \ 30 printk(KERN_ERR "mb86a20s: " args); \ 31 } while (0) 32 33 #define dprintk(args...) \ 34 do { \ 35 if (debug) { \ 36 printk(KERN_DEBUG "mb86a20s: %s: ", __func__); \ 37 printk(args); \ 38 } \ 39 } while (0) 40 41 struct mb86a20s_state { 42 struct i2c_adapter *i2c; 43 const struct mb86a20s_config *config; 44 45 struct dvb_frontend frontend; 46 47 bool need_init; 48 }; 49 50 struct regdata { 51 u8 reg; 52 u8 data; 53 }; 54 55 /* 56 * Initialization sequence: Use whatevere default values that PV SBTVD 57 * does on its initialisation, obtained via USB snoop 58 */ 59 static struct regdata mb86a20s_init[] = { 60 { 0x70, 0x0f }, 61 { 0x70, 0xff }, 62 { 0x08, 0x01 }, 63 { 0x09, 0x3e }, 64 { 0x50, 0xd1 }, { 0x51, 0x22 }, 65 { 0x39, 0x01 }, 66 { 0x71, 0x00 }, 67 { 0x28, 0x2a }, { 0x29, 0x00 }, { 0x2a, 0xff }, { 0x2b, 0x80 }, 68 { 0x28, 0x20 }, { 0x29, 0x33 }, { 0x2a, 0xdf }, { 0x2b, 0xa9 }, 69 { 0x28, 0x22 }, { 0x29, 0x00 }, { 0x2a, 0x1f }, { 0x2b, 0xf0 }, 70 { 0x3b, 0x21 }, 71 { 0x3c, 0x3a }, 72 { 0x01, 0x0d }, 73 { 0x04, 0x08 }, { 0x05, 0x05 }, 74 { 0x04, 0x0e }, { 0x05, 0x00 }, 75 { 0x04, 0x0f }, { 0x05, 0x14 }, 76 { 0x04, 0x0b }, { 0x05, 0x8c }, 77 { 0x04, 0x00 }, { 0x05, 0x00 }, 78 { 0x04, 0x01 }, { 0x05, 0x07 }, 79 { 0x04, 0x02 }, { 0x05, 0x0f }, 80 { 0x04, 0x03 }, { 0x05, 0xa0 }, 81 { 0x04, 0x09 }, { 0x05, 0x00 }, 82 { 0x04, 0x0a }, { 0x05, 0xff }, 83 { 0x04, 0x27 }, { 0x05, 0x64 }, 84 { 0x04, 0x28 }, { 0x05, 0x00 }, 85 { 0x04, 0x1e }, { 0x05, 0xff }, 86 { 0x04, 0x29 }, { 0x05, 0x0a }, 87 { 0x04, 0x32 }, { 0x05, 0x0a }, 88 { 0x04, 0x14 }, { 0x05, 0x02 }, 89 { 0x04, 0x04 }, { 0x05, 0x00 }, 90 { 0x04, 0x05 }, { 0x05, 0x22 }, 91 { 0x04, 0x06 }, { 0x05, 0x0e }, 92 { 0x04, 0x07 }, { 0x05, 0xd8 }, 93 { 0x04, 0x12 }, { 0x05, 0x00 }, 94 { 0x04, 0x13 }, { 0x05, 0xff }, 95 { 0x04, 0x15 }, { 0x05, 0x4e }, 96 { 0x04, 0x16 }, { 0x05, 0x20 }, 97 { 0x52, 0x01 }, 98 { 0x50, 0xa7 }, { 0x51, 0xff }, 99 { 0x50, 0xa8 }, { 0x51, 0xff }, 100 { 0x50, 0xa9 }, { 0x51, 0xff }, 101 { 0x50, 0xaa }, { 0x51, 0xff }, 102 { 0x50, 0xab }, { 0x51, 0xff }, 103 { 0x50, 0xac }, { 0x51, 0xff }, 104 { 0x50, 0xad }, { 0x51, 0xff }, 105 { 0x50, 0xae }, { 0x51, 0xff }, 106 { 0x50, 0xaf }, { 0x51, 0xff }, 107 { 0x5e, 0x07 }, 108 { 0x50, 0xdc }, { 0x51, 0x01 }, 109 { 0x50, 0xdd }, { 0x51, 0xf4 }, 110 { 0x50, 0xde }, { 0x51, 0x01 }, 111 { 0x50, 0xdf }, { 0x51, 0xf4 }, 112 { 0x50, 0xe0 }, { 0x51, 0x01 }, 113 { 0x50, 0xe1 }, { 0x51, 0xf4 }, 114 { 0x50, 0xb0 }, { 0x51, 0x07 }, 115 { 0x50, 0xb2 }, { 0x51, 0xff }, 116 { 0x50, 0xb3 }, { 0x51, 0xff }, 117 { 0x50, 0xb4 }, { 0x51, 0xff }, 118 { 0x50, 0xb5 }, { 0x51, 0xff }, 119 { 0x50, 0xb6 }, { 0x51, 0xff }, 120 { 0x50, 0xb7 }, { 0x51, 0xff }, 121 { 0x50, 0x50 }, { 0x51, 0x02 }, 122 { 0x50, 0x51 }, { 0x51, 0x04 }, 123 { 0x45, 0x04 }, 124 { 0x48, 0x04 }, 125 { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ 126 { 0x50, 0xd6 }, { 0x51, 0x1f }, 127 { 0x50, 0xd2 }, { 0x51, 0x03 }, 128 { 0x50, 0xd7 }, { 0x51, 0x3f }, 129 { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x28, 0x74 }, { 0x29, 0x40 }, 130 { 0x28, 0x46 }, { 0x29, 0x2c }, { 0x28, 0x46 }, { 0x29, 0x0c }, 131 { 0x04, 0x40 }, { 0x05, 0x01 }, 132 { 0x28, 0x00 }, { 0x29, 0x10 }, 133 { 0x28, 0x05 }, { 0x29, 0x02 }, 134 { 0x1c, 0x01 }, 135 { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 }, 136 { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d }, 137 { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 }, 138 { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 }, 139 { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 }, 140 { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 }, 141 { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 }, 142 { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 }, 143 { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e }, 144 { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e }, 145 { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 }, 146 { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f }, 147 { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 }, 148 { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 }, 149 { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe }, 150 { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 }, 151 { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee }, 152 { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 }, 153 { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f }, 154 { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 }, 155 { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 }, 156 { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a }, 157 { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc }, 158 { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba }, 159 { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 }, 160 { 0x50, 0x1e }, { 0x51, 0x5d }, 161 { 0x50, 0x22 }, { 0x51, 0x00 }, 162 { 0x50, 0x23 }, { 0x51, 0xc8 }, 163 { 0x50, 0x24 }, { 0x51, 0x00 }, 164 { 0x50, 0x25 }, { 0x51, 0xf0 }, 165 { 0x50, 0x26 }, { 0x51, 0x00 }, 166 { 0x50, 0x27 }, { 0x51, 0xc3 }, 167 { 0x50, 0x39 }, { 0x51, 0x02 }, 168 { 0x28, 0x6a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 }, 169 { 0xd0, 0x00 }, 170 }; 171 172 static struct regdata mb86a20s_reset_reception[] = { 173 { 0x70, 0xf0 }, 174 { 0x70, 0xff }, 175 { 0x08, 0x01 }, 176 { 0x08, 0x00 }, 177 }; 178 179 static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, 180 u8 i2c_addr, int reg, int data) 181 { 182 u8 buf[] = { reg, data }; 183 struct i2c_msg msg = { 184 .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 185 }; 186 int rc; 187 188 rc = i2c_transfer(state->i2c, &msg, 1); 189 if (rc != 1) { 190 printk("%s: writereg error (rc == %i, reg == 0x%02x," 191 " data == 0x%02x)\n", __func__, rc, reg, data); 192 return rc; 193 } 194 195 return 0; 196 } 197 198 static int mb86a20s_i2c_writeregdata(struct mb86a20s_state *state, 199 u8 i2c_addr, struct regdata *rd, int size) 200 { 201 int i, rc; 202 203 for (i = 0; i < size; i++) { 204 rc = mb86a20s_i2c_writereg(state, i2c_addr, rd[i].reg, 205 rd[i].data); 206 if (rc < 0) 207 return rc; 208 } 209 return 0; 210 } 211 212 static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, 213 u8 i2c_addr, u8 reg) 214 { 215 u8 val; 216 int rc; 217 struct i2c_msg msg[] = { 218 { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, 219 { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 } 220 }; 221 222 rc = i2c_transfer(state->i2c, msg, 2); 223 224 if (rc != 2) { 225 rc("%s: reg=0x%x (error=%d)\n", __func__, reg, rc); 226 return rc; 227 } 228 229 return val; 230 } 231 232 #define mb86a20s_readreg(state, reg) \ 233 mb86a20s_i2c_readreg(state, state->config->demod_address, reg) 234 #define mb86a20s_writereg(state, reg, val) \ 235 mb86a20s_i2c_writereg(state, state->config->demod_address, reg, val) 236 #define mb86a20s_writeregdata(state, regdata) \ 237 mb86a20s_i2c_writeregdata(state, state->config->demod_address, \ 238 regdata, ARRAY_SIZE(regdata)) 239 240 static int mb86a20s_initfe(struct dvb_frontend *fe) 241 { 242 struct mb86a20s_state *state = fe->demodulator_priv; 243 int rc; 244 u8 regD5 = 1; 245 246 dprintk("\n"); 247 248 if (fe->ops.i2c_gate_ctrl) 249 fe->ops.i2c_gate_ctrl(fe, 0); 250 251 /* Initialize the frontend */ 252 rc = mb86a20s_writeregdata(state, mb86a20s_init); 253 if (rc < 0) 254 goto err; 255 256 if (!state->config->is_serial) { 257 regD5 &= ~1; 258 259 rc = mb86a20s_writereg(state, 0x50, 0xd5); 260 if (rc < 0) 261 goto err; 262 rc = mb86a20s_writereg(state, 0x51, regD5); 263 if (rc < 0) 264 goto err; 265 } 266 267 if (fe->ops.i2c_gate_ctrl) 268 fe->ops.i2c_gate_ctrl(fe, 1); 269 270 err: 271 if (rc < 0) { 272 state->need_init = true; 273 printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); 274 } else { 275 state->need_init = false; 276 dprintk("Initialization succeeded.\n"); 277 } 278 return rc; 279 } 280 281 static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) 282 { 283 struct mb86a20s_state *state = fe->demodulator_priv; 284 unsigned rf_max, rf_min, rf; 285 u8 val; 286 287 dprintk("\n"); 288 289 if (fe->ops.i2c_gate_ctrl) 290 fe->ops.i2c_gate_ctrl(fe, 0); 291 292 /* Does a binary search to get RF strength */ 293 rf_max = 0xfff; 294 rf_min = 0; 295 do { 296 rf = (rf_max + rf_min) / 2; 297 mb86a20s_writereg(state, 0x04, 0x1f); 298 mb86a20s_writereg(state, 0x05, rf >> 8); 299 mb86a20s_writereg(state, 0x04, 0x20); 300 mb86a20s_writereg(state, 0x04, rf); 301 302 val = mb86a20s_readreg(state, 0x02); 303 if (val & 0x08) 304 rf_min = (rf_max + rf_min) / 2; 305 else 306 rf_max = (rf_max + rf_min) / 2; 307 if (rf_max - rf_min < 4) { 308 *strength = (((rf_max + rf_min) / 2) * 65535) / 4095; 309 break; 310 } 311 } while (1); 312 313 dprintk("signal strength = %d\n", *strength); 314 315 if (fe->ops.i2c_gate_ctrl) 316 fe->ops.i2c_gate_ctrl(fe, 1); 317 318 return 0; 319 } 320 321 static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) 322 { 323 struct mb86a20s_state *state = fe->demodulator_priv; 324 u8 val; 325 326 dprintk("\n"); 327 *status = 0; 328 329 if (fe->ops.i2c_gate_ctrl) 330 fe->ops.i2c_gate_ctrl(fe, 0); 331 val = mb86a20s_readreg(state, 0x0a) & 0xf; 332 if (fe->ops.i2c_gate_ctrl) 333 fe->ops.i2c_gate_ctrl(fe, 1); 334 335 if (val >= 2) 336 *status |= FE_HAS_SIGNAL; 337 338 if (val >= 4) 339 *status |= FE_HAS_CARRIER; 340 341 if (val >= 5) 342 *status |= FE_HAS_VITERBI; 343 344 if (val >= 7) 345 *status |= FE_HAS_SYNC; 346 347 if (val >= 8) /* Maybe 9? */ 348 *status |= FE_HAS_LOCK; 349 350 dprintk("val = %d, status = 0x%02x\n", val, *status); 351 352 return 0; 353 } 354 355 static int mb86a20s_set_frontend(struct dvb_frontend *fe) 356 { 357 struct mb86a20s_state *state = fe->demodulator_priv; 358 int rc; 359 #if 0 360 /* 361 * FIXME: Properly implement the set frontend properties 362 */ 363 struct dtv_frontend_properties *p = &fe->dtv_property_cache; 364 #endif 365 366 dprintk("\n"); 367 368 if (fe->ops.i2c_gate_ctrl) 369 fe->ops.i2c_gate_ctrl(fe, 1); 370 dprintk("Calling tuner set parameters\n"); 371 fe->ops.tuner_ops.set_params(fe); 372 373 /* 374 * Make it more reliable: if, for some reason, the initial 375 * device initialization doesn't happen, initialize it when 376 * a SBTVD parameters are adjusted. 377 * 378 * Unfortunately, due to a hard to track bug at tda829x/tda18271, 379 * the agc callback logic is not called during DVB attach time, 380 * causing mb86a20s to not be initialized with Kworld SBTVD. 381 * So, this hack is needed, in order to make Kworld SBTVD to work. 382 */ 383 if (state->need_init) 384 mb86a20s_initfe(fe); 385 386 if (fe->ops.i2c_gate_ctrl) 387 fe->ops.i2c_gate_ctrl(fe, 0); 388 rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); 389 if (fe->ops.i2c_gate_ctrl) 390 fe->ops.i2c_gate_ctrl(fe, 1); 391 392 return rc; 393 } 394 395 static int mb86a20s_get_modulation(struct mb86a20s_state *state, 396 unsigned layer) 397 { 398 int rc; 399 static unsigned char reg[] = { 400 [0] = 0x86, /* Layer A */ 401 [1] = 0x8a, /* Layer B */ 402 [2] = 0x8e, /* Layer C */ 403 }; 404 405 if (layer >= ARRAY_SIZE(reg)) 406 return -EINVAL; 407 rc = mb86a20s_writereg(state, 0x6d, reg[layer]); 408 if (rc < 0) 409 return rc; 410 rc = mb86a20s_readreg(state, 0x6e); 411 if (rc < 0) 412 return rc; 413 switch ((rc & 0x70) >> 4) { 414 case 0: 415 return DQPSK; 416 case 1: 417 return QPSK; 418 case 2: 419 return QAM_16; 420 case 3: 421 return QAM_64; 422 default: 423 return QAM_AUTO; 424 } 425 } 426 427 static int mb86a20s_get_fec(struct mb86a20s_state *state, 428 unsigned layer) 429 { 430 int rc; 431 432 static unsigned char reg[] = { 433 [0] = 0x87, /* Layer A */ 434 [1] = 0x8b, /* Layer B */ 435 [2] = 0x8f, /* Layer C */ 436 }; 437 438 if (layer >= ARRAY_SIZE(reg)) 439 return -EINVAL; 440 rc = mb86a20s_writereg(state, 0x6d, reg[layer]); 441 if (rc < 0) 442 return rc; 443 rc = mb86a20s_readreg(state, 0x6e); 444 if (rc < 0) 445 return rc; 446 switch (rc) { 447 case 0: 448 return FEC_1_2; 449 case 1: 450 return FEC_2_3; 451 case 2: 452 return FEC_3_4; 453 case 3: 454 return FEC_5_6; 455 case 4: 456 return FEC_7_8; 457 default: 458 return FEC_AUTO; 459 } 460 } 461 462 static int mb86a20s_get_interleaving(struct mb86a20s_state *state, 463 unsigned layer) 464 { 465 int rc; 466 467 static unsigned char reg[] = { 468 [0] = 0x88, /* Layer A */ 469 [1] = 0x8c, /* Layer B */ 470 [2] = 0x90, /* Layer C */ 471 }; 472 473 if (layer >= ARRAY_SIZE(reg)) 474 return -EINVAL; 475 rc = mb86a20s_writereg(state, 0x6d, reg[layer]); 476 if (rc < 0) 477 return rc; 478 rc = mb86a20s_readreg(state, 0x6e); 479 if (rc < 0) 480 return rc; 481 if (rc > 3) 482 return -EINVAL; /* Not used */ 483 return rc; 484 } 485 486 static int mb86a20s_get_segment_count(struct mb86a20s_state *state, 487 unsigned layer) 488 { 489 int rc, count; 490 491 static unsigned char reg[] = { 492 [0] = 0x89, /* Layer A */ 493 [1] = 0x8d, /* Layer B */ 494 [2] = 0x91, /* Layer C */ 495 }; 496 497 if (layer >= ARRAY_SIZE(reg)) 498 return -EINVAL; 499 rc = mb86a20s_writereg(state, 0x6d, reg[layer]); 500 if (rc < 0) 501 return rc; 502 rc = mb86a20s_readreg(state, 0x6e); 503 if (rc < 0) 504 return rc; 505 count = (rc >> 4) & 0x0f; 506 507 return count; 508 } 509 510 static int mb86a20s_get_frontend(struct dvb_frontend *fe) 511 { 512 struct mb86a20s_state *state = fe->demodulator_priv; 513 struct dtv_frontend_properties *p = &fe->dtv_property_cache; 514 int i, rc; 515 516 /* Fixed parameters */ 517 p->delivery_system = SYS_ISDBT; 518 p->bandwidth_hz = 6000000; 519 520 if (fe->ops.i2c_gate_ctrl) 521 fe->ops.i2c_gate_ctrl(fe, 0); 522 523 /* Check for partial reception */ 524 rc = mb86a20s_writereg(state, 0x6d, 0x85); 525 if (rc >= 0) 526 rc = mb86a20s_readreg(state, 0x6e); 527 if (rc >= 0) 528 p->isdbt_partial_reception = (rc & 0x10) ? 1 : 0; 529 530 /* Get per-layer data */ 531 p->isdbt_layer_enabled = 0; 532 for (i = 0; i < 3; i++) { 533 rc = mb86a20s_get_segment_count(state, i); 534 if (rc >= 0 && rc < 14) 535 p->layer[i].segment_count = rc; 536 if (rc == 0x0f) 537 continue; 538 p->isdbt_layer_enabled |= 1 << i; 539 rc = mb86a20s_get_modulation(state, i); 540 if (rc >= 0) 541 p->layer[i].modulation = rc; 542 rc = mb86a20s_get_fec(state, i); 543 if (rc >= 0) 544 p->layer[i].fec = rc; 545 rc = mb86a20s_get_interleaving(state, i); 546 if (rc >= 0) 547 p->layer[i].interleaving = rc; 548 } 549 550 p->isdbt_sb_mode = 0; 551 rc = mb86a20s_writereg(state, 0x6d, 0x84); 552 if ((rc >= 0) && ((rc & 0x60) == 0x20)) { 553 p->isdbt_sb_mode = 1; 554 /* At least, one segment should exist */ 555 if (!p->isdbt_sb_segment_count) 556 p->isdbt_sb_segment_count = 1; 557 } else 558 p->isdbt_sb_segment_count = 0; 559 560 /* Get transmission mode and guard interval */ 561 p->transmission_mode = TRANSMISSION_MODE_AUTO; 562 p->guard_interval = GUARD_INTERVAL_AUTO; 563 rc = mb86a20s_readreg(state, 0x07); 564 if (rc >= 0) { 565 if ((rc & 0x60) == 0x20) { 566 switch (rc & 0x0c >> 2) { 567 case 0: 568 p->transmission_mode = TRANSMISSION_MODE_2K; 569 break; 570 case 1: 571 p->transmission_mode = TRANSMISSION_MODE_4K; 572 break; 573 case 2: 574 p->transmission_mode = TRANSMISSION_MODE_8K; 575 break; 576 } 577 } 578 if (!(rc & 0x10)) { 579 switch (rc & 0x3) { 580 case 0: 581 p->guard_interval = GUARD_INTERVAL_1_4; 582 break; 583 case 1: 584 p->guard_interval = GUARD_INTERVAL_1_8; 585 break; 586 case 2: 587 p->guard_interval = GUARD_INTERVAL_1_16; 588 break; 589 } 590 } 591 } 592 593 if (fe->ops.i2c_gate_ctrl) 594 fe->ops.i2c_gate_ctrl(fe, 1); 595 596 return 0; 597 } 598 599 static int mb86a20s_tune(struct dvb_frontend *fe, 600 bool re_tune, 601 unsigned int mode_flags, 602 unsigned int *delay, 603 fe_status_t *status) 604 { 605 int rc = 0; 606 607 dprintk("\n"); 608 609 if (re_tune) 610 rc = mb86a20s_set_frontend(fe); 611 612 if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) 613 mb86a20s_read_status(fe, status); 614 615 return rc; 616 } 617 618 static void mb86a20s_release(struct dvb_frontend *fe) 619 { 620 struct mb86a20s_state *state = fe->demodulator_priv; 621 622 dprintk("\n"); 623 624 kfree(state); 625 } 626 627 static struct dvb_frontend_ops mb86a20s_ops; 628 629 struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, 630 struct i2c_adapter *i2c) 631 { 632 u8 rev; 633 634 /* allocate memory for the internal state */ 635 struct mb86a20s_state *state = 636 kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); 637 638 dprintk("\n"); 639 if (state == NULL) { 640 rc("Unable to kzalloc\n"); 641 goto error; 642 } 643 644 /* setup the state */ 645 state->config = config; 646 state->i2c = i2c; 647 648 /* create dvb_frontend */ 649 memcpy(&state->frontend.ops, &mb86a20s_ops, 650 sizeof(struct dvb_frontend_ops)); 651 state->frontend.demodulator_priv = state; 652 653 /* Check if it is a mb86a20s frontend */ 654 rev = mb86a20s_readreg(state, 0); 655 656 if (rev == 0x13) { 657 printk(KERN_INFO "Detected a Fujitsu mb86a20s frontend\n"); 658 } else { 659 printk(KERN_ERR "Frontend revision %d is unknown - aborting.\n", 660 rev); 661 goto error; 662 } 663 664 return &state->frontend; 665 666 error: 667 kfree(state); 668 return NULL; 669 } 670 EXPORT_SYMBOL(mb86a20s_attach); 671 672 static struct dvb_frontend_ops mb86a20s_ops = { 673 .delsys = { SYS_ISDBT }, 674 /* Use dib8000 values per default */ 675 .info = { 676 .name = "Fujitsu mb86A20s", 677 .caps = FE_CAN_INVERSION_AUTO | FE_CAN_RECOVER | 678 FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 679 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 680 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | 681 FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_QAM_AUTO | 682 FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, 683 /* Actually, those values depend on the used tuner */ 684 .frequency_min = 45000000, 685 .frequency_max = 864000000, 686 .frequency_stepsize = 62500, 687 }, 688 689 .release = mb86a20s_release, 690 691 .init = mb86a20s_initfe, 692 .set_frontend = mb86a20s_set_frontend, 693 .get_frontend = mb86a20s_get_frontend, 694 .read_status = mb86a20s_read_status, 695 .read_signal_strength = mb86a20s_read_signal_strength, 696 .tune = mb86a20s_tune, 697 }; 698 699 MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware"); 700 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); 701 MODULE_LICENSE("GPL"); 702