linux/drivers/media/usb/dvb-usb/cinergyT2-fe.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
   4 *
   5 * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
   6 *
   7 * Based on the dvb-usb-framework code and the
   8 * original Terratec Cinergy T2 driver by:
   9 *
  10 * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
  11 *                  Holger Waechtler <holger@qanu.de>
  12 *
  13 *  Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
  14 */
  15
  16#include "cinergyT2.h"
  17
  18
  19/*
  20 *  convert linux-dvb frontend parameter set into TPS.
  21 *  See ETSI ETS-300744, section 4.6.2, table 9 for details.
  22 *
  23 *  This function is probably reusable and may better get placed in a support
  24 *  library.
  25 *
  26 *  We replace erroneous fields by default TPS fields (the ones with value 0).
  27 */
  28
  29static uint16_t compute_tps(struct dtv_frontend_properties *op)
  30{
  31        uint16_t tps = 0;
  32
  33        switch (op->code_rate_HP) {
  34        case FEC_2_3:
  35                tps |= (1 << 7);
  36                break;
  37        case FEC_3_4:
  38                tps |= (2 << 7);
  39                break;
  40        case FEC_5_6:
  41                tps |= (3 << 7);
  42                break;
  43        case FEC_7_8:
  44                tps |= (4 << 7);
  45                break;
  46        case FEC_1_2:
  47        case FEC_AUTO:
  48        default:
  49                /* tps |= (0 << 7) */;
  50        }
  51
  52        switch (op->code_rate_LP) {
  53        case FEC_2_3:
  54                tps |= (1 << 4);
  55                break;
  56        case FEC_3_4:
  57                tps |= (2 << 4);
  58                break;
  59        case FEC_5_6:
  60                tps |= (3 << 4);
  61                break;
  62        case FEC_7_8:
  63                tps |= (4 << 4);
  64                break;
  65        case FEC_1_2:
  66        case FEC_AUTO:
  67        default:
  68                /* tps |= (0 << 4) */;
  69        }
  70
  71        switch (op->modulation) {
  72        case QAM_16:
  73                tps |= (1 << 13);
  74                break;
  75        case QAM_64:
  76                tps |= (2 << 13);
  77                break;
  78        case QPSK:
  79        default:
  80                /* tps |= (0 << 13) */;
  81        }
  82
  83        switch (op->transmission_mode) {
  84        case TRANSMISSION_MODE_8K:
  85                tps |= (1 << 0);
  86                break;
  87        case TRANSMISSION_MODE_2K:
  88        default:
  89                /* tps |= (0 << 0) */;
  90        }
  91
  92        switch (op->guard_interval) {
  93        case GUARD_INTERVAL_1_16:
  94                tps |= (1 << 2);
  95                break;
  96        case GUARD_INTERVAL_1_8:
  97                tps |= (2 << 2);
  98                break;
  99        case GUARD_INTERVAL_1_4:
 100                tps |= (3 << 2);
 101                break;
 102        case GUARD_INTERVAL_1_32:
 103        default:
 104                /* tps |= (0 << 2) */;
 105        }
 106
 107        switch (op->hierarchy) {
 108        case HIERARCHY_1:
 109                tps |= (1 << 10);
 110                break;
 111        case HIERARCHY_2:
 112                tps |= (2 << 10);
 113                break;
 114        case HIERARCHY_4:
 115                tps |= (3 << 10);
 116                break;
 117        case HIERARCHY_NONE:
 118        default:
 119                /* tps |= (0 << 10) */;
 120        }
 121
 122        return tps;
 123}
 124
 125struct cinergyt2_fe_state {
 126        struct dvb_frontend fe;
 127        struct dvb_usb_device *d;
 128
 129        unsigned char data[64];
 130        struct mutex data_mutex;
 131
 132        struct dvbt_get_status_msg status;
 133};
 134
 135static int cinergyt2_fe_read_status(struct dvb_frontend *fe,
 136                                    enum fe_status *status)
 137{
 138        struct cinergyt2_fe_state *state = fe->demodulator_priv;
 139        int ret;
 140
 141        mutex_lock(&state->data_mutex);
 142        state->data[0] = CINERGYT2_EP1_GET_TUNER_STATUS;
 143
 144        ret = dvb_usb_generic_rw(state->d, state->data, 1,
 145                                 state->data, sizeof(state->status), 0);
 146        if (!ret)
 147                memcpy(&state->status, state->data, sizeof(state->status));
 148        mutex_unlock(&state->data_mutex);
 149
 150        if (ret < 0)
 151                return ret;
 152
 153        *status = 0;
 154
 155        if (0xffff - le16_to_cpu(state->status.gain) > 30)
 156                *status |= FE_HAS_SIGNAL;
 157        if (state->status.lock_bits & (1 << 6))
 158                *status |= FE_HAS_LOCK;
 159        if (state->status.lock_bits & (1 << 5))
 160                *status |= FE_HAS_SYNC;
 161        if (state->status.lock_bits & (1 << 4))
 162                *status |= FE_HAS_CARRIER;
 163        if (state->status.lock_bits & (1 << 1))
 164                *status |= FE_HAS_VITERBI;
 165
 166        if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) !=
 167                        (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))
 168                *status &= ~FE_HAS_LOCK;
 169
 170        return 0;
 171}
 172
 173static int cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
 174{
 175        struct cinergyt2_fe_state *state = fe->demodulator_priv;
 176
 177        *ber = le32_to_cpu(state->status.viterbi_error_rate);
 178        return 0;
 179}
 180
 181static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
 182{
 183        struct cinergyt2_fe_state *state = fe->demodulator_priv;
 184
 185        *unc = le32_to_cpu(state->status.uncorrected_block_count);
 186        return 0;
 187}
 188
 189static int cinergyt2_fe_read_signal_strength(struct dvb_frontend *fe,
 190                                                u16 *strength)
 191{
 192        struct cinergyt2_fe_state *state = fe->demodulator_priv;
 193
 194        *strength = (0xffff - le16_to_cpu(state->status.gain));
 195        return 0;
 196}
 197
 198static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
 199{
 200        struct cinergyt2_fe_state *state = fe->demodulator_priv;
 201
 202        *snr = (state->status.snr << 8) | state->status.snr;
 203        return 0;
 204}
 205
 206static int cinergyt2_fe_init(struct dvb_frontend *fe)
 207{
 208        return 0;
 209}
 210
 211static int cinergyt2_fe_sleep(struct dvb_frontend *fe)
 212{
 213        deb_info("cinergyt2_fe_sleep() Called\n");
 214        return 0;
 215}
 216
 217static int cinergyt2_fe_get_tune_settings(struct dvb_frontend *fe,
 218                                struct dvb_frontend_tune_settings *tune)
 219{
 220        tune->min_delay_ms = 800;
 221        return 0;
 222}
 223
 224static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe)
 225{
 226        struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
 227        struct cinergyt2_fe_state *state = fe->demodulator_priv;
 228        struct dvbt_set_parameters_msg *param;
 229        int err;
 230
 231        mutex_lock(&state->data_mutex);
 232
 233        param = (void *)state->data;
 234        param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
 235        param->tps = cpu_to_le16(compute_tps(fep));
 236        param->freq = cpu_to_le32(fep->frequency / 1000);
 237        param->flags = 0;
 238
 239        switch (fep->bandwidth_hz) {
 240        default:
 241        case 8000000:
 242                param->bandwidth = 8;
 243                break;
 244        case 7000000:
 245                param->bandwidth = 7;
 246                break;
 247        case 6000000:
 248                param->bandwidth = 6;
 249                break;
 250        }
 251
 252        err = dvb_usb_generic_rw(state->d, state->data, sizeof(*param),
 253                                 state->data, 2, 0);
 254        if (err < 0)
 255                err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err);
 256
 257        mutex_unlock(&state->data_mutex);
 258        return (err < 0) ? err : 0;
 259}
 260
 261static void cinergyt2_fe_release(struct dvb_frontend *fe)
 262{
 263        struct cinergyt2_fe_state *state = fe->demodulator_priv;
 264        kfree(state);
 265}
 266
 267static const struct dvb_frontend_ops cinergyt2_fe_ops;
 268
 269struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d)
 270{
 271        struct cinergyt2_fe_state *s = kzalloc(sizeof(
 272                                        struct cinergyt2_fe_state), GFP_KERNEL);
 273        if (s == NULL)
 274                return NULL;
 275
 276        s->d = d;
 277        memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops));
 278        s->fe.demodulator_priv = s;
 279        mutex_init(&s->data_mutex);
 280        return &s->fe;
 281}
 282
 283
 284static const struct dvb_frontend_ops cinergyt2_fe_ops = {
 285        .delsys = { SYS_DVBT },
 286        .info = {
 287                .name                   = DRIVER_NAME,
 288                .frequency_min_hz       = 174 * MHz,
 289                .frequency_max_hz       = 862 * MHz,
 290                .frequency_stepsize_hz  = 166667,
 291                .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2
 292                        | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4
 293                        | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8
 294                        | FE_CAN_FEC_AUTO | FE_CAN_QPSK
 295                        | FE_CAN_QAM_16 | FE_CAN_QAM_64
 296                        | FE_CAN_QAM_AUTO
 297                        | FE_CAN_TRANSMISSION_MODE_AUTO
 298                        | FE_CAN_GUARD_INTERVAL_AUTO
 299                        | FE_CAN_HIERARCHY_AUTO
 300                        | FE_CAN_RECOVER
 301                        | FE_CAN_MUTE_TS
 302        },
 303
 304        .release                = cinergyt2_fe_release,
 305
 306        .init                   = cinergyt2_fe_init,
 307        .sleep                  = cinergyt2_fe_sleep,
 308
 309        .set_frontend           = cinergyt2_fe_set_frontend,
 310        .get_tune_settings      = cinergyt2_fe_get_tune_settings,
 311
 312        .read_status            = cinergyt2_fe_read_status,
 313        .read_ber               = cinergyt2_fe_read_ber,
 314        .read_signal_strength   = cinergyt2_fe_read_signal_strength,
 315        .read_snr               = cinergyt2_fe_read_snr,
 316        .read_ucblocks          = cinergyt2_fe_read_unc_blocks,
 317};
 318