diff --git a/README.md b/README.md index 643775a7aaec7..743826a977865 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ It contains patches for the following drivers * stv091x: tbs5927 * tas2101 (incomplete): tbs5990, tbs6904 * si2183 based cards: tbs6504 +* m88ds3103: pctv 461e v1(B6H9)/Hauppauge wintv-nova-s2 v1.4(B9H9) to support blindscan, to fix bugs and to make improvements. diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 293c80010a915..490aaccdec578 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -761,7 +761,7 @@ static int dvb_frontend_thread(void *data) if (fe->ops.scan) fe->ops.scan(fe, fepriv->state & FESTATE_SCAN_NEXT , &fepriv->delay, &s); fepriv->state = FESTATE_IDLE; - } else if (fepriv->state & FESTATE_GETTING_SPECTRUM) { + } else if (fepriv->state & FESTATE_GETTING_SPECTRUM) { dev_dbg(fe->dvb->device, "%s: Spectrum requested, DTV_SPECTRUM\n", __func__); dprintk("starting spectrum scan\n"); if (fe->ops.spectrum_start) @@ -792,7 +792,31 @@ static int dvb_frontend_thread(void *data) break; case DVBFE_ALGO_SW: dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__); - dvb_frontend_swzigzag(fe); + + if (fepriv->state & FESTATE_SCANNING) { + dev_dbg(fe->dvb->device, "%s: Sscan requested, FESTATE_SCANNING\n", __func__); + if (fe->ops.scan) + fe->ops.scan(fe, fepriv->state & FESTATE_SCAN_NEXT , &fepriv->delay, &s); + fepriv->state = FESTATE_IDLE; + } else if (fepriv->state & FESTATE_GETTING_SPECTRUM) { + dev_dbg(fe->dvb->device, "%s: Spectrum requested, DTV_SPECTRUM\n", __func__); + dprintk("starting spectrum scan\n"); + if (fe->ops.spectrum_start) + fe->ops.spectrum_start(fe, &fepriv->spectrum, &fepriv->delay, &s); + dprintk("spectrum scan ended s=0x%x\n", s); + fepriv->state = FESTATE_IDLE; + } else { + dvb_frontend_swzigzag(fe); + fe->ops.read_status(fe, &s); + } + if ((s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) + || (fepriv->heartbeat_interval>0)) { + //dprintk("Adding event val=0x%x\n", s); + dev_dbg(fe->dvb->device, "%s: state changed, adding current state\n", __func__); + dvb_frontend_add_event(fe, s); + fepriv->status = s; + } + atomic_set(&fe->algo_state.task_should_stop, false); break; case DVBFE_ALGO_CUSTOM: dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state); @@ -983,6 +1007,7 @@ static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, /* If the standard is for satellite, convert frequencies to kHz */ switch (c->delivery_system) { + case SYS_AUTO: case SYS_DVBS: case SYS_DVBS2: case SYS_TURBO: @@ -1008,6 +1033,7 @@ static u32 dvb_frontend_get_stepsize(struct dvb_frontend *fe) u32 step = max(fe_step, tuner_step); switch (c->delivery_system) { + case SYS_AUTO: case SYS_DVBS: case SYS_DVBS2: case SYS_TURBO: @@ -1035,17 +1061,20 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe) dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max, NULL); if ((freq_min && c->frequency < freq_min) || (freq_max && c->frequency > freq_max)) { - dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", + if (c->frequency != 0) { + dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", fe->dvb->num, fe->id, c->frequency, freq_min, freq_max); - dprintk( "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", + dprintk( "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", fe->dvb->num, fe->id, c->frequency, freq_min, freq_max); - return -EINVAL; + return -EINVAL; + } } /* range check: symbol rate */ switch (c->delivery_system) { + case SYS_AUTO: case SYS_DVBS: case SYS_DVBS2: case SYS_TURBO: @@ -1055,15 +1084,17 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe) c->symbol_rate < fe->ops.info.symbol_rate_min) || (fe->ops.info.symbol_rate_max && c->symbol_rate > fe->ops.info.symbol_rate_max)) { - dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n", + if (c->symbol_rate != 0) { + dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n", fe->dvb->num, fe->id, c->symbol_rate, fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max); - dprintk("DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n", + dprintk("DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n", fe->dvb->num, fe->id, c->symbol_rate, fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max); - return -EINVAL; + return -EINVAL; + } } default: break; @@ -1119,6 +1150,7 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe) c->scrambling_sequence_index = 0;/* default sequence */ switch (c->delivery_system) { + case SYS_AUTO: case SYS_DVBS: case SYS_DVBS2: case SYS_TURBO: @@ -2624,6 +2656,7 @@ static int dtv_set_frontend(struct dvb_frontend *fe) case SYS_ISDBS: rolloff = 135; break; + case SYS_AUTO: case SYS_DVBS2: switch (c->rolloff) { case ROLLOFF_20: @@ -2663,6 +2696,7 @@ static int dtv_set_frontend(struct dvb_frontend *fe) } else { /* default values */ switch (c->delivery_system) { + case SYS_AUTO: case SYS_DVBS: case SYS_DVBS2: case SYS_ISDBS: @@ -2738,6 +2772,7 @@ static int dtv_get_pls_search_list(struct dvb_frontend *fe, struct dtv_pls_searc static int dtv_set_pls_search_list(struct dvb_frontend *fe, struct dtv_pls_search_list* user) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int i; c->pls_search_codes_len = sizeof(c->pls_search_codes)/sizeof(c->pls_search_codes[0]); if(user->num_codes < c->pls_search_codes_len) c->pls_search_codes_len = user->num_codes; @@ -2749,7 +2784,7 @@ static int dtv_set_pls_search_list(struct dvb_frontend *fe, struct dtv_pls_searc dprintk("ERROR: len=%d/%d codes=%p", c->pls_search_codes_len, user->num_codes, user->codes); return -EFAULT; } - int i; + dprintk("PLS: %d codes:\n", c->pls_search_codes_len); for(i=0;i< c->pls_search_codes_len;++i) dprintk("code=0x%x\n", c->pls_search_codes[i]); diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 9d03a23e5c83f..abfb14fd325ef 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -20,7 +20,7 @@ drxd-objs := drxd_firm.o drxd_hard.o cxd2820r-objs := cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o drxk-objs := drxk_hard.o stv091x-objs := stv091x_chip.o stv091x_drv.o - +m88ds3103-objs := m88ds3103_drv.o neumo-scan.o obj-$(CONFIG_DVB_PLL) += dvb-pll.o obj-$(CONFIG_DVB_STV0299) += stv0299.o obj-$(CONFIG_DVB_STB0899) += stb0899.o diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index 57f52c004a233..fff725de45217 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -10,17 +10,133 @@ struct a8293_dev { struct i2c_client *client; u8 reg[2]; + int volt_slew_nanos_per_mv; }; -static int a8293_set_voltage(struct dvb_frontend *fe, - enum fe_sec_voltage fe_sec_voltage) +/* + * When increasing voltage, do so in minimal steps over time, minimizing risk of vIN undervoltage. + */ + +static int a8293_set_voltage_slew(struct a8293_dev *dev, struct i2c_client *client, + enum fe_sec_voltage fe_sec_voltage, int min_nanos_per_mv) +{ + int ret; + u8 reg0, reg1; + int new_volt_idx; + const int idx_to_mv[] = {0, 12709, 13042, 13375, 14042, 15042, 18042, 18709, 19042}; + const u8 idx_to_reg[] = {0x00, 0x20, 0x21, 0x22, 0x24, 0x27, 0x28, 0x2A, 0x2B }; + int this_volt_idx; + u8 status; + int prev_volt_idx; + + dev_dbg(&client->dev, "set_voltage_slew fe_sec_voltage=%d\n", fe_sec_voltage); + + /* Read status register to clear any stale faults. */ + ret = i2c_master_recv(client, &status, 1); + if (ret < 0) + goto err; + + /* Determine previous voltage */ + switch (dev->reg[0] & 0x2F) { + case 0x00: + prev_volt_idx = 0; + break; + case 0x20: + prev_volt_idx = 1; + break; + case 0x21: + prev_volt_idx = 2; + break; + case 0x22: + prev_volt_idx = 3; + break; + case 0x24: + prev_volt_idx = 4; + break; + case 0x27: + prev_volt_idx = 5; + break; + case 0x28: + prev_volt_idx = 6; + break; + case 0x2A: + prev_volt_idx = 7; + break; + case 0x2B: + prev_volt_idx = 8; + break; + default: + prev_volt_idx = 0; + } + + /* Determine new voltage */ + switch (fe_sec_voltage) { + case SEC_VOLTAGE_OFF: + new_volt_idx = 0; + break; + case SEC_VOLTAGE_13: + new_volt_idx = 2; + break; + case SEC_VOLTAGE_18: + new_volt_idx = 6; + break; + default: + ret = -EINVAL; + goto err; + } + + /* Slew to new voltage if new voltage is greater than current voltage */ + this_volt_idx = prev_volt_idx; + if (this_volt_idx < new_volt_idx) { + while (this_volt_idx < new_volt_idx) { + int delta_mv = idx_to_mv[this_volt_idx+1] - idx_to_mv[this_volt_idx]; + int min_wait_time = delta_mv * min_nanos_per_mv; + + reg0 = idx_to_reg[this_volt_idx+1]; + reg0 |= A8293_FLAG_ODT; + + ret = i2c_master_send(client, ®0, 1); + if (ret < 0) + goto err; + dev->reg[0] = reg0; + this_volt_idx++; + usleep_range(min_wait_time, min_wait_time * 2); + } + } else { /* Else just set the voltage */ + reg0 = idx_to_reg[new_volt_idx]; + reg0 |= A8293_FLAG_ODT; + ret = i2c_master_send(client, ®0, 1); + if (ret < 0) + goto err; + dev->reg[0] = reg0; + } + + /* TMODE=0, TGATE=1 */ + reg1 = 0x82; + if (reg1 != dev->reg[1]) { + ret = i2c_master_send(client, ®1, 1); + if (ret < 0) + goto err; + dev->reg[1] = reg1; + } + + usleep_range(1500, 5000); + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int a8293_set_voltage_noslew(struct dvb_frontend *fe, + enum fe_sec_voltage fe_sec_voltage) { struct a8293_dev *dev = fe->sec_priv; struct i2c_client *client = dev->client; int ret; u8 reg0, reg1; - dev_dbg(&client->dev, "fe_sec_voltage=%d\n", fe_sec_voltage); + dev_dbg(&client->dev, "set_voltage_noslew fe_sec_voltage=%d\n", fe_sec_voltage); switch (fe_sec_voltage) { case SEC_VOLTAGE_OFF: @@ -62,6 +178,24 @@ static int a8293_set_voltage(struct dvb_frontend *fe, return ret; } +static int a8293_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage fe_sec_voltage) +{ + struct a8293_dev *dev = fe->sec_priv; + struct i2c_client *client = dev->client; + int volt_slew_nanos_per_mv = dev->volt_slew_nanos_per_mv; + + dev_dbg(&client->dev, "set_voltage volt_slew_nanos_per_mv=%d\n", volt_slew_nanos_per_mv); + + /* Use slew version if slew rate is set to a sane value */ + if (volt_slew_nanos_per_mv > 0 && volt_slew_nanos_per_mv < 1600) + a8293_set_voltage_slew(dev, client, fe_sec_voltage, volt_slew_nanos_per_mv); + else + a8293_set_voltage_noslew(fe, fe_sec_voltage); + + return 0; +} + static int a8293_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -78,6 +212,7 @@ static int a8293_probe(struct i2c_client *client, } dev->client = client; + dev->volt_slew_nanos_per_mv = pdata->volt_slew_nanos_per_mv; /* check if the SEC is there */ ret = i2c_master_recv(client, buf, 2); diff --git a/drivers/media/dvb-frontends/a8293.h b/drivers/media/dvb-frontends/a8293.h index 8c09635ef8a49..5ead89606532c 100644 --- a/drivers/media/dvb-frontends/a8293.h +++ b/drivers/media/dvb-frontends/a8293.h @@ -8,6 +8,8 @@ #ifndef A8293_H #define A8293_H +#define A8293_FLAG_ODT 0x10 + #include /* @@ -18,9 +20,11 @@ /** * struct a8293_platform_data - Platform data for the a8293 driver * @dvb_frontend: DVB frontend. + * @volt_slew_nanos_per_mv: Slew rate when increasing LNB voltage, in nanoseconds per millivolt. */ struct a8293_platform_data { struct dvb_frontend *dvb_frontend; + int volt_slew_nanos_per_mv; }; #endif /* A8293_H */ diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h index 6b1b2637dab2b..c0c41cf394808 100644 --- a/drivers/media/dvb-frontends/m88ds3103.h +++ b/drivers/media/dvb-frontends/m88ds3103.h @@ -10,6 +10,11 @@ #include +bool blind; +bool scandone; +u16 tpcnt; +u16 tpnum; + /* * I2C address * 0x68, diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103_drv.c similarity index 60% rename from drivers/media/dvb-frontends/m88ds3103.c rename to drivers/media/dvb-frontends/m88ds3103_drv.c index 02e8aa11e36e7..f445a8407a7b3 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103_drv.c @@ -5,6 +5,7 @@ * Copyright (C) 2013 Antti Palosaari */ +#include #include "m88ds3103_priv.h" static const struct dvb_frontend_ops m88ds3103_ops; @@ -166,6 +167,47 @@ int m88ds3103_get_agc_pwm(struct dvb_frontend *fe, u8 *_agc_pwm) } EXPORT_SYMBOL(m88ds3103_get_agc_pwm); +static int m88ds3103_set_carrier_offset(struct dvb_frontend *fe, s16 lpfoffset) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 tuner_frequency_khz; + s32 s32tmp; + u8 buf[2]; + int ret; + + if (fe->ops.tuner_ops.get_frequency) { + ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency_khz); + if (ret) + goto err; + } else { + /* + * Use nominal target frequency as tuner driver does not provide + * actual frequency used. Carrier offset calculation is not + * valid. + */ + tuner_frequency_khz = c->frequency; + } + + /* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */ + s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency - lpfoffset); + s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000); + buf[0] = (s32tmp >> 0) & 0xff; + buf[1] = (s32tmp >> 8) & 0xff; + ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2); + if (ret) + goto err; + + dev_dbg(&client->dev, "carrier offset=%d\n", + (tuner_frequency_khz - c->frequency - lpfoffset)); + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + static int m88ds3103_read_status(struct dvb_frontend *fe, enum fe_status *status) { @@ -173,7 +215,7 @@ static int m88ds3103_read_status(struct dvb_frontend *fe, struct i2c_client *client = dev->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i, itmp; - unsigned int utmp; + u32 utmp, utmp1, cnr, noise, signal, noise_tot, signal_tot, post_bit_error, post_bit_count; u8 buf[3]; *status = 0; @@ -183,26 +225,39 @@ static int m88ds3103_read_status(struct dvb_frontend *fe, goto err; } + if (c->delivery_system == SYS_AUTO) + c->delivery_system = dev->delivery_system; + switch (c->delivery_system) { case SYS_DVBS: - ret = regmap_read(dev->regmap, 0xd1, &utmp); + ret = regmap_read(dev->regmap, 0x0d, &utmp); if (ret) goto err; - if ((utmp & 0x07) == 0x07) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | - FE_HAS_LOCK; + if ((utmp & 0x01) == 0x01) + *status = FE_HAS_SIGNAL; + if ((utmp & 0x02) == 0x02) + *status |= FE_HAS_LOCK; + if ((utmp & 0x04) == 0x04) { + *status |= FE_HAS_CARRIER; + *status |= FE_HAS_SYNC; + *status |= FE_HAS_VITERBI; + } break; case SYS_DVBS2: ret = regmap_read(dev->regmap, 0x0d, &utmp); if (ret) goto err; - - if ((utmp & 0x8f) == 0x8f) - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | - FE_HAS_VITERBI | FE_HAS_SYNC | - FE_HAS_LOCK; + if ((utmp & 0x01) == 0x01) + *status = FE_HAS_SIGNAL; + if ((utmp & 0x02) == 0x02) + *status |= FE_HAS_LOCK; + if ((utmp & 0x04) == 0x04) + *status |= FE_HAS_CARRIER; + if ((utmp & 0x08) == 0x08) + *status |= FE_HAS_SYNC; + if ((utmp & 0x80) == 0x80) + *status |= FE_HAS_VITERBI; break; default: dev_dbg(&client->dev, "invalid delivery_system\n"); @@ -214,13 +269,8 @@ static int m88ds3103_read_status(struct dvb_frontend *fe, dev_dbg(&client->dev, "lock=%02x status=%02x\n", utmp, *status); /* CNR */ - if (dev->fe_status & FE_HAS_VITERBI) { - unsigned int cnr, noise, signal, noise_tot, signal_tot; - + if (dev->fe_status & FE_HAS_LOCK) { cnr = 0; - /* more iterations for more accurate estimation */ - #define M88DS3103_SNR_ITERATIONS 3 - switch (c->delivery_system) { case SYS_DVBS: itmp = 0; @@ -284,9 +334,7 @@ static int m88ds3103_read_status(struct dvb_frontend *fe, } /* BER */ - if (dev->fe_status & FE_HAS_LOCK) { - unsigned int utmp, post_bit_error, post_bit_count; - + if (dev->fe_status & FE_HAS_VITERBI) { switch (c->delivery_system) { case SYS_DVBS: ret = regmap_write(dev->regmap, 0xf9, 0x04); @@ -297,60 +345,91 @@ static int m88ds3103_read_status(struct dvb_frontend *fe, if (ret) goto err; - /* measurement ready? */ if (!(utmp & 0x10)) { ret = regmap_bulk_read(dev->regmap, 0xf6, buf, 2); if (ret) goto err; post_bit_error = buf[1] << 8 | buf[0] << 0; - post_bit_count = 0x800000; - dev->post_bit_error += post_bit_error; - dev->post_bit_count += post_bit_count; + post_bit_count = (!(utmp & 0x08)) ? 0x800000 : 0x100000; + dev->post_bit_error = post_bit_error; + dev->post_bit_count = post_bit_count; dev->dvbv3_ber = post_bit_error; - /* restart measurement */ - utmp |= 0x10; - ret = regmap_write(dev->regmap, 0xf8, utmp); + ret = regmap_write(dev->regmap, 0xf8, utmp | 0x10); if (ret) goto err; } break; case SYS_DVBS2: + switch (c->fec_inner) { + case FEC_1_4: + utmp1 = 15928; + break; + case FEC_1_3: + utmp1 = 21328; + break; + case FEC_2_5: + utmp1 = 25648; + break; + case FEC_1_2: + utmp1 = 32128; + break; + case FEC_3_5: + utmp1 = 38608; + break; + case FEC_2_3: + utmp1 = 42960; + break; + case FEC_3_4: + utmp1 = 48328; + break; + case FEC_4_5: + utmp1 = 51568; + break; + case FEC_5_6: + utmp1 = 53760; + break; + case FEC_8_9: + utmp1 = 57392; + break; + case FEC_9_10: + utmp1 = 58112; + break; + default: + break; + } + ret = regmap_bulk_read(dev->regmap, 0xd5, buf, 3); if (ret) goto err; - utmp = buf[2] << 16 | buf[1] << 8 | buf[0] << 0; + utmp = buf[2] << 16 | buf[1] << 8 | buf[0]; - /* enough data? */ - if (utmp > 4000) { - ret = regmap_bulk_read(dev->regmap, 0xf7, buf, 2); - if (ret) - goto err; + ret = regmap_bulk_read(dev->regmap, 0xf7, buf, 2); + if (ret) + goto err; - post_bit_error = buf[1] << 8 | buf[0] << 0; - post_bit_count = 32 * utmp; /* TODO: FEC */ - dev->post_bit_error += post_bit_error; - dev->post_bit_count += post_bit_count; - dev->dvbv3_ber = post_bit_error; + post_bit_error = buf[1] << 8 | buf[0]; + post_bit_count = utmp * utmp1 / 1504; - /* restart measurement */ + if (utmp > 3000) { ret = regmap_write(dev->regmap, 0xd1, 0x01); if (ret) goto err; - ret = regmap_write(dev->regmap, 0xf9, 0x01); if (ret) goto err; - ret = regmap_write(dev->regmap, 0xf9, 0x00); if (ret) goto err; - ret = regmap_write(dev->regmap, 0xd1, 0x00); if (ret) goto err; + + dev->post_bit_error = post_bit_error; + dev->post_bit_count = post_bit_count; + dev->dvbv3_ber = post_bit_error; } break; default: @@ -374,6 +453,67 @@ static int m88ds3103_read_status(struct dvb_frontend *fe, return ret; } +static int m88ds3103_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + unsigned tmp1, tmp2, tmp3; + u16 uc_blocks; + int ret; + + if (c->delivery_system == SYS_AUTO) + c->delivery_system = dev->delivery_system; + + switch (c->delivery_system) { + case SYS_DVBS: + ret = regmap_read(dev->regmap, 0xf5, &tmp1); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0xf4, &tmp2); + if (ret) + goto err; + *ucblocks = tmp1 << 8 | tmp2; + ret = regmap_read(dev->regmap, 0xf8, &tmp3); + if (ret) + goto err; + /* clear packet counters */ + tmp3 &= ~0x20; + ret = regmap_write(dev->regmap, 0xf8, tmp3); + if (ret) + goto err; + /* enable packet counters */ + tmp3 |= 0x20; + ret = regmap_write(dev->regmap, 0xf8, tmp3); + if (ret) + goto err; + break; + case SYS_DVBS2: + ret = regmap_read(dev->regmap, 0xe2, &tmp1); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0xe1, &tmp2); + if (ret) + goto err; + uc_blocks = tmp1 << 8 | tmp2; + if (uc_blocks > dev->prevUCBS2) + *ucblocks = uc_blocks - dev->prevUCBS2; + else + *ucblocks = dev->prevUCBS2 - uc_blocks; + dev->prevUCBS2 = uc_blocks; + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev) { struct i2c_client *client = dev->client; @@ -619,175 +759,164 @@ static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz) return 0; } -static int m88ds3103_set_frontend(struct dvb_frontend *fe) +static int m88ds3103_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *c) { struct m88ds3103_dev *dev = fe->demodulator_priv; struct i2c_client *client = dev->client; - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - int ret, len; - const struct m88ds3103_reg_val *init; - u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0; /* silence compiler warning */ + int ret; u8 buf[3]; - u16 u16tmp; - u32 tuner_frequency_khz, target_mclk, u32tmp; - s32 s32tmp; - static const struct reg_sequence reset_buf[] = { - {0x07, 0x80}, {0x07, 0x00} - }; - dev_dbg(&client->dev, - "delivery_system=%d modulation=%d frequency=%u symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", - c->delivery_system, c->modulation, c->frequency, c->symbol_rate, - c->inversion, c->pilot, c->rolloff); - - if (!dev->warm) { - ret = -EAGAIN; - goto err; - } + dev_dbg(&client->dev, "\n"); - /* reset */ - ret = regmap_multi_reg_write(dev->regmap, reset_buf, 2); - if (ret) + if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { + ret = 0; goto err; - - /* Disable demod clock path */ - if (dev->chip_id == M88RS6000_CHIP_ID) { - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { - ret = regmap_read(dev->regmap, 0xb2, &u32tmp); - if (ret) - goto err; - if (u32tmp == 0x01) { - ret = regmap_write(dev->regmap, 0x00, 0x00); - if (ret) - goto err; - ret = regmap_write(dev->regmap, 0xb2, 0x00); - if (ret) - goto err; - } - } - - ret = regmap_write(dev->regmap, 0x06, 0xe0); - if (ret) - goto err; } - /* program tuner */ - if (fe->ops.tuner_ops.set_params) { - ret = fe->ops.tuner_ops.set_params(fe); - if (ret) - goto err; - } + if (c->delivery_system == SYS_AUTO) + c->delivery_system = dev->delivery_system; - if (fe->ops.tuner_ops.get_frequency) { - ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency_khz); + switch (c->delivery_system) { + case SYS_DVBS: + ret = regmap_bulk_read(dev->regmap, 0xe0, &buf[0], 1); if (ret) goto err; - } else { - /* - * Use nominal target frequency as tuner driver does not provide - * actual frequency used. Carrier offset calculation is not - * valid. - */ - tuner_frequency_khz = c->frequency; - } - - /* set M88RS6000/DS3103B demod main mclk and ts mclk from tuner die */ - if (dev->chip_id == M88RS6000_CHIP_ID) { - if (c->symbol_rate > 45010000) - dev->mclk = 110250000; - else - dev->mclk = 96000000; - - if (c->delivery_system == SYS_DVBS) - target_mclk = 96000000; - else - target_mclk = 144000000; - - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { - m88ds3103b_select_mclk(dev); - m88ds3103b_set_mclk(dev, target_mclk / 1000); - } - /* Enable demod clock path */ - ret = regmap_write(dev->regmap, 0x06, 0x00); + ret = regmap_bulk_read(dev->regmap, 0xe6, &buf[1], 1); if (ret) goto err; - usleep_range(10000, 20000); - } else { - /* set M88DS3103 mclk and ts mclk. */ - dev->mclk = 96000000; - switch (dev->cfg->ts_mode) { - case M88DS3103_TS_SERIAL: - case M88DS3103_TS_SERIAL_D7: - target_mclk = dev->cfg->ts_clk; + switch ((buf[0] >> 2) & 0x01) { + case 0: + c->inversion = INVERSION_OFF; break; - case M88DS3103_TS_PARALLEL: - case M88DS3103_TS_CI: - if (c->delivery_system == SYS_DVBS) - target_mclk = 96000000; - else { - if (c->symbol_rate < 18000000) - target_mclk = 96000000; - else if (c->symbol_rate < 28000000) - target_mclk = 144000000; - else - target_mclk = 192000000; - } + case 1: + c->inversion = INVERSION_ON; break; - default: - dev_dbg(&client->dev, "invalid ts_mode\n"); - ret = -EINVAL; - goto err; } - switch (target_mclk) { - case 96000000: - u8tmp1 = 0x02; /* 0b10 */ - u8tmp2 = 0x01; /* 0b01 */ + switch ((buf[1] >> 5) & 0x07) { + case 0: + c->fec_inner = FEC_7_8; break; - case 144000000: - u8tmp1 = 0x00; /* 0b00 */ - u8tmp2 = 0x01; /* 0b01 */ + case 1: + c->fec_inner = FEC_5_6; break; - case 192000000: - u8tmp1 = 0x03; /* 0b11 */ - u8tmp2 = 0x00; /* 0b00 */ + case 2: + c->fec_inner = FEC_3_4; + break; + case 3: + c->fec_inner = FEC_2_3; break; + case 4: + c->fec_inner = FEC_1_2; + break; + default: + dev_dbg(&client->dev, "invalid fec_inner\n"); } - ret = m88ds3103_update_bits(dev, 0x22, 0xc0, u8tmp1 << 6); + + c->modulation = QPSK; + + break; + case SYS_DVBS2: + ret = regmap_bulk_read(dev->regmap, 0x7e, &buf[0], 1); if (ret) goto err; - ret = m88ds3103_update_bits(dev, 0x24, 0xc0, u8tmp2 << 6); + + ret = regmap_bulk_read(dev->regmap, 0x89, &buf[1], 1); if (ret) goto err; - } - ret = regmap_write(dev->regmap, 0xb2, 0x01); - if (ret) - goto err; - - ret = regmap_write(dev->regmap, 0x00, 0x01); - if (ret) - goto err; + ret = regmap_bulk_read(dev->regmap, 0x76, &buf[2], 1); + if (ret) + goto err; - switch (c->delivery_system) { - case SYS_DVBS: - if (dev->chip_id == M88RS6000_CHIP_ID) { - len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals); - init = m88rs6000_dvbs_init_reg_vals; - } else { - len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals); - init = m88ds3103_dvbs_init_reg_vals; - } - break; - case SYS_DVBS2: - if (dev->chip_id == M88RS6000_CHIP_ID) { - len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals); - init = m88rs6000_dvbs2_init_reg_vals; - } else { - len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals); - init = m88ds3103_dvbs2_init_reg_vals; + switch ((buf[0] >> 0) & 0x0f) { + case 0: + c->fec_inner = FEC_1_4; + break; + case 1: + c->fec_inner = FEC_1_3; + break; + case 2: + c->fec_inner = FEC_2_5; + break; + case 3: + c->fec_inner = FEC_1_2; + break; + case 4: + c->fec_inner = FEC_3_5; + break; + case 5: + c->fec_inner = FEC_2_3; + break; + case 6: + c->fec_inner = FEC_3_4; + break; + case 7: + c->fec_inner = FEC_4_5; + break; + case 8: + c->fec_inner = FEC_5_6; + break; + case 9: + c->fec_inner = FEC_8_9; + break; + case 10: + c->fec_inner = FEC_9_10; + break; + default: + dev_dbg(&client->dev, "invalid fec_inner\n"); + } + + switch ((buf[0] >> 5) & 0x01) { + case 0: + c->pilot = PILOT_OFF; + break; + case 1: + c->pilot = PILOT_ON; + break; + } + + switch ((buf[0] >> 6) & 0x07) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = PSK_8; + break; + case 2: + c->modulation = APSK_16; + break; + case 3: + c->modulation = APSK_32; + break; + default: + dev_dbg(&client->dev, "invalid modulation\n"); + } + + switch ((buf[1] >> 7) & 0x01) { + case 0: + c->inversion = INVERSION_OFF; + break; + case 1: + c->inversion = INVERSION_ON; + break; + } + + switch ((buf[2] >> 0) & 0x03) { + case 0: + c->rolloff = ROLLOFF_35; + break; + case 1: + c->rolloff = ROLLOFF_25; + break; + case 2: + c->rolloff = ROLLOFF_20; + break; + default: + dev_dbg(&client->dev, "invalid rolloff\n"); } break; default: @@ -796,222 +925,1228 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) goto err; } - /* program init table */ - if (c->delivery_system != dev->delivery_system) { - ret = m88ds3103_wr_reg_val_tab(dev, init, len); + ret = regmap_bulk_read(dev->regmap, 0x6d, buf, 2); + if (ret) + goto err; + + c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000); + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_get_total_carrier_offset(struct dvb_frontend *fe, s32 *tcoff) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + s32 tmp3, tmp4, nval1, nval2; + unsigned tmp, tmp1, tmp2; + int ret; + + ret = regmap_read(dev->regmap, 0x5d, &tmp); + if (ret) + goto err; + tmp &= 0xf8; + ret = regmap_write(dev->regmap, 0x5d, tmp); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x5e, &tmp1); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x5f, &tmp2); + if (ret) + goto err; + tmp3 = (tmp2 << 8) | tmp1; + tmp |= 0x06; + ret = regmap_write(dev->regmap, 0x5d, tmp); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x5e, &tmp1); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x5f, &tmp2); + if (ret) + goto err; + tmp4 = (tmp2 << 8) | tmp1; + if (((tmp3 >> 15) & 0x01) == 0x01) + nval1 = tmp3 - (1 << 16); + else + nval1 = tmp3; + if (((tmp4 >> 15) & 0x01) == 0x01) + nval2 = tmp4 - (1 << 16); + else + nval2 = tmp4; + + *tcoff = (nval1 - nval2) * 96000 / (1 << 16); + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_bs_set_reg(struct dvb_frontend *fe, bool vendor) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + unsigned tmp; + int ret; + + ret = regmap_write(dev->regmap, 0xb2, 0x01); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x00, 0x01); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x4a, 0x00); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x4d, &tmp); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x4d, tmp | 0x91); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x90, &tmp); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x90, tmp | 0x73); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x91, vendor ? 0x46 : 0x42); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x92, vendor ? 0x03 : 0x01); + if (ret) + goto err; + if (vendor) { + ret = regmap_read(dev->regmap, 0x93, &tmp); + if (ret) + goto err; + tmp &= 0x0f; + ret = regmap_write(dev->regmap, 0x93, tmp | 0x8f); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x94, &tmp); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x94, tmp | 0x15); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x95, 0x64); + if (ret) + goto err; + } + ret = regmap_write(dev->regmap, 0x97, 0xb3); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x99, 0x1c); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x30, dev->cfg->agc_inv ? 0x18 : 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x32, 0x44); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x33, dev->cfg->agc); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x35, 0x7f); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x4b, 0x04); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x56, 0x01); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xa0, 0x44); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x08, 0x83); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x25, &tmp); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x25, tmp | 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x70, 0x00); + if (ret) + goto err; + ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x00, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xb2, 0x00); + if (ret) + goto err; + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_set_delsys(struct dvb_frontend *fe, u8 delivery_system) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + const struct m88ds3103_reg_val *init; + u32 target_mclk = 96000000, ts_clk = dev->cfg->ts_clk, u32tmp; + u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0, buf[2]; + unsigned tmp; + u16 u16tmp; + int ret, len; + static const struct reg_sequence reset_buf[] = { + {0x07, 0x80}, {0x07, 0x00} + }; + buf[0] = 0; + buf[1] = 0; + + ret = regmap_multi_reg_write(dev->regmap, reset_buf, 2); + if (ret) + goto err; + + ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0xb2, 0x01); + if (ret) + goto err; + + if (!blind) { + ret = regmap_write(dev->regmap, 0x00, 0x01); + if (ret) + goto err; + } + + if (blind) { + switch (delivery_system) { + case SYS_DVBS: + if (dev->chip_id == M88RS6000_CHIP_ID) { + len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals); + init = m88rs6000_dvbs_init_reg_vals; + } else { + len = ARRAY_SIZE(m88ds3103_dvbs_bs_init_reg_vals); + init = m88ds3103_dvbs_bs_init_reg_vals; + } + break; + case SYS_DVBS2: + if (dev->chip_id == M88RS6000_CHIP_ID) { + len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals); + init = m88rs6000_dvbs2_init_reg_vals; + } else { + len = ARRAY_SIZE(m88ds3103_dvbs2_bs_init_reg_vals); + init = m88ds3103_dvbs2_bs_init_reg_vals; + } + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto err; + } + } else { + switch (delivery_system) { + case SYS_DVBS: + if (dev->chip_id == M88RS6000_CHIP_ID) { + len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals); + init = m88rs6000_dvbs_init_reg_vals; + } else { + len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals); + init = m88ds3103_dvbs_init_reg_vals; + } + break; + case SYS_DVBS2: + if (dev->chip_id == M88RS6000_CHIP_ID) { + len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals); + init = m88rs6000_dvbs2_init_reg_vals; + } else { + len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals); + init = m88ds3103_dvbs2_init_reg_vals; + } + break; + default: + dev_dbg(&client->dev, "invalid delivery_system\n"); + ret = -EINVAL; + goto err; + } + } + + /* program init table */ + ret = m88ds3103_wr_reg_val_tab(dev, init, len); + if (ret) + goto err; + + /* Disable demod clock path */ + if (dev->chip_id == M88RS6000_CHIP_ID) { + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + ret = regmap_read(dev->regmap, 0xb2, &u32tmp); + if (ret) + goto err; + if (u32tmp == 0x01) { + ret = regmap_write(dev->regmap, 0x00, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xb2, 0x00); + if (ret) + goto err; + } + } + + ret = regmap_write(dev->regmap, 0x06, 0xe0); + if (ret) + goto err; + } + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + /* set M88RS6000/DS3103B demod main mclk and ts mclk from tuner die */ + if (dev->chip_id == M88RS6000_CHIP_ID) { + if (c->symbol_rate > 45010000) + dev->mclk = 110250000; + else + dev->mclk = 96000000; + + if (c->delivery_system == SYS_DVBS) + target_mclk = 96000000; + else + target_mclk = 144000000; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + m88ds3103b_select_mclk(dev); + m88ds3103b_set_mclk(dev, target_mclk / 1000); + } + + /* Enable demod clock path */ + ret = regmap_write(dev->regmap, 0x06, 0x00); + if (ret) + goto err; + usleep_range(10000, 20000); + } else { + /* set M88DS3103 mclk and ts mclk. */ + dev->mclk = 96000000; + + switch (dev->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + case M88DS3103_TS_SERIAL_D7: + ts_clk = 0; + if (c->delivery_system == SYS_DVBS2) { + if (c->symbol_rate > 18000000) + target_mclk = 144000000; + else + target_mclk = 96000000; + } + break; + case M88DS3103_TS_CI: + ts_clk = (c->delivery_system == SYS_DVBS2) ? 8471000 : 8000000; + fallthrough; + case M88DS3103_TS_PARALLEL: + if (c->delivery_system == SYS_DVBS2) { + if (c->symbol_rate < 18000000) + target_mclk = 96000000; + else if (c->symbol_rate < 28000000) + target_mclk = 144000000; + else + target_mclk = 192000000; + } + break; + default: + dev_dbg(&client->dev, "invalid ts_mode\n"); + ret = -EINVAL; + goto err; + } + + switch (target_mclk) { + case 72000000: + u8tmp1 = 0x00; + u8tmp2 = 0x03; + break; + case 96000000: + u8tmp1 = 0x02; + u8tmp2 = 0x01; + break; + case 115200000: + u8tmp1 = 0x01; + u8tmp2 = 0x01; + break; + case 144000000: + u8tmp1 = 0x00; + u8tmp2 = 0x01; + break; + case 192000000: + u8tmp1 = 0x03; + u8tmp2 = 0x00; + break; + } + + ret = m88ds3103_update_bits(dev, 0x22, 0xc0, u8tmp1 << 6); + if (ret) + goto err; + ret = m88ds3103_update_bits(dev, 0x24, 0xc0, u8tmp2 << 6); + if (ret) + goto err; + } + + if (dev->chip_id == M88RS6000_CHIP_ID) { + ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08); + if (ret) + goto err; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + buf[0] = m88ds3103b_dt_read(dev, 0x15); + buf[1] = m88ds3103b_dt_read(dev, 0x16); + + if (c->symbol_rate > 45010000) { + buf[0] &= ~0x03; + buf[0] |= 0x02; + buf[0] |= ((147 - 32) >> 8) & 0x01; + buf[1] = (147 - 32) & 0xFF; + + dev->mclk = 110250 * 1000; + } else { + buf[0] &= ~0x03; + buf[0] |= ((128 - 32) >> 8) & 0x01; + buf[1] = (128 - 32) & 0xFF; + + dev->mclk = 96000 * 1000; + } + m88ds3103b_dt_write(dev, 0x15, buf[0]); + m88ds3103b_dt_write(dev, 0x16, buf[1]); + + regmap_read(dev->regmap, 0x30, &u32tmp); + u32tmp &= ~0x80; + regmap_write(dev->regmap, 0x30, u32tmp & 0xff); + } + + ret = regmap_write(dev->regmap, 0xf1, 0x01); + if (ret) + goto err; + + if (dev->chiptype != M88DS3103_CHIPTYPE_3103B) { + ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80); + if (ret) + goto err; + } + } + + switch (dev->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + u8tmp1 = 0x00; + u8tmp = 0x06; + break; + case M88DS3103_TS_SERIAL_D7: + u8tmp1 = 0x20; + u8tmp = 0x06; + break; + case M88DS3103_TS_PARALLEL: + u8tmp = 0x00; + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + u8tmp = 0x01; + u8tmp1 = 0x01; + } + break; + case M88DS3103_TS_CI: + u8tmp = 0x03; + break; + default: + dev_dbg(&client->dev, "invalid ts_mode\n"); + ret = -EINVAL; + goto err; + } + + if (dev->cfg->ts_clk_pol) + u8tmp |= 0x40; + + /* TS mode */ + ret = regmap_write(dev->regmap, 0xfd, u8tmp); + if (ret) + goto err; + + switch (dev->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + case M88DS3103_TS_SERIAL_D7: + ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1); + if (ret) + goto err; + u16tmp = 0; + u8tmp1 = 0; + u8tmp2 = 0; + break; + case M88DS3103_TS_PARALLEL: + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + ret = m88ds3103_update_bits(dev, 0x29, 0x01, u8tmp1); + if (ret) + goto err; + } + fallthrough; + default: + u16tmp = DIV_ROUND_UP(target_mclk, ts_clk); + u8tmp1 = u16tmp / 2 - 1; + u8tmp2 = DIV_ROUND_UP(u16tmp, 2) - 1; + } + + dev_dbg(&client->dev, "target_mclk=%u ts_clk=%u ts_clk_divide_ratio=%u\n", + target_mclk, ts_clk, u16tmp); + + /* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */ + /* u8tmp2[5:0] => ea[5:0] */ + u8tmp = (u8tmp1 >> 2) & 0x0f; + ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp); + if (ret) + goto err; + u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0; + ret = regmap_write(dev->regmap, 0xea, u8tmp); + if (ret) + goto err; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) + m88ds3103b_set_mclk(dev, target_mclk / 1000); + + if (c->symbol_rate <= 5000000 && c->delivery_system == SYS_DVBS2) { + ret = regmap_write(dev->regmap, 0xc0, 0x04); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x8a, 0x09); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x8b, 0x22); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0x8c, 0x88); + if (ret) + goto err; + } + + ret = regmap_write(dev->regmap, 0xc3, 0x08); + if (ret) + goto err; + + if (blind) { + if (c->symbol_rate <= 2500000) { + ret = regmap_write(dev->regmap, 0xc8, 0x0a); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xc4, 0x07); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xc7, 0x28); + if (ret) + goto err; + } else if (c->symbol_rate <= 5000000) { + ret = regmap_write(dev->regmap, 0xc8, 0x0a); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xc4, 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xc7, 0x10); + if (ret) + goto err; + } else if (c->symbol_rate <= 20000000) { + ret = regmap_write(dev->regmap, 0xc8, 0x0a); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xc4, 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xc7, 0x20); + if (ret) + goto err; + } else { + ret = regmap_write(dev->regmap, 0xc8, 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xc4, 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xc7, 0x20); + if (ret) + goto err; + } + } else { + if (c->symbol_rate <= 3000000) + u8tmp = 0x20; + else if (c->symbol_rate <= 10000000) + u8tmp = 0x10; + else + u8tmp = 0x06; + + ret = regmap_write(dev->regmap, 0xc8, u8tmp); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xc4, 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xc7, 0x00); + if (ret) + goto err; + } + + u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk); + buf[0] = (u16tmp >> 0) & 0xff; + buf[1] = (u16tmp >> 8) & 0xff; + ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2); + if (ret) + goto err; + + ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1); + if (ret) + goto err; + + ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4); + if (ret) + goto err; + + ret = regmap_write(dev->regmap, 0x33, dev->cfg->agc); + if (ret) + goto err; + + ret = regmap_read(dev->regmap, 0x25, &tmp); + if (ret) + goto err; + tmp |= 0x08; + ret = regmap_write(dev->regmap, 0x25, tmp); + if (ret) + goto err; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + /* enable/disable 192M LDPC clock */ + ret = m88ds3103_update_bits(dev, 0x29, 0x10, + (c->delivery_system == SYS_DVBS) ? 0x10 : 0x0); + if (ret) + goto err; + + ret = m88ds3103_update_bits(dev, 0xc9, 0x08, 0x08); + if (ret) + goto err; + } + + ret = m88ds3103_set_carrier_offset(fe, 0); + if (ret) + goto err; + + ret = regmap_read(dev->regmap, 0x56, &tmp); + if (ret) + goto err; + tmp &= ~0x01; + ret = regmap_write(dev->regmap, 0x56, tmp); + if (ret) + goto err; + + ret = regmap_read(dev->regmap, 0x76, &tmp); + if (ret) + goto err; + tmp &= ~0x80; + ret = regmap_write(dev->regmap, 0x76, tmp); + if (ret) + goto err; + + if (!blind) { + ret = regmap_write(dev->regmap, 0x00, 0x00); if (ret) goto err; } - if (dev->chip_id == M88RS6000_CHIP_ID) { - if (c->delivery_system == SYS_DVBS2 && - c->symbol_rate <= 5000000) { - ret = regmap_write(dev->regmap, 0xc0, 0x04); + ret = regmap_write(dev->regmap, 0xb2, 0x00); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_set_frontend(struct dvb_frontend *fe) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + enum fe_status tmpstatus; + int i, j, ret; + + dev_dbg(&client->dev, + "delivery_system=%d modulation=%d frequency=%u symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", + c->delivery_system, c->modulation, c->frequency, c->symbol_rate, + c->inversion, c->pilot, c->rolloff); + + if (!dev->warm) { + ret = -EAGAIN; + goto err; + } + + blind = (c->algorithm == ALGORITHM_BLIND || c->algorithm == ALGORITHM_SPECTRUM); + + if (c->delivery_system == SYS_DVBS2) + m88ds3103_set_delsys(fe, SYS_DVBS2); + + if (c->delivery_system == SYS_DVBS) + m88ds3103_set_delsys(fe, SYS_DVBS); + + if (c->delivery_system == SYS_AUTO) { + for (i = 0; i < 2; i++) { + dev->delivery_system = (i == 0) ? SYS_DVBS2 : SYS_DVBS; + c->delivery_system = dev->delivery_system; + m88ds3103_set_delsys(fe, dev->delivery_system); + + for (j = 0; j < 120; j++) { + m88ds3103_read_status(fe, &tmpstatus); + if (tmpstatus & FE_HAS_CARRIER) + goto end; + } + + } + } +end: + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_fft_scan(struct dvb_frontend *fe) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct fe_tp_info *tmp = NULL; + unsigned tmp0, tmp1, tmp2, tmp3, tmp4; + bool fftdone; + int ret; + u8 cnt, buf[2]; + u16 i, totaltpnum; + u32 freq = 0, sym = 0; + s32 s_value = 0; + + tmp = dev->msd; + if (tpcnt > 1000) + return -ENOMEM; + + for (i = 0; i < 2; i++) { + buf[0] = 0x70; + buf[1] = 0x00; + fftdone = 0; + cnt = 50; + ret = regmap_write(dev->regmap, 0x9a, 0x80); + if (ret) + goto err; + do { + ret = regmap_read(dev->regmap, 0x9a, &tmp0); if (ret) goto err; - buf[0] = 0x09; - buf[1] = 0x22; - buf[2] = 0x88; - ret = regmap_bulk_write(dev->regmap, 0x8a, buf, 3); + fftdone = ((tmp0 & 0x80) == 0x00) ? 1 : 0; + msleep(1); + cnt--; + } while ((fftdone == 0) && (cnt > 0)); + if (fftdone || (1 == i)) { + break; + } else { + ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2); if (ret) goto err; } - ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08); + } + ret = regmap_read(dev->regmap, 0x9a, &tmp0); + if (ret) + goto err; + totaltpnum = tmp0 & 0x1f; + + while (totaltpnum > 0) { + totaltpnum--; + + ret = regmap_write(dev->regmap, 0x9a, 0x20); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x9b, &tmp1); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x9b, &tmp2); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x9b, &tmp3); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x9b, &tmp4); if (ret) goto err; - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { - buf[0] = m88ds3103b_dt_read(dev, 0x15); - buf[1] = m88ds3103b_dt_read(dev, 0x16); + sym = (((tmp4 & 0x03) << 8) | tmp3) * 187500; + s_value = (((tmp2 & 0x03) << 8) | tmp1); + if (s_value >= 512) + s_value -= 1024; - if (c->symbol_rate > 45010000) { - buf[0] &= ~0x03; - buf[0] |= 0x02; - buf[0] |= ((147 - 32) >> 8) & 0x01; - buf[1] = (147 - 32) & 0xFF; + freq = c->frequency + s_value * (96000 / 512); - dev->mclk = 110250 * 1000; - } else { - buf[0] &= ~0x03; - buf[0] |= ((128 - 32) >> 8) & 0x01; - buf[1] = (128 - 32) & 0xFF; + if (freq < 290000) + return 0; - dev->mclk = 96000 * 1000; - } - m88ds3103b_dt_write(dev, 0x15, buf[0]); - m88ds3103b_dt_write(dev, 0x16, buf[1]); + if ((sym > 50000000) || (sym < 800000)) + return 0; - regmap_read(dev->regmap, 0x30, &u32tmp); - u32tmp &= ~0x80; - regmap_write(dev->regmap, 0x30, u32tmp & 0xff); - } + if (sym > 45000000) + sym = 45000000; + else if (sym > 2000000) + sym -= 400000; + else + sym -= 500000; - ret = regmap_write(dev->regmap, 0xf1, 0x01); + tpcnt++; + tmp[tpcnt-1].frequency = freq; + tmp[tpcnt-1].symbol_rate = sym; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_blindscan(struct dvb_frontend *fe, bool init, unsigned int *delay, enum fe_status *status) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + enum fe_status tmpstatus; + u8 lockwait, locksleep, i, j, buf[2]; + u32 tmpfreq; + s32 carrieroffset; + u16 index; + int ret; + static const struct reg_sequence reset_buf[] = { + {0x07, 0x80}, {0x07, 0x00} + }; + + if (init) { + struct fe_tp_info *blindscaninfo = kmalloc(sizeof(*blindscaninfo) * 1000, GFP_KERNEL); + dev->msd = blindscaninfo; + scandone = 0; + tpcnt = 0; + tpnum = 0; + + ret = m88ds3103_set_frontend(fe); if (ret) goto err; - if (dev->chiptype != M88DS3103_CHIPTYPE_3103B) { - ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80); + ret = regmap_multi_reg_write(dev->regmap, reset_buf, 2); + if (ret) + goto err; + + c->frequency = c->scan_start_frequency; + c->symbol_rate = 40000000; + c->bandwidth_hz = c->symbol_rate / 100 * 135; + ret = m88ds3103_bs_set_reg(fe, 1); + if (ret) + goto err; + + while (c->frequency < c->scan_end_frequency) { + if (kthread_should_stop() || dvb_frontend_task_should_stop(fe)) + break; + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + ret = m88ds3103_set_carrier_offset(fe, 0); + if (ret) + goto err; + + ret = m88ds3103_fft_scan(fe); + if (ret) + goto err; + + ret = regmap_bulk_read(dev->regmap, 0x95, buf, 2); if (ret) goto err; + + tmpfreq = 0; + tmpfreq = (((((buf[0] & 0xc0) >> 6) << 8) | buf[1]) * 96000) / 512; + if (tmpfreq > 96000) + tmpfreq = 10000; + + c->frequency += tmpfreq; + + if (tmpfreq == 0) + c->frequency += 36000; + + if (c->frequency >= 2350000) + c->frequency = 2350000; } } - switch (dev->cfg->ts_mode) { - case M88DS3103_TS_SERIAL: - u8tmp1 = 0x00; - u8tmp = 0x06; - break; - case M88DS3103_TS_SERIAL_D7: - u8tmp1 = 0x20; - u8tmp = 0x06; - break; - case M88DS3103_TS_PARALLEL: - u8tmp = 0x02; - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { - u8tmp = 0x01; - u8tmp1 = 0x01; + if (tpcnt == 0) + goto end; + +next: + for (index = tpnum; index < tpcnt; index++) { + if (kthread_should_stop() || dvb_frontend_task_should_stop(fe)) + break; + if (index == tpcnt - 1) + scandone = 1; + c->frequency = dev->msd[index].frequency; + c->symbol_rate = dev->msd[index].symbol_rate; + c->bandwidth_hz = c->symbol_rate / 100 * 135; + + for (i = 0; i < 2; i++) { + c->algorithm = ALGORITHM_BLIND; + c->delivery_system = (i == 0) ? SYS_DVBS2 : SYS_DVBS; + + ret = m88ds3103_set_frontend(fe); + if (ret) + goto err; + + if (c->symbol_rate < 1000000) { + lockwait = 80; + locksleep = 10; + } else if (c->symbol_rate < 1500000) { + lockwait = 40; + locksleep = 10; + } else if (c->symbol_rate > 7000000 && c->symbol_rate < 20000000) { + lockwait = 25; + locksleep = 5; + } else if (c->symbol_rate > 20000000) { + lockwait = 20; + locksleep = 0; + } else { + lockwait = 30; + locksleep = 10; + } + + for (j = 0; j < lockwait ; j++) { + ret = m88ds3103_read_status(fe, &tmpstatus); + if (ret) + goto err; + msleep(locksleep); + + if (tmpstatus & FE_HAS_CARRIER) + break; + } + + if (dev->fe_status & FE_HAS_CARRIER) { + ret = m88ds3103_get_total_carrier_offset(fe, &carrieroffset); + if (ret) + goto err; + + c->frequency -= carrieroffset; + + ret = m88ds3103_get_frontend(fe, c); + if (ret) + goto err; + + *status = FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|FE_HAS_SYNC|FE_HAS_LOCK; + goto locked; + } else { + if (i == 1) + goto nolock; + } } - break; - case M88DS3103_TS_CI: - u8tmp = 0x03; - break; - default: - dev_dbg(&client->dev, "invalid ts_mode\n"); - ret = -EINVAL; - goto err; +nolock: + if (scandone) + goto end; + tpnum++; + goto next; +locked: + tpnum++; + return 0; } + if (scandone) +end: + *status = FE_TIMEDOUT; - if (dev->cfg->ts_clk_pol) - u8tmp |= 0x40; + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} - /* TS mode */ - ret = regmap_write(dev->regmap, 0xfd, u8tmp); +static int m88ds3103_get_fft_one_band(struct dvb_frontend *fe, s32 center_freq, s32 range, u32 *freq, s32 *rf_level, int fft_size) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + unsigned tmp1, tmp2; + int x, ret, i = 0; + bool fft_done = false; + u8 cnt; + s32 tmp; + + ret = regmap_write(dev->regmap, 0x9a, 0x80); if (ret) goto err; - switch (dev->cfg->ts_mode) { - case M88DS3103_TS_SERIAL: - case M88DS3103_TS_SERIAL_D7: - ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1); - if (ret) - goto err; - u16tmp = 0; - u8tmp1 = 0x3f; - u8tmp2 = 0x3f; - break; - case M88DS3103_TS_PARALLEL: - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { - ret = m88ds3103_update_bits(dev, 0x29, 0x01, u8tmp1); + for (i = 0; i < 2; ++i) { + for (cnt = 0; cnt < 200; ++cnt) { + ret = regmap_read(dev->regmap, 0x9a, &tmp); if (ret) goto err; + fft_done = ((tmp & 0x80) == 0x00); + if (fft_done) + break; + msleep(1); } - fallthrough; - default: - u16tmp = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk); - u8tmp1 = u16tmp / 2 - 1; - u8tmp2 = DIV_ROUND_UP(u16tmp, 2) - 1; - } - dev_dbg(&client->dev, "target_mclk=%u ts_clk=%u ts_clk_divide_ratio=%u\n", - target_mclk, dev->cfg->ts_clk, u16tmp); + if (!(fft_done)) { + if (i == 1) + msleep(100); + else + return -1; + } else + break; + } - /* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */ - /* u8tmp2[5:0] => ea[5:0] */ - u8tmp = (u8tmp1 >> 2) & 0x0f; - ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp); - if (ret) - goto err; - u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0; - ret = regmap_write(dev->regmap, 0xea, u8tmp); + ret = regmap_write(dev->regmap, 0x9a, 0x40); if (ret) goto err; - if (c->symbol_rate <= 3000000) - u8tmp = 0x20; - else if (c->symbol_rate <= 10000000) - u8tmp = 0x10; - else - u8tmp = 0x06; + for (i = 0; i < 32; ++i) + rf_level[i] = 0; - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) - m88ds3103b_set_mclk(dev, target_mclk / 1000); + for (i = 32; i < fft_size; ++i) { + freq[i] = ((i - (signed)fft_size / 2) * range) / (signed)fft_size + center_freq; + ret = regmap_read(dev->regmap, 0x9b, &tmp1); + if (ret) + goto err; + ret = regmap_read(dev->regmap, 0x9b, &tmp2); + if (ret) + goto err; + x = (tmp2 << 8) | tmp1; + x = ((20000 * ((long long) intlog10(x))) >> 24); + rf_level[i] = x; + } - ret = regmap_write(dev->regmap, 0xc3, 0x08); - if (ret) - goto err; + for (; i < fft_size; ++i) + rf_level[i] = 0; - ret = regmap_write(dev->regmap, 0xc8, u8tmp); - if (ret) - goto err; + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} - ret = regmap_write(dev->regmap, 0xc4, 0x08); - if (ret) - goto err; +int m88ds3103_get_spectrum_scan_fft(struct dvb_frontend *fe, unsigned int *delay, enum fe_status *status) +{ + + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct spectrum_scan_state *ss = &dev->spectrum_scan_state; + s32 useable_samples2, lost_samples2, sum_len, sum_correction, min_correction[2], max_correction[2]; + s32 last_avg = 0, current_avg = 0, correction = 0, *temp_rf_level = NULL; + u32 idx = 0, *temp_freq = NULL; + int i, ret, error = 0; + + ss->spectrum_present = false; + c->algorithm = ALGORITHM_SPECTRUM; + + if (c->scan_end_frequency < c->scan_start_frequency) + return -1; + + ss->start_frequency = c->scan_start_frequency; + ss->end_frequency = c->scan_end_frequency; + ss->range = 96000; + ss->fft_size = 512; + ss->frequency_step = ss->range / ss->fft_size; + ss->range = ss->frequency_step * ss->fft_size; + ss->spectrum_len = (ss->end_frequency - ss->start_frequency + ss->frequency_step - 1) / ss->frequency_step; + useable_samples2 = ((ss->fft_size * 6) / 10 + 1) / 2; + lost_samples2 = ss->fft_size / 2 - useable_samples2; + + ss->freq = kzalloc(ss->spectrum_len * (sizeof(ss->freq[0])), GFP_KERNEL); + ss->spectrum = kzalloc(ss->spectrum_len * (sizeof(ss->spectrum[0])), GFP_KERNEL); + temp_freq = kzalloc(8192 * (sizeof(temp_freq[0])), GFP_KERNEL); + temp_rf_level = kzalloc(8192 * (sizeof(temp_rf_level[0])), GFP_KERNEL); + + if (!temp_rf_level || !temp_freq || !ss->freq || !ss->spectrum) { + error = -1; + goto _end; + } - ret = regmap_write(dev->regmap, 0xc7, 0x00); + ret = m88ds3103_set_frontend(fe); if (ret) goto err; - u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk); - buf[0] = (u16tmp >> 0) & 0xff; - buf[1] = (u16tmp >> 8) & 0xff; - ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2); + ret = m88ds3103_bs_set_reg(fe, 0); if (ret) goto err; - ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1); - if (ret) - goto err; + min_correction[0] = 0x7fffffff; + min_correction[1] = 0x7fffffff; + max_correction[0] = 0x80000000; + max_correction[1] = 0x80000000; + sum_correction = 0; + sum_len = 0; - ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4); - if (ret) - goto err; + for (idx = 0; idx < ss->spectrum_len;) { + if (kthread_should_stop() || dvb_frontend_task_should_stop(fe)) + break; + + c->frequency = ss->start_frequency + (idx + useable_samples2) * ss->frequency_step; + c->symbol_rate = 40000000; + c->bandwidth_hz = c->symbol_rate / 100 * 135; - ret = regmap_write(dev->regmap, 0x33, dev->cfg->agc); - if (ret) - goto err; + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } - if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { - /* enable/disable 192M LDPC clock */ - ret = m88ds3103_update_bits(dev, 0x29, 0x10, - (c->delivery_system == SYS_DVBS) ? 0x10 : 0x0); + ret = m88ds3103_set_carrier_offset(fe, 0); if (ret) goto err; - ret = m88ds3103_update_bits(dev, 0xc9, 0x08, 0x08); - if (ret) - goto err; + error = m88ds3103_get_fft_one_band(fe, c->frequency, ss->range, temp_freq, temp_rf_level, ss->fft_size); + + if (error) + goto _end; + + current_avg = 0; + + for (i = lost_samples2 - 5; i < lost_samples2 + 5; ++i) + current_avg += temp_rf_level[i]; + + current_avg /= 10; + correction = (idx == 0) ? 0 : -(current_avg - last_avg); + + for (i = lost_samples2; i < ss->fft_size - lost_samples2 && idx < ss->spectrum_len; ++idx, i++ ) { + ss->freq[idx] = temp_freq[i]; + ss->spectrum[idx] = temp_rf_level[i] + correction; + } + + if (correction < min_correction[0]) { + min_correction[1] = min_correction[0]; + min_correction[0] = correction; + } else if (correction < min_correction[1]) { + min_correction[1] = correction; + } + + if (correction > max_correction[0]) { + max_correction[1] = max_correction[0]; + max_correction[0] = correction; + } else if (correction > max_correction[1]) { + max_correction[1] = correction; + } + + sum_correction += correction; + sum_len ++; + + last_avg = 0; + + for (i = ss->fft_size - lost_samples2 - 5; i < ss->fft_size - lost_samples2 + 5; ++i) + last_avg += temp_rf_level[i] + correction; + last_avg /= 10; + } - dev_dbg(&client->dev, "carrier offset=%d\n", - (tuner_frequency_khz - c->frequency)); + if (sum_len > 4) { + sum_correction -= min_correction[0] + min_correction[1] + max_correction[0] + max_correction[1]; + correction = sum_correction / (sum_len - 4); - /* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */ - s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency); - s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000); - buf[0] = (s32tmp >> 0) & 0xff; - buf[1] = (s32tmp >> 8) & 0xff; - ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2); - if (ret) - goto err; + for (i = 0; i < ss->spectrum_len; ++i) + ss->spectrum[i] -= correction; + } - ret = regmap_write(dev->regmap, 0x00, 0x00); + ss->spectrum_present = true; +_end: + if (temp_freq) + kfree(temp_freq); + if (temp_rf_level) + kfree(temp_rf_level); + + if (!error) { + *status = FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|FE_HAS_SYNC|FE_HAS_LOCK; + return 0; + } else { + *status = FE_TIMEDOUT|FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|FE_HAS_SYNC|FE_HAS_LOCK; + return error; + } +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int m88ds3103_stop_task(struct dvb_frontend *fe) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct spectrum_scan_state *ss = &dev->spectrum_scan_state; + + if (ss->freq) + kfree(ss->freq); + if (ss->spectrum) + kfree(ss->spectrum); + + memset(ss, 0, sizeof(*ss)); + + return 0; +} + +static int m88ds3103_spectrum_start(struct dvb_frontend *fe, struct dtv_fe_spectrum *s, + unsigned int *delay, enum fe_status *status) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct spectrum_scan_state *ss = &dev->spectrum_scan_state; + int ret; + + ret = m88ds3103_stop_task(fe); if (ret) goto err; - ret = regmap_write(dev->regmap, 0xb2, 0x00); + s->scale = FE_SCALE_DECIBEL; + + ret = m88ds3103_get_spectrum_scan_fft(fe, delay, status); if (ret) goto err; - dev->delivery_system = c->delivery_system; + s->num_freq = ss->spectrum_len; - return 0; + return neumo_scan_spectrum(ss); err: dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } +static int m88ds3103_spectrum_get(struct dvb_frontend *fe, struct dtv_fe_spectrum *user) +{ + struct m88ds3103_dev *dev = fe->demodulator_priv; + int error = 0; + + if (user->num_freq > dev->spectrum_scan_state.spectrum_len) + user->num_freq = dev->spectrum_scan_state.spectrum_len; + + if (user->num_candidates > dev->spectrum_scan_state.num_candidates) + user->num_candidates = dev->spectrum_scan_state.num_candidates; + + if (dev->spectrum_scan_state.freq && dev->spectrum_scan_state.spectrum) { + if (user->freq && user->num_freq > 0 && copy_to_user((void __user*) user->freq, + dev->spectrum_scan_state.freq, user->num_freq * sizeof(__u32))) { + error = -EFAULT; + } + if (user->rf_level && user->num_freq > 0 && copy_to_user((void __user*) user->rf_level, + dev->spectrum_scan_state.spectrum, user->num_freq * sizeof(__s32))) { + error = -EFAULT; + } + if (user->candidates && user->num_candidates > 0 && copy_to_user((void __user*) user->candidates, + dev->spectrum_scan_state.candidates, user->num_candidates * sizeof(struct spectral_peak_t))) { + error = -EFAULT; + } + } else + error = -EFAULT; + + return error; +} + static int m88ds3103_init(struct dvb_frontend *fe) { struct m88ds3103_dev *dev = fe->demodulator_priv; @@ -1177,183 +2312,21 @@ static int m88ds3103_sleep(struct dvb_frontend *fe) return ret; } -static int m88ds3103_get_frontend(struct dvb_frontend *fe, - struct dtv_frontend_properties *c) +static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr) { struct m88ds3103_dev *dev = fe->demodulator_priv; - struct i2c_client *client = dev->client; - int ret; - u8 buf[3]; - - dev_dbg(&client->dev, "\n"); - - if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) { - ret = 0; - goto err; - } - - switch (c->delivery_system) { - case SYS_DVBS: - ret = regmap_bulk_read(dev->regmap, 0xe0, &buf[0], 1); - if (ret) - goto err; - - ret = regmap_bulk_read(dev->regmap, 0xe6, &buf[1], 1); - if (ret) - goto err; - - switch ((buf[0] >> 2) & 0x01) { - case 0: - c->inversion = INVERSION_OFF; - break; - case 1: - c->inversion = INVERSION_ON; - break; - } - - switch ((buf[1] >> 5) & 0x07) { - case 0: - c->fec_inner = FEC_7_8; - break; - case 1: - c->fec_inner = FEC_5_6; - break; - case 2: - c->fec_inner = FEC_3_4; - break; - case 3: - c->fec_inner = FEC_2_3; - break; - case 4: - c->fec_inner = FEC_1_2; - break; - default: - dev_dbg(&client->dev, "invalid fec_inner\n"); - } - - c->modulation = QPSK; - - break; - case SYS_DVBS2: - ret = regmap_bulk_read(dev->regmap, 0x7e, &buf[0], 1); - if (ret) - goto err; - - ret = regmap_bulk_read(dev->regmap, 0x89, &buf[1], 1); - if (ret) - goto err; - - ret = regmap_bulk_read(dev->regmap, 0xf2, &buf[2], 1); - if (ret) - goto err; - - switch ((buf[0] >> 0) & 0x0f) { - case 2: - c->fec_inner = FEC_2_5; - break; - case 3: - c->fec_inner = FEC_1_2; - break; - case 4: - c->fec_inner = FEC_3_5; - break; - case 5: - c->fec_inner = FEC_2_3; - break; - case 6: - c->fec_inner = FEC_3_4; - break; - case 7: - c->fec_inner = FEC_4_5; - break; - case 8: - c->fec_inner = FEC_5_6; - break; - case 9: - c->fec_inner = FEC_8_9; - break; - case 10: - c->fec_inner = FEC_9_10; - break; - default: - dev_dbg(&client->dev, "invalid fec_inner\n"); - } - - switch ((buf[0] >> 5) & 0x01) { - case 0: - c->pilot = PILOT_OFF; - break; - case 1: - c->pilot = PILOT_ON; - break; - } - - switch ((buf[0] >> 6) & 0x07) { - case 0: - c->modulation = QPSK; - break; - case 1: - c->modulation = PSK_8; - break; - case 2: - c->modulation = APSK_16; - break; - case 3: - c->modulation = APSK_32; - break; - default: - dev_dbg(&client->dev, "invalid modulation\n"); - } - - switch ((buf[1] >> 7) & 0x01) { - case 0: - c->inversion = INVERSION_OFF; - break; - case 1: - c->inversion = INVERSION_ON; - break; - } - - switch ((buf[2] >> 0) & 0x03) { - case 0: - c->rolloff = ROLLOFF_35; - break; - case 1: - c->rolloff = ROLLOFF_25; - break; - case 2: - c->rolloff = ROLLOFF_20; - break; - default: - dev_dbg(&client->dev, "invalid rolloff\n"); - } - break; - default: - dev_dbg(&client->dev, "invalid delivery_system\n"); - ret = -EINVAL; - goto err; - } - - ret = regmap_bulk_read(dev->regmap, 0x6d, buf, 2); - if (ret) - goto err; - - c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u16 tmp; - return 0; -err: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} + tmp = div_s64(c->cnr.stat[0].svalue, 100); -static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; + if (c->delivery_system == SYS_AUTO) + c->delivery_system = dev->delivery_system; - if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) - *snr = div_s64(c->cnr.stat[0].svalue, 100); + if (c->delivery_system == SYS_DVBS) + *snr = (tmp > 0x7d) ? 0xffff : tmp * 524; else - *snr = 0; + *snr = (tmp > 0xc8) ? 0xffff : tmp * 327; return 0; } @@ -1490,6 +2463,8 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, if (ret) goto err; + msleep(1); + ret = regmap_bulk_write(dev->regmap, 0xa3, diseqc_cmd->msg, diseqc_cmd->msg_len); if (ret) @@ -1629,6 +2604,11 @@ static int m88ds3103_get_tune_settings(struct dvb_frontend *fe, return 0; } +static enum dvbfe_algo m88ds3103_get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_SW; +} + static void m88ds3103_release(struct dvb_frontend *fe) { struct m88ds3103_dev *dev = fe->demodulator_priv; @@ -1702,14 +2682,14 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, EXPORT_SYMBOL(m88ds3103_attach); static const struct dvb_frontend_ops m88ds3103_ops = { - .delsys = {SYS_DVBS, SYS_DVBS2}, + .delsys = {SYS_DVBS, SYS_DVBS2, SYS_AUTO}, .info = { .name = "Montage Technology M88DS3103", - .frequency_min_hz = 950 * MHz, - .frequency_max_hz = 2150 * MHz, + .frequency_min_hz = 290 * MHz, + .frequency_max_hz = 2350 * MHz, .frequency_tolerance_hz = 5 * MHz, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, + .symbol_rate_min = 500000, + .symbol_rate_max = 48000000, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | @@ -1722,12 +2702,15 @@ static const struct dvb_frontend_ops m88ds3103_ops = { FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_RECOVER | - FE_CAN_2G_MODULATION + FE_CAN_2G_MODULATION, + .extended_caps = FE_CAN_SPECTRUM_FFT | FE_CAN_BLINDSEARCH, + .supports_neumo = 1 }, .release = m88ds3103_release, .get_tune_settings = m88ds3103_get_tune_settings, + .get_frontend_algo = m88ds3103_get_algo, .init = m88ds3103_init, .sleep = m88ds3103_sleep, @@ -1735,9 +2718,15 @@ static const struct dvb_frontend_ops m88ds3103_ops = { .set_frontend = m88ds3103_set_frontend, .get_frontend = m88ds3103_get_frontend, + .scan = m88ds3103_blindscan, + .stop_task = m88ds3103_stop_task, + .spectrum_start = m88ds3103_spectrum_start, + .spectrum_get = m88ds3103_spectrum_get, + .read_status = m88ds3103_read_status, .read_snr = m88ds3103_read_snr, .read_ber = m88ds3103_read_ber, + .read_ucblocks = m88ds3103_read_ucblocks, .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd, .diseqc_send_burst = m88ds3103_diseqc_send_burst, diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index aa5306f40201e..0102abafa9a57 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -15,6 +15,7 @@ #include #include #include +#include #define M88DS3103B_FIRMWARE "dvb-demod-m88ds3103b.fw" #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" @@ -27,6 +28,14 @@ #define M88DS3103_CHIPTYPE_RS6000 1 #define M88DS3103_CHIPTYPE_3103B 2 +#define M88DS3103_SNR_ITERATIONS 3 + +struct fe_tp_info +{ + u32 frequency; + u32 symbol_rate; +}; + struct m88ds3103_dev { struct i2c_client *client; struct i2c_client *dt_client; @@ -35,6 +44,7 @@ struct m88ds3103_dev { struct m88ds3103_config config; const struct m88ds3103_config *cfg; struct dvb_frontend fe; + struct fe_tp_info *msd; enum fe_delivery_system delivery_system; enum fe_status fe_status; u32 dvbv3_ber; /* for old DVBv3 API read_ber */ @@ -49,6 +59,8 @@ struct m88ds3103_dev { u64 post_bit_error; u64 post_bit_count; u8 dt_addr; + u16 prevUCBS2; + struct spectrum_scan_state spectrum_scan_state; }; struct m88ds3103_reg_val { @@ -225,6 +237,174 @@ static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = { {0xb8, 0x00}, }; +static const struct m88ds3103_reg_val m88ds3103_dvbs_bs_init_reg_vals[] = { + {0x23, 0x07}, + {0x08, 0x03}, + {0x0c, 0x02}, + {0x21, 0x54}, + {0x25, 0x8a}, + {0x27, 0x31}, + {0x30, 0x08}, + {0x31, 0x40}, + {0x32, 0x32}, + {0x33, 0x35}, + {0x35, 0xff}, + {0x3a, 0x00}, + {0x37, 0x10}, + {0x38, 0x10}, + {0x39, 0x02}, + {0x42, 0x60}, + {0x4a, 0x80}, + {0x4b, 0x04}, + {0x4d, 0x91}, + {0x5d, 0xc8}, + {0x50, 0x36}, + {0x51, 0x36}, + {0x52, 0x36}, + {0x53, 0x36}, + {0x63, 0x60}, + {0x64, 0x30}, + {0x65, 0x40}, + {0x68, 0x26}, + {0x69, 0x4c}, + {0x70, 0x20}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x40}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x60}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x80}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0xa0}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x1f}, + {0x76, 0x38}, + {0x77, 0xa6}, + {0x78, 0x0c}, + {0x79, 0x80}, + {0x7f, 0x14}, + {0x7c, 0x00}, + {0xae, 0x82}, + {0x80, 0x64}, + {0x81, 0x66}, + {0x82, 0x44}, + {0x85, 0x04}, + {0xcd, 0xf4}, + {0x90, 0x33}, + {0xa0, 0x44}, + {0xc0, 0x08}, + {0xc3, 0x10}, + {0xc4, 0x08}, + {0xc5, 0xf0}, + {0xc6, 0x40}, + {0xc7, 0x00}, + {0xc8, 0x1a}, + {0xc9, 0x80}, + {0xe0, 0xf8}, + {0xe6, 0x8b}, + {0xd0, 0x40}, + {0xf8, 0x20}, + {0xfa, 0x0f}, + {0xbd, 0x01}, + {0xb8, 0x00}, +}; + +static const struct m88ds3103_reg_val m88ds3103_dvbs2_bs_init_reg_vals[] = { + {0x23, 0x07}, + {0x08, 0x07}, + {0x0c, 0x02}, + {0x21, 0x54}, + {0x25, 0x8a}, + {0x27, 0x31}, + {0x30, 0x08}, + {0x32, 0x32}, + {0x33, 0x35}, + {0x35, 0xff}, + {0x3a, 0x00}, + {0x37, 0x10}, + {0x38, 0x10}, + {0x39, 0x02}, + {0x42, 0x60}, + {0x4a, 0x80}, + {0x4b, 0x04}, + {0x4d, 0x91}, + {0x5d, 0xc8}, + {0x50, 0x36}, + {0x51, 0x36}, + {0x52, 0x36}, + {0x53, 0x36}, + {0x63, 0x60}, + {0x64, 0x10}, + {0x65, 0x20}, + {0x68, 0x04}, + {0x69, 0x29}, + {0x70, 0x20}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x40}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x60}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x80}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0xa0}, + {0x71, 0x70}, + {0x72, 0x04}, + {0x73, 0x00}, + {0x70, 0x1f}, + {0x76, 0x38}, + {0x77, 0xa6}, + {0x78, 0x0c}, + {0x79, 0x80}, + {0x7f, 0x14}, + {0x85, 0x08}, + {0xcd, 0xf4}, + {0x90, 0x33}, + {0x86, 0x00}, + {0x87, 0x0f}, + {0x89, 0x00}, + {0x8b, 0x44}, + {0x8c, 0x66}, + {0x9d, 0xc1}, + {0x8a, 0x10}, + {0xad, 0x40}, + {0xa0, 0x44}, + {0xc0, 0x08}, + {0xc1, 0x10}, + {0xc2, 0x08}, + {0xc3, 0x08}, + {0xc4, 0x08}, + {0xc5, 0xf0}, + {0xc6, 0x40}, + {0xc7, 0x00}, + {0xc8, 0x1a}, + {0xc9, 0x80}, + {0xca, 0x23}, + {0xcb, 0x24}, + {0xcc, 0xf4}, + {0xce, 0x74}, + {0x00, 0x00}, + {0xbd, 0x01}, + {0xb8, 0x00}, +}; + static const struct m88ds3103_reg_val m88rs6000_dvbs_init_reg_vals[] = { {0x23, 0x07}, {0x08, 0x03}, diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 3e383912bcfd8..bace7bccf3352 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -12,9 +12,6 @@ #include #include -#define TS2020_XTAL_FREQ 27000 /* in kHz */ -#define FREQ_OFFSET_LOW_SYM_RATE 3000 - struct ts2020_priv { struct i2c_client *client; struct mutex regmap_mutex; @@ -32,8 +29,6 @@ struct ts2020_priv { bool dont_poll:1; u32 frequency_div; /* LO output divider switch frequency */ u32 frequency_khz; /* actual used LO frequency */ -#define TS2020_M88TS2020 0 -#define TS2020_M88TS2022 1 u8 tuner; }; @@ -157,7 +152,7 @@ static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset) ret |= regmap_write(priv->regmap, 0x51, 0x1f); ret |= regmap_write(priv->regmap, 0x50, offset); ret |= regmap_write(priv->regmap, 0x50, 0x00); - msleep(20); + return ret; } @@ -189,13 +184,10 @@ static int ts2020_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct ts2020_priv *priv = fe->tuner_priv; - int ret; - unsigned int utmp; - u32 f3db, gdiv28; - u16 u16tmp, value, lpf_coeff; + u32 utmp, f3db, gdiv28, f_ref_khz, f_vco_khz, div_ref, div_out, pll_n; + u16 u16tmp, lpf_coeff = 2766; u8 buf[3], reg10, lpf_mxdiv, mlpf_max, mlpf_min, nlpf; - unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n; - unsigned int frequency_khz = c->frequency; + bool spectrum = (c->algorithm == ALGORITHM_SPECTRUM); /* * Integer-N PLL synthesizer @@ -204,16 +196,25 @@ static int ts2020_set_params(struct dvb_frontend *fe) f_ref_khz = TS2020_XTAL_FREQ; div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000); + if (priv->tuner == TS2020_M88TS2022) + regmap_read(priv->regmap, 0x62, &utmp); + /* select LO output divider */ - if (frequency_khz < priv->frequency_div) { + if (c->frequency < 493714) { + div_out = 8; + reg10 = 0x10; + utmp |= 0x02; + } else if (c->frequency >= 493714 && c->frequency < priv->frequency_div) { div_out = 4; reg10 = 0x10; + utmp &= 0xfd; } else { div_out = 2; reg10 = 0x00; + utmp &= 0xfd; } - f_vco_khz = frequency_khz * div_out; + f_vco_khz = c->frequency * div_out; pll_n = f_vco_khz * div_ref / f_ref_khz; pll_n += pll_n % 2; priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out; @@ -223,92 +224,91 @@ static int ts2020_set_params(struct dvb_frontend *fe) f_vco_khz, pll_n, div_ref, div_out); if (priv->tuner == TS2020_M88TS2020) { - lpf_coeff = 2766; reg10 |= 0x01; - ret = regmap_write(priv->regmap, 0x10, reg10); + regmap_write(priv->regmap, 0x10, reg10); + + u16tmp = pll_n - 1024; + buf[0] = (u16tmp >> 8) & 0x0f; + buf[1] = u16tmp & 0xff; + buf[2] = div_ref - 8; } else { - lpf_coeff = 3200; reg10 |= 0x0b; - ret = regmap_write(priv->regmap, 0x10, reg10); - ret |= regmap_write(priv->regmap, 0x11, 0x40); - } + regmap_write(priv->regmap, 0x10, reg10); + regmap_write(priv->regmap, 0x11, 0x40); + regmap_write(priv->regmap, 0x62, utmp); + + if (pll_n < 4095) + u16tmp = pll_n - 1024; + else if (pll_n < 6143) + u16tmp = pll_n + 1024; + else + u16tmp = pll_n + 3072; - u16tmp = pll_n - 1024; - buf[0] = (u16tmp >> 8) & 0xff; - buf[1] = (u16tmp >> 0) & 0xff; - buf[2] = div_ref - 8; + buf[0] = (u16tmp >> 8) & 0x3f; + buf[1] = u16tmp & 0xff; + buf[2] = div_ref - 8; + } - ret |= regmap_write(priv->regmap, 0x01, buf[0]); - ret |= regmap_write(priv->regmap, 0x02, buf[1]); - ret |= regmap_write(priv->regmap, 0x03, buf[2]); + regmap_write(priv->regmap, 0x01, buf[0]); + regmap_write(priv->regmap, 0x02, buf[1]); + regmap_write(priv->regmap, 0x03, buf[2]); + ts2020_tuner_gate_ctrl(fe, 0x10); - ret |= ts2020_tuner_gate_ctrl(fe, 0x10); - if (ret < 0) - return -ENODEV; + /* Tuner RF */ + ts2020_tuner_gate_ctrl(fe, 0x08); + regmap_read(priv->regmap, 0x3c, &utmp); - ret |= ts2020_tuner_gate_ctrl(fe, 0x08); + if (utmp == 0) + ts2020_tuner_gate_ctrl(fe, 0x08); - /* Tuner RF */ if (priv->tuner == TS2020_M88TS2020) - ret |= ts2020_set_tuner_rf(fe); - - gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; - ret |= regmap_write(priv->regmap, 0x04, gdiv28 & 0xff); - ret |= ts2020_tuner_gate_ctrl(fe, 0x04); - if (ret < 0) - return -ENODEV; + ts2020_set_tuner_rf(fe); + /* Tuner BB LPF */ if (priv->tuner == TS2020_M88TS2022) { - ret = regmap_write(priv->regmap, 0x25, 0x00); - ret |= regmap_write(priv->regmap, 0x27, 0x70); - ret |= regmap_write(priv->regmap, 0x41, 0x09); - ret |= regmap_write(priv->regmap, 0x08, 0x0b); - if (ret < 0) - return -ENODEV; + regmap_write(priv->regmap, 0x25, 0x00); + regmap_write(priv->regmap, 0x27, 0x70); + regmap_write(priv->regmap, 0x41, 0x09); + regmap_write(priv->regmap, 0x08, 0x0b); } + f3db = (c->bandwidth_hz / 1000 / 2) + priv->frequency_khz - c->frequency; + f3db = clamp(f3db, 100U, 40000U); + gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; + regmap_write(priv->regmap, 0x04, gdiv28); + ts2020_tuner_gate_ctrl(fe, 0x04); regmap_read(priv->regmap, 0x26, &utmp); - value = utmp; - - f3db = (c->bandwidth_hz / 1000 / 2) + 2000; - f3db += FREQ_OFFSET_LOW_SYM_RATE; /* FIXME: ~always too wide filter */ - f3db = clamp(f3db, 7000U, 40000U); - - gdiv28 = gdiv28 * 207 / (value * 2 + 151); + gdiv28 = gdiv28 * 207 / (utmp * 2 + 151); mlpf_max = gdiv28 * 135 / 100; + mlpf_max = clamp_val(mlpf_max, 1U, 63U); mlpf_min = gdiv28 * 78 / 100; - if (mlpf_max > 63) - mlpf_max = 63; - - nlpf = (f3db * gdiv28 * 2 / lpf_coeff / - (TS2020_XTAL_FREQ / 1000) + 1) / 2; - if (nlpf > 23) - nlpf = 23; - if (nlpf < 1) - nlpf = 1; - - lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) - * lpf_coeff * 2 / f3db + 1) / 2; + nlpf = (f3db * gdiv28 * 2 / lpf_coeff / (TS2020_XTAL_FREQ / 1000) + 1) / 2; + nlpf = clamp_val(nlpf, 1U, 31U); + lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) * lpf_coeff * 2 / f3db + 1) / 2; if (lpf_mxdiv < mlpf_min) { nlpf++; - lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) - * lpf_coeff * 2 / f3db + 1) / 2; + lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) * lpf_coeff * 2 / f3db + 1) / 2; } - if (lpf_mxdiv > mlpf_max) - lpf_mxdiv = mlpf_max; - - ret = regmap_write(priv->regmap, 0x04, lpf_mxdiv); - ret |= regmap_write(priv->regmap, 0x06, nlpf); + lpf_mxdiv = clamp_val(lpf_mxdiv, 1U, mlpf_max); + regmap_write(priv->regmap, 0x04, lpf_mxdiv); + regmap_write(priv->regmap, 0x06, nlpf); + ts2020_tuner_gate_ctrl(fe, 0x04); - ret |= ts2020_tuner_gate_ctrl(fe, 0x04); + /* Tuner BB gain */ + ts2020_tuner_gate_ctrl(fe, 0x01); - ret |= ts2020_tuner_gate_ctrl(fe, 0x01); + if (priv->tuner == TS2020_M88TS2022) { + regmap_read(priv->regmap, 0x21, &utmp); + if (utmp == 0) + ts2020_tuner_gate_ctrl(fe, 0x01); + } - msleep(80); + if (spectrum) + msleep(60); - return (ret < 0) ? -EINVAL : 0; + return 0; } static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) @@ -441,7 +441,7 @@ static void ts2020_stat_work(struct work_struct *work) c->strength.stat[0].scale = FE_SCALE_DECIBEL; if (!priv->dont_poll) - schedule_delayed_work(&priv->stat_work, msecs_to_jiffies(2000)); + schedule_delayed_work(&priv->stat_work, msecs_to_jiffies(1000)); return; err: dev_dbg(&client->dev, "failed=%d\n", ret); @@ -489,8 +489,8 @@ static int ts2020_read_signal_strength(struct dvb_frontend *fe, static const struct dvb_tuner_ops ts2020_tuner_ops = { .info = { .name = "TS2020", - .frequency_min_hz = 950 * MHz, - .frequency_max_hz = 2150 * MHz + .frequency_min_hz = 290 * MHz, + .frequency_max_hz = 2350 * MHz }, .init = ts2020_init, .release = ts2020_release, diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h index 84c2dc8c3a604..0caa3e80b288f 100644 --- a/drivers/media/dvb-frontends/ts2020.h +++ b/drivers/media/dvb-frontends/ts2020.h @@ -12,6 +12,13 @@ #include +#define TS2020_CLK_OUT_DISABLED 0 +#define TS2020_CLK_OUT_ENABLED 1 +#define TS2020_CLK_OUT_ENABLED_XTALOUT 2 +#define TS2020_M88TS2020 0 +#define TS2020_M88TS2022 1 +#define TS2020_XTAL_FREQ 27000 /* in kHz */ + struct ts2020_config { u8 tuner_address; u32 frequency_div; @@ -24,9 +31,6 @@ struct ts2020_config { /* * clock output */ -#define TS2020_CLK_OUT_DISABLED 0 -#define TS2020_CLK_OUT_ENABLED 1 -#define TS2020_CLK_OUT_ENABLED_XTALOUT 2 u8 clk_out:2; /* diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 471bd74667e3c..333ed4eea8e5f 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1178,7 +1178,7 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev) m88ds3103_pdata.clk = 27000000; m88ds3103_pdata.i2c_wr_max = 33; m88ds3103_pdata.ts_mode = M88DS3103_TS_PARALLEL; - m88ds3103_pdata.ts_clk = 16000; + m88ds3103_pdata.ts_clk = 20000; /* BUG: TS breaks at 24000 */ m88ds3103_pdata.ts_clk_pol = 1; m88ds3103_pdata.agc = 0x99; @@ -1193,7 +1193,7 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev) /* attach tuner */ ts2020_config.fe = dvb->fe[0]; - + ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm; dvb->i2c_client_tuner = dvb_module_probe("ts2020", "ts2022", i2c_adapter, 0x60, &ts2020_config); @@ -1208,6 +1208,8 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev) /* attach SEC */ a8293_pdata.dvb_frontend = dvb->fe[0]; + /* 461e has a tendency to have vIN undervoltage troubles. Slew mitigates this. */ + a8293_pdata.volt_slew_nanos_per_mv = 20; dvb->i2c_client_sec = dvb_module_probe("a8293", NULL, &dev->i2c_adap[dev->def_i2c_bus], 0x08, &a8293_pdata); @@ -1249,6 +1251,7 @@ static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev) /* attach tuner */ ts2020_config.fe = dvb->fe[0]; + ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm; dvb->i2c_client_tuner = dvb_module_probe("ts2020", "ts2022", i2c_adapter, 0x60, &ts2020_config); diff --git a/include/uapi/linux/dvb/frontend.h b/include/uapi/linux/dvb/frontend.h index 9b8a68fd197fa..4206d8d5b2fe8 100644 --- a/include/uapi/linux/dvb/frontend.h +++ b/include/uapi/linux/dvb/frontend.h @@ -775,6 +775,7 @@ enum fe_algorithm { ALGORITHM_COLD_BEST_GUESS, ALGORITHM_BLIND, ALGORITHM_BLIND_BEST_GUESS, + ALGORITHM_SPECTRUM, //ALGORITHM_SEARCH, //ALGORITHM_SEARCH_NEXT, //ALGORITHM_BANDWIDTH,