1/* 2 * Abilis Systems Single DVB-T Receiver 3 * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> 4 * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20#include <linux/version.h> 21 22#include "as102_drv.h" 23#include "as10x_types.h" 24#include "as10x_cmd.h" 25 26static void as10x_fe_copy_tps_parameters(struct dtv_frontend_properties *dst, 27 struct as10x_tps *src); 28 29static void as102_fe_copy_tune_parameters(struct as10x_tune_args *dst, 30 struct dtv_frontend_properties *src); 31 32static int as102_fe_set_frontend(struct dvb_frontend *fe) 33{ 34 struct dtv_frontend_properties *p = &fe->dtv_property_cache; 35 int ret = 0; 36 struct as102_dev_t *dev; 37 struct as10x_tune_args tune_args = { 0 }; 38 39 ENTER(); 40 41 dev = (struct as102_dev_t *) fe->tuner_priv; 42 if (dev == NULL) 43 return -ENODEV; 44 45 if (mutex_lock_interruptible(&dev->bus_adap.lock)) 46 return -EBUSY; 47 48 as102_fe_copy_tune_parameters(&tune_args, p); 49 50 /* send abilis command: SET_TUNE */ 51 ret = as10x_cmd_set_tune(&dev->bus_adap, &tune_args); 52 if (ret != 0) 53 dprintk(debug, "as10x_cmd_set_tune failed. (err = %d)\n", ret); 54 55 mutex_unlock(&dev->bus_adap.lock); 56 57 LEAVE(); 58 return (ret < 0) ? -EINVAL : 0; 59} 60 61static int as102_fe_get_frontend(struct dvb_frontend *fe) 62{ 63 struct dtv_frontend_properties *p = &fe->dtv_property_cache; 64 int ret = 0; 65 struct as102_dev_t *dev; 66 struct as10x_tps tps = { 0 }; 67 68 ENTER(); 69 70 dev = (struct as102_dev_t *) fe->tuner_priv; 71 if (dev == NULL) 72 return -EINVAL; 73 74 if (mutex_lock_interruptible(&dev->bus_adap.lock)) 75 return -EBUSY; 76 77 /* send abilis command: GET_TPS */ 78 ret = as10x_cmd_get_tps(&dev->bus_adap, &tps); 79 80 if (ret == 0) 81 as10x_fe_copy_tps_parameters(p, &tps); 82 83 mutex_unlock(&dev->bus_adap.lock); 84 85 LEAVE(); 86 return (ret < 0) ? -EINVAL : 0; 87} 88 89static int as102_fe_get_tune_settings(struct dvb_frontend *fe, 90 struct dvb_frontend_tune_settings *settings) { 91 ENTER(); 92 93#if 0 94 dprintk(debug, "step_size = %d\n", settings->step_size); 95 dprintk(debug, "max_drift = %d\n", settings->max_drift); 96 dprintk(debug, "min_delay_ms = %d -> %d\n", settings->min_delay_ms, 97 1000); 98#endif 99 100 settings->min_delay_ms = 1000; 101 102 LEAVE(); 103 return 0; 104} 105 106 107static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) 108{ 109 int ret = 0; 110 struct as102_dev_t *dev; 111 struct as10x_tune_status tstate = { 0 }; 112 113 ENTER(); 114 115 dev = (struct as102_dev_t *) fe->tuner_priv; 116 if (dev == NULL) 117 return -ENODEV; 118 119 if (mutex_lock_interruptible(&dev->bus_adap.lock)) 120 return -EBUSY; 121 122 /* send abilis command: GET_TUNE_STATUS */ 123 ret = as10x_cmd_get_tune_status(&dev->bus_adap, &tstate); 124 if (ret < 0) { 125 dprintk(debug, "as10x_cmd_get_tune_status failed (err = %d)\n", 126 ret); 127 goto out; 128 } 129 130 dev->signal_strength = tstate.signal_strength; 131 dev->ber = tstate.BER; 132 133 switch (tstate.tune_state) { 134 case TUNE_STATUS_SIGNAL_DVB_OK: 135 *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; 136 break; 137 case TUNE_STATUS_STREAM_DETECTED: 138 *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC; 139 break; 140 case TUNE_STATUS_STREAM_TUNED: 141 *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | 142 FE_HAS_LOCK; 143 break; 144 default: 145 *status = TUNE_STATUS_NOT_TUNED; 146 } 147 148 dprintk(debug, "tuner status: 0x%02x, strength %d, per: %d, ber: %d\n", 149 tstate.tune_state, tstate.signal_strength, 150 tstate.PER, tstate.BER); 151 152 if (*status & FE_HAS_LOCK) { 153 if (as10x_cmd_get_demod_stats(&dev->bus_adap, 154 (struct as10x_demod_stats *) &dev->demod_stats) < 0) { 155 memset(&dev->demod_stats, 0, sizeof(dev->demod_stats)); 156 dprintk(debug, "as10x_cmd_get_demod_stats failed " 157 "(probably not tuned)\n"); 158 } else { 159 dprintk(debug, 160 "demod status: fc: 0x%08x, bad fc: 0x%08x, " 161 "bytes corrected: 0x%08x , MER: 0x%04x\n", 162 dev->demod_stats.frame_count, 163 dev->demod_stats.bad_frame_count, 164 dev->demod_stats.bytes_fixed_by_rs, 165 dev->demod_stats.mer); 166 } 167 } else { 168 memset(&dev->demod_stats, 0, sizeof(dev->demod_stats)); 169 } 170 171out: 172 mutex_unlock(&dev->bus_adap.lock); 173 LEAVE(); 174 return ret; 175} 176 177/* 178 * Note: 179 * - in AS102 SNR=MER 180 * - the SNR will be returned in linear terms, i.e. not in dB 181 * - the accuracy equals ±2dB for a SNR range from 4dB to 30dB 182 * - the accuracy is >2dB for SNR values outside this range 183 */ 184static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr) 185{ 186 struct as102_dev_t *dev; 187 188 ENTER(); 189 190 dev = (struct as102_dev_t *) fe->tuner_priv; 191 if (dev == NULL) 192 return -ENODEV; 193 194 *snr = dev->demod_stats.mer; 195 196 LEAVE(); 197 return 0; 198} 199 200static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber) 201{ 202 struct as102_dev_t *dev; 203 204 ENTER(); 205 206 dev = (struct as102_dev_t *) fe->tuner_priv; 207 if (dev == NULL) 208 return -ENODEV; 209 210 *ber = dev->ber; 211 212 LEAVE(); 213 return 0; 214} 215 216static int as102_fe_read_signal_strength(struct dvb_frontend *fe, 217 u16 *strength) 218{ 219 struct as102_dev_t *dev; 220 221 ENTER(); 222 223 dev = (struct as102_dev_t *) fe->tuner_priv; 224 if (dev == NULL) 225 return -ENODEV; 226 227 *strength = (((0xffff * 400) * dev->signal_strength + 41000) * 2); 228 229 LEAVE(); 230 return 0; 231} 232 233static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 234{ 235 struct as102_dev_t *dev; 236 237 ENTER(); 238 239 dev = (struct as102_dev_t *) fe->tuner_priv; 240 if (dev == NULL) 241 return -ENODEV; 242 243 if (dev->demod_stats.has_started) 244 *ucblocks = dev->demod_stats.bad_frame_count; 245 else 246 *ucblocks = 0; 247 248 LEAVE(); 249 return 0; 250} 251 252static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) 253{ 254 struct as102_dev_t *dev; 255 int ret; 256 257 ENTER(); 258 259 dev = (struct as102_dev_t *) fe->tuner_priv; 260 if (dev == NULL) 261 return -ENODEV; 262 263 if (mutex_lock_interruptible(&dev->bus_adap.lock)) 264 return -EBUSY; 265 266 if (acquire) { 267 if (elna_enable) 268 as10x_cmd_set_context(&dev->bus_adap, CONTEXT_LNA, dev->elna_cfg); 269 270 ret = as10x_cmd_turn_on(&dev->bus_adap); 271 } else { 272 ret = as10x_cmd_turn_off(&dev->bus_adap); 273 } 274 275 mutex_unlock(&dev->bus_adap.lock); 276 277 LEAVE(); 278 return ret; 279} 280 281static struct dvb_frontend_ops as102_fe_ops = { 282 .delsys = { SYS_DVBT }, 283 .info = { 284 .name = "Unknown AS102 device", 285 .frequency_min = 174000000, 286 .frequency_max = 862000000, 287 .frequency_stepsize = 166667, 288 .caps = FE_CAN_INVERSION_AUTO 289 | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 290 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO 291 | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK 292 | FE_CAN_QAM_AUTO 293 | FE_CAN_TRANSMISSION_MODE_AUTO 294 | FE_CAN_GUARD_INTERVAL_AUTO 295 | FE_CAN_HIERARCHY_AUTO 296 | FE_CAN_RECOVER 297 | FE_CAN_MUTE_TS 298 }, 299 300 .set_frontend = as102_fe_set_frontend, 301 .get_frontend = as102_fe_get_frontend, 302 .get_tune_settings = as102_fe_get_tune_settings, 303 304 .read_status = as102_fe_read_status, 305 .read_snr = as102_fe_read_snr, 306 .read_ber = as102_fe_read_ber, 307 .read_signal_strength = as102_fe_read_signal_strength, 308 .read_ucblocks = as102_fe_read_ucblocks, 309 .ts_bus_ctrl = as102_fe_ts_bus_ctrl, 310}; 311 312int as102_dvb_unregister_fe(struct dvb_frontend *fe) 313{ 314 /* unregister frontend */ 315 dvb_unregister_frontend(fe); 316 317 /* detach frontend */ 318 dvb_frontend_detach(fe); 319 320 return 0; 321} 322 323int as102_dvb_register_fe(struct as102_dev_t *as102_dev, 324 struct dvb_frontend *dvb_fe) 325{ 326 int errno; 327 struct dvb_adapter *dvb_adap; 328 329 if (as102_dev == NULL) 330 return -EINVAL; 331 332 /* extract dvb_adapter */ 333 dvb_adap = &as102_dev->dvb_adap; 334 335 /* init frontend callback ops */ 336 memcpy(&dvb_fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops)); 337 strncpy(dvb_fe->ops.info.name, as102_dev->name, 338 sizeof(dvb_fe->ops.info.name)); 339 340 /* register dvb frontend */ 341 errno = dvb_register_frontend(dvb_adap, dvb_fe); 342 if (errno == 0) 343 dvb_fe->tuner_priv = as102_dev; 344 345 return errno; 346} 347 348static void as10x_fe_copy_tps_parameters(struct dtv_frontend_properties *fe_tps, 349 struct as10x_tps *as10x_tps) 350{ 351 352 /* extract constellation */ 353 switch (as10x_tps->modulation) { 354 case CONST_QPSK: 355 fe_tps->modulation = QPSK; 356 break; 357 case CONST_QAM16: 358 fe_tps->modulation = QAM_16; 359 break; 360 case CONST_QAM64: 361 fe_tps->modulation = QAM_64; 362 break; 363 } 364 365 /* extract hierarchy */ 366 switch (as10x_tps->hierarchy) { 367 case HIER_NONE: 368 fe_tps->hierarchy = HIERARCHY_NONE; 369 break; 370 case HIER_ALPHA_1: 371 fe_tps->hierarchy = HIERARCHY_1; 372 break; 373 case HIER_ALPHA_2: 374 fe_tps->hierarchy = HIERARCHY_2; 375 break; 376 case HIER_ALPHA_4: 377 fe_tps->hierarchy = HIERARCHY_4; 378 break; 379 } 380 381 /* extract code rate HP */ 382 switch (as10x_tps->code_rate_HP) { 383 case CODE_RATE_1_2: 384 fe_tps->code_rate_HP = FEC_1_2; 385 break; 386 case CODE_RATE_2_3: 387 fe_tps->code_rate_HP = FEC_2_3; 388 break; 389 case CODE_RATE_3_4: 390 fe_tps->code_rate_HP = FEC_3_4; 391 break; 392 case CODE_RATE_5_6: 393 fe_tps->code_rate_HP = FEC_5_6; 394 break; 395 case CODE_RATE_7_8: 396 fe_tps->code_rate_HP = FEC_7_8; 397 break; 398 } 399 400 /* extract code rate LP */ 401 switch (as10x_tps->code_rate_LP) { 402 case CODE_RATE_1_2: 403 fe_tps->code_rate_LP = FEC_1_2; 404 break; 405 case CODE_RATE_2_3: 406 fe_tps->code_rate_LP = FEC_2_3; 407 break; 408 case CODE_RATE_3_4: 409 fe_tps->code_rate_LP = FEC_3_4; 410 break; 411 case CODE_RATE_5_6: 412 fe_tps->code_rate_LP = FEC_5_6; 413 break; 414 case CODE_RATE_7_8: 415 fe_tps->code_rate_LP = FEC_7_8; 416 break; 417 } 418 419 /* extract guard interval */ 420 switch (as10x_tps->guard_interval) { 421 case GUARD_INT_1_32: 422 fe_tps->guard_interval = GUARD_INTERVAL_1_32; 423 break; 424 case GUARD_INT_1_16: 425 fe_tps->guard_interval = GUARD_INTERVAL_1_16; 426 break; 427 case GUARD_INT_1_8: 428 fe_tps->guard_interval = GUARD_INTERVAL_1_8; 429 break; 430 case GUARD_INT_1_4: 431 fe_tps->guard_interval = GUARD_INTERVAL_1_4; 432 break; 433 } 434 435 /* extract transmission mode */ 436 switch (as10x_tps->transmission_mode) { 437 case TRANS_MODE_2K: 438 fe_tps->transmission_mode = TRANSMISSION_MODE_2K; 439 break; 440 case TRANS_MODE_8K: 441 fe_tps->transmission_mode = TRANSMISSION_MODE_8K; 442 break; 443 } 444} 445 446static uint8_t as102_fe_get_code_rate(fe_code_rate_t arg) 447{ 448 uint8_t c; 449 450 switch (arg) { 451 case FEC_1_2: 452 c = CODE_RATE_1_2; 453 break; 454 case FEC_2_3: 455 c = CODE_RATE_2_3; 456 break; 457 case FEC_3_4: 458 c = CODE_RATE_3_4; 459 break; 460 case FEC_5_6: 461 c = CODE_RATE_5_6; 462 break; 463 case FEC_7_8: 464 c = CODE_RATE_7_8; 465 break; 466 default: 467 c = CODE_RATE_UNKNOWN; 468 break; 469 } 470 471 return c; 472} 473 474static void as102_fe_copy_tune_parameters(struct as10x_tune_args *tune_args, 475 struct dtv_frontend_properties *params) 476{ 477 478 /* set frequency */ 479 tune_args->freq = params->frequency / 1000; 480 481 /* fix interleaving_mode */ 482 tune_args->interleaving_mode = INTLV_NATIVE; 483 484 switch (params->bandwidth_hz) { 485 case 8000000: 486 tune_args->bandwidth = BW_8_MHZ; 487 break; 488 case 7000000: 489 tune_args->bandwidth = BW_7_MHZ; 490 break; 491 case 6000000: 492 tune_args->bandwidth = BW_6_MHZ; 493 break; 494 default: 495 tune_args->bandwidth = BW_8_MHZ; 496 } 497 498 switch (params->guard_interval) { 499 case GUARD_INTERVAL_1_32: 500 tune_args->guard_interval = GUARD_INT_1_32; 501 break; 502 case GUARD_INTERVAL_1_16: 503 tune_args->guard_interval = GUARD_INT_1_16; 504 break; 505 case GUARD_INTERVAL_1_8: 506 tune_args->guard_interval = GUARD_INT_1_8; 507 break; 508 case GUARD_INTERVAL_1_4: 509 tune_args->guard_interval = GUARD_INT_1_4; 510 break; 511 case GUARD_INTERVAL_AUTO: 512 default: 513 tune_args->guard_interval = GUARD_UNKNOWN; 514 break; 515 } 516 517 switch (params->modulation) { 518 case QPSK: 519 tune_args->modulation = CONST_QPSK; 520 break; 521 case QAM_16: 522 tune_args->modulation = CONST_QAM16; 523 break; 524 case QAM_64: 525 tune_args->modulation = CONST_QAM64; 526 break; 527 default: 528 tune_args->modulation = CONST_UNKNOWN; 529 break; 530 } 531 532 switch (params->transmission_mode) { 533 case TRANSMISSION_MODE_2K: 534 tune_args->transmission_mode = TRANS_MODE_2K; 535 break; 536 case TRANSMISSION_MODE_8K: 537 tune_args->transmission_mode = TRANS_MODE_8K; 538 break; 539 default: 540 tune_args->transmission_mode = TRANS_MODE_UNKNOWN; 541 } 542 543 switch (params->hierarchy) { 544 case HIERARCHY_NONE: 545 tune_args->hierarchy = HIER_NONE; 546 break; 547 case HIERARCHY_1: 548 tune_args->hierarchy = HIER_ALPHA_1; 549 break; 550 case HIERARCHY_2: 551 tune_args->hierarchy = HIER_ALPHA_2; 552 break; 553 case HIERARCHY_4: 554 tune_args->hierarchy = HIER_ALPHA_4; 555 break; 556 case HIERARCHY_AUTO: 557 tune_args->hierarchy = HIER_UNKNOWN; 558 break; 559 } 560 561 dprintk(debug, "tuner parameters: freq: %d bw: 0x%02x gi: 0x%02x\n", 562 params->frequency, 563 tune_args->bandwidth, 564 tune_args->guard_interval); 565 566 /* 567 * Detect a hierarchy selection 568 * if HP/LP are both set to FEC_NONE, HP will be selected. 569 */ 570 if ((tune_args->hierarchy != HIER_NONE) && 571 ((params->code_rate_LP == FEC_NONE) || 572 (params->code_rate_HP == FEC_NONE))) { 573 574 if (params->code_rate_LP == FEC_NONE) { 575 tune_args->hier_select = HIER_HIGH_PRIORITY; 576 tune_args->code_rate = 577 as102_fe_get_code_rate(params->code_rate_HP); 578 } 579 580 if (params->code_rate_HP == FEC_NONE) { 581 tune_args->hier_select = HIER_LOW_PRIORITY; 582 tune_args->code_rate = 583 as102_fe_get_code_rate(params->code_rate_LP); 584 } 585 586 dprintk(debug, "\thierarchy: 0x%02x " 587 "selected: %s code_rate_%s: 0x%02x\n", 588 tune_args->hierarchy, 589 tune_args->hier_select == HIER_HIGH_PRIORITY ? 590 "HP" : "LP", 591 tune_args->hier_select == HIER_HIGH_PRIORITY ? 592 "HP" : "LP", 593 tune_args->code_rate); 594 } else { 595 tune_args->code_rate = 596 as102_fe_get_code_rate(params->code_rate_HP); 597 } 598} 599