1
0
mirror of https://github.com/DigitalDevices/dddvb.git synced 2023-10-10 13:37:43 +02:00
dddvb/ddbridge/ddbridge-mod.c

1787 lines
45 KiB
C
Raw Normal View History

2015-08-05 17:22:42 +02:00
/*
* ddbridge.c: Digital Devices PCIe bridge driver
*
* Copyright (C) 2010-2017 Digital Devices GmbH
2015-08-05 17:22:42 +02:00
* Marcus Metzler <mocm@metzlerbros.de>
* Ralph Metzler <rjkm@metzlerbros.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 only, as published by the Free Software Foundation.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/
#include "ddbridge.h"
#include "ddbridge-regs.h"
#include <linux/dvb/mod.h>
2016-07-31 21:41:03 +02:00
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
2015-08-05 17:22:42 +02:00
inline s64 ConvertPCR(s64 a)
{
s32 ext;
s64 b;
b = div_s64_rem(a, 300 << 22, &ext);
return (b << 31) | ext;
}
inline s64 NegConvertPCR(s64 a)
{
s32 ext;
s64 b;
b = -div_s64_rem(a, 300 << 22, &ext);
if (ext != 0) {
ext = (300 << 22) - ext;
b -= 1;
}
return (b << 31) | ext;
}
inline s64 RoundPCR(s64 a)
{
s64 b = a + (HW_LSB_MASK>>1);
return b & ~(HW_LSB_MASK - 1);
}
inline s64 RoundPCRUp(s64 a)
{
s64 b = a + (HW_LSB_MASK - 1);
return b & ~(HW_LSB_MASK - 1);
}
inline s64 RoundPCRDown(s64 a)
{
return a & ~(HW_LSB_MASK - 1);
}
2017-04-16 21:20:52 +02:00
/* Calculating KF, LF from Symbolrate
*
* Symbolrate is usually calculated as (M/N) * 10.24 MS/s
*
* Common Values for M,N
* J.83 Annex A,
* Euro Docsis 6.952 MS/s : M = 869, N = 1280
* 6.900 MS/s : M = 345, N = 512
* 6.875 MS/s : M = 1375, N = 2048
* 6.111 MS/s : M = 6111, N = 10240
* J.83 Annex B **
* QAM64 5.056941 : M = 401, N = 812
* QAM256 5.360537 : M = 78, N = 149
* J.83 Annex C **
* 5.309734 : M = 1889, N = 3643
*
* For the present hardware
* KF' = 256 * M
* LF' = 225 * N
* or
* KF' = Symbolrate in Hz
* LF' = 9000000
*
* KF = KF' / gcd(KF',LF')
* LF = LF' / gcd(KF',LF')
* Note: LF must not be a power of 2.
* Maximum value for KF,LF = 13421727 ( 0x7FFFFFF )
* ** using these M,N values will result in a small err (<5ppm)
* calculating KF,LF directly gives the exact normative result
* but with rather large KF,LF values
*/
static inline u32 gcd(u32 u, u32 v)
2016-07-31 21:41:03 +02:00
{
2017-04-16 21:20:52 +02:00
int s = 0;
while (((u | v) & 1) == 0) {
s += 1;
u >>= 1;
v >>= 1;
}
while ((u & 1) == 0)
u >>= 1;
do {
while ((v & 1) == 0)
v >>= 1;
if (u > v) {
u32 t = v;
v = u;
u = t;
}
v = v - u;
} while (v != 0);
return (u << s);
2016-07-31 21:41:03 +02:00
}
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
static int mod_SendChannelCommand(struct ddb *dev, u32 Channel, u32 Command)
{
u32 ControlReg = ddbreadl(dev, CHANNEL_CONTROL(Channel));
2017-04-16 21:20:52 +02:00
2016-07-31 21:41:03 +02:00
ControlReg = (ControlReg & ~CHANNEL_CONTROL_CMD_MASK)|Command;
ddbwritel(dev, ControlReg, CHANNEL_CONTROL(Channel));
2017-04-16 21:20:52 +02:00
while (1) {
2016-07-31 21:41:03 +02:00
ControlReg = ddbreadl(dev, CHANNEL_CONTROL(Channel));
if (ControlReg == 0xFFFFFFFF)
return -EIO;
2017-04-16 21:20:52 +02:00
if ((ControlReg & CHANNEL_CONTROL_CMD_STATUS) == 0)
2016-07-31 21:41:03 +02:00
break;
}
if (ControlReg & CHANNEL_CONTROL_ERROR_CMD)
return -EINVAL;
return 0;
}
2015-08-05 17:22:42 +02:00
static int mod_busy(struct ddb *dev, int chan)
{
u32 creg;
while (1) {
creg = ddbreadl(dev, CHANNEL_CONTROL(chan));
if (creg == 0xffffffff)
return -EFAULT;
if ((creg & CHANNEL_CONTROL_BUSY) == 0)
break;
}
return 0;
}
void ddbridge_mod_output_stop(struct ddb_output *output)
{
struct ddb *dev = output->port->dev;
2016-07-31 21:41:03 +02:00
struct ddb_mod *mod = &dev->mod[output->nr];
2015-08-05 17:22:42 +02:00
mod->State = CM_IDLE;
2017-01-09 19:28:26 +01:00
mod->Control &= 0xfffffff0;
2016-07-31 21:41:03 +02:00
if (dev->link[0].info->version == 2)
2017-04-16 21:20:52 +02:00
mod_SendChannelCommand(dev, output->nr,
CHANNEL_CONTROL_CMD_FREE);
2017-01-09 19:28:26 +01:00
ddbwritel(dev, mod->Control, CHANNEL_CONTROL(output->nr));
2015-08-05 17:22:42 +02:00
#if 0
udelay(10);
ddbwritel(dev, CHANNEL_CONTROL_RESET, CHANNEL_CONTROL(output->nr));
udelay(10);
ddbwritel(dev, 0, CHANNEL_CONTROL(output->nr));
#endif
mod_busy(dev, output->nr);
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: mod_output_stop %d.%d\n", dev->nr, output->nr);
2015-08-05 17:22:42 +02:00
}
static void mod_set_incs(struct ddb_output *output)
{
s64 pcr;
struct ddb *dev = output->port->dev;
2016-07-31 21:41:03 +02:00
struct ddb_mod *mod = &dev->mod[output->nr];
2015-08-05 17:22:42 +02:00
pcr = ConvertPCR(mod->PCRIncrement);
ddbwritel(dev, pcr & 0xffffffff,
CHANNEL_PCR_ADJUST_OUTL(output->nr));
ddbwritel(dev, (pcr >> 32) & 0xffffffff,
CHANNEL_PCR_ADJUST_OUTH(output->nr));
mod_busy(dev, output->nr);
pcr = NegConvertPCR(mod->PCRDecrement);
ddbwritel(dev, pcr & 0xffffffff,
CHANNEL_PCR_ADJUST_INL(output->nr));
ddbwritel(dev, (pcr >> 32) & 0xffffffff,
CHANNEL_PCR_ADJUST_INH(output->nr));
mod_busy(dev, output->nr);
}
static void mod_set_rateinc(struct ddb *dev, u32 chan)
{
ddbwritel(dev, dev->mod[chan].rate_inc, CHANNEL_RATE_INCR(chan));
mod_busy(dev, chan);
}
2016-09-02 17:36:30 +02:00
static void mod_calc_rateinc(struct ddb_mod *mod)
{
u32 ri;
2017-04-16 21:20:52 +02:00
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: ibitrate %llu\n", mod->ibitrate);
pr_info("DDBridge: obitrate %llu\n", mod->obitrate);
2017-04-16 21:20:52 +02:00
2016-09-02 17:36:30 +02:00
if (mod->ibitrate != 0) {
u64 d = mod->obitrate - mod->ibitrate;
2017-04-16 21:20:52 +02:00
2016-09-02 17:36:30 +02:00
d = div64_u64(d, mod->obitrate >> 24);
if (d > 0xfffffe)
ri = 0xfffffe;
else
ri = d;
} else
ri = 0;
mod->rate_inc = ri;
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: ibr=%llu, obr=%llu, ri=0x%06x\n",
2016-09-02 17:36:30 +02:00
mod->ibitrate >> 32, mod->obitrate >> 32, ri);
}
static int mod_calc_obitrate(struct ddb_mod *mod)
{
2017-04-16 21:20:52 +02:00
u64 ofac;
2016-09-02 17:36:30 +02:00
ofac = (((u64) mod->symbolrate) << 32) * 188;
ofac = div_u64(ofac, 204);
mod->obitrate = ofac * (mod->modulation + 3);
return 0;
}
static int mod_set_symbolrate(struct ddb_mod *mod, u32 srate)
{
struct ddb *dev = mod->port->dev;
if (dev->link[0].info->version < 2) {
if (srate != 6900000)
return -EINVAL;
} else {
if (srate > 7100000)
return -EINVAL;
}
mod->symbolrate = srate;
mod_calc_obitrate(mod);
return 0;
}
2015-08-05 17:22:42 +02:00
static u32 qamtab[6] = { 0x000, 0x600, 0x601, 0x602, 0x903, 0x604 };
2017-04-16 21:20:52 +02:00
static int mod_set_modulation(struct ddb_mod *mod,
enum fe_modulation modulation)
2016-09-02 17:36:30 +02:00
{
struct ddb *dev = mod->port->dev;
if (modulation > QAM_256 || modulation < QAM_16)
return -EINVAL;
mod->modulation = modulation;
2017-04-16 21:20:52 +02:00
if (dev->link[0].info->version < 2)
ddbwritel(dev, qamtab[modulation],
CHANNEL_SETTINGS(mod->port->nr));
2016-09-02 17:36:30 +02:00
mod_calc_obitrate(mod);
return 0;
}
static int mod_set_frequency(struct ddb_mod *mod, u32 frequency)
{
u32 freq = frequency / 1000000;
2017-04-16 21:20:52 +02:00
2016-09-02 17:36:30 +02:00
if (frequency % 1000000)
return -EINVAL;
if ((freq - 114) % 8)
return -EINVAL;
if ((freq < 114) || (freq > 874))
return -EINVAL;
mod->frequency = frequency;
return 0;
}
static int mod_set_ibitrate(struct ddb_mod *mod, u64 ibitrate)
{
if (ibitrate > mod->obitrate)
return -EINVAL;
mod->ibitrate = ibitrate;
mod_calc_rateinc(mod);
return 0;
}
int ddbridge_mod_output_start(struct ddb_output *output)
2015-08-05 17:22:42 +02:00
{
struct ddb *dev = output->port->dev;
2016-07-31 21:41:03 +02:00
u32 Channel = output->nr;
struct ddb_mod *mod = &dev->mod[output->nr];
u32 Symbolrate = mod->symbolrate;
2017-01-22 02:05:44 +01:00
if (dev->link[0].info->version < 3)
mod_calc_rateinc(mod);
2015-08-05 17:22:42 +02:00
mod->LastInPacketCount = 0;
mod->LastOutPacketCount = 0;
mod->InOverflowPacketCount = 0;
mod->OutOverflowPacketCount = 0;
mod->LastInPackets = 0;
mod->LastOutPackets = 0;
mod->LastPCRAdjust = 0;
mod->PCRRunningCorr = 0;
/* we interrupt every 0x80000=524288 packets */
mod->MinInputPackets = 524288 / 2;
mod->PCRIncrement = 0;
mod->PCRDecrement = 0;
mod->State = CM_STARTUP;
mod->StateCounter = CM_STARTUP_DELAY;
2017-04-16 21:20:52 +02:00
2017-01-09 19:28:26 +01:00
if (dev->link[0].info->version == 3)
2017-04-16 21:20:52 +02:00
mod->Control = 0xfffffff0 &
ddbreadl(dev, CHANNEL_CONTROL(output->nr));
2017-01-09 19:28:26 +01:00
else
mod->Control = 0;
ddbwritel(dev, mod->Control, CHANNEL_CONTROL(output->nr));
2015-08-05 17:22:42 +02:00
udelay(10);
2017-04-16 21:20:52 +02:00
ddbwritel(dev, mod->Control | CHANNEL_CONTROL_RESET,
CHANNEL_CONTROL(output->nr));
2015-08-05 17:22:42 +02:00
udelay(10);
2017-01-09 19:28:26 +01:00
ddbwritel(dev, mod->Control, CHANNEL_CONTROL(output->nr));
2015-08-05 17:22:42 +02:00
2016-07-31 21:41:03 +02:00
if (dev->link[0].info->version == 2) {
2016-09-02 17:36:30 +02:00
u32 Output = (mod->frequency - 114000000) / 8000000;
2016-07-31 21:41:03 +02:00
u32 KF = Symbolrate;
u32 LF = 9000000UL;
2017-04-16 21:20:52 +02:00
u32 d = gcd(KF, LF);
2016-07-31 21:41:03 +02:00
u32 checkLF;
ddbwritel(dev, mod->modulation - 1, CHANNEL_SETTINGS(Channel));
2016-07-31 21:41:03 +02:00
ddbwritel(dev, Output, CHANNEL_SETTINGS2(Channel));
2017-04-16 21:20:52 +02:00
2016-07-31 21:41:03 +02:00
KF = KF / d;
LF = LF / d;
2017-04-16 21:20:52 +02:00
while ((KF > KFLF_MAX) || (LF > KFLF_MAX)) {
2016-07-31 21:41:03 +02:00
KF >>= 1;
LF >>= 1;
}
2017-04-16 21:20:52 +02:00
2016-07-31 21:41:03 +02:00
checkLF = LF;
while ((checkLF & 1) == 0)
checkLF >>= 1;
if (checkLF <= 1)
return -EINVAL;
2015-08-05 17:22:42 +02:00
2017-04-16 21:20:52 +02:00
pr_info("DDBridge: KF=%u LF=%u Output=%u mod=%u\n",
KF, LF, Output, mod->modulation);
2016-07-31 21:41:03 +02:00
ddbwritel(dev, KF, CHANNEL_KF(Channel));
ddbwritel(dev, LF, CHANNEL_LF(Channel));
2017-04-16 21:20:52 +02:00
if (mod_SendChannelCommand(dev, Channel,
CHANNEL_CONTROL_CMD_SETUP))
2016-07-31 21:41:03 +02:00
return -EINVAL;
2017-01-09 19:28:26 +01:00
mod->Control |= CHANNEL_CONTROL_ENABLE_DVB;
2017-04-16 21:20:52 +02:00
} else if (dev->link[0].info->version == 1) {
2016-07-31 21:41:03 +02:00
/* QAM: 600 601 602 903 604 = 16 32 64 128 256 */
/* ddbwritel(dev, 0x604, CHANNEL_SETTINGS(output->nr)); */
2017-04-16 21:20:52 +02:00
ddbwritel(dev, qamtab[mod->modulation],
CHANNEL_SETTINGS(output->nr));
mod->Control |= (CHANNEL_CONTROL_ENABLE_IQ |
CHANNEL_CONTROL_ENABLE_DVB);
} else if (dev->link[0].info->version == 3) {
2017-04-16 21:20:52 +02:00
mod->Control |= (CHANNEL_CONTROL_ENABLE_IQ |
CHANNEL_CONTROL_ENABLE_DVB);
2017-01-09 19:28:26 +01:00
}
if (dev->link[0].info->version < 3) {
mod_set_rateinc(dev, output->nr);
mod_set_incs(output);
2016-07-31 21:41:03 +02:00
}
mod->Control |= CHANNEL_CONTROL_ENABLE_SOURCE;
2015-08-05 17:22:42 +02:00
ddbwritel(dev, mod->Control, CHANNEL_CONTROL(output->nr));
2016-07-31 21:41:03 +02:00
if (dev->link[0].info->version == 2)
2017-04-16 21:20:52 +02:00
if (mod_SendChannelCommand(dev, Channel,
CHANNEL_CONTROL_CMD_UNMUTE))
2016-07-31 21:41:03 +02:00
return -EINVAL;
2017-01-09 19:28:26 +01:00
pr_info("DDBridge: mod_output_start %d.%d ctrl=%08x\n",
dev->nr, output->nr, mod->Control);
return 0;
2015-08-05 17:22:42 +02:00
}
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
2016-07-31 21:41:03 +02:00
static int mod_write_max2871(struct ddb *dev, u32 val)
{
ddbwritel(dev, val, MAX2871_OUTDATA);
2017-04-16 21:20:52 +02:00
ddbwritel(dev, MAX2871_CONTROL_CE | MAX2871_CONTROL_WRITE,
MAX2871_CONTROL);
while (1) {
2016-07-31 21:41:03 +02:00
u32 ControlReg = ddbreadl(dev, MAX2871_CONTROL);
2017-04-16 21:20:52 +02:00
2016-07-31 21:41:03 +02:00
if (ControlReg == 0xFFFFFFFF)
return -EIO;
if ((ControlReg & MAX2871_CONTROL_WRITE) == 0)
break;
}
return 0;
}
static u32 max2871_fsm[6] = {
2017-01-22 02:05:44 +01:00
0x00730040, 0x600080A1, 0x510061C2, 0x010000CB, 0x6199003C, 0x60440005,
};
static u32 max2871_sdr[6] = {
2017-01-22 02:05:44 +01:00
0x007A8098, 0x600080C9, 0x510061C2, 0x010000CB, 0x6199003C, 0x60440005
};
static int mod_setup_max2871(struct ddb *dev, u32 *reg)
2017-01-22 02:05:44 +01:00
{
int status = 0;
int i, j;
u32 val;
2017-04-16 21:20:52 +02:00
2017-01-22 02:05:44 +01:00
ddbwritel(dev, MAX2871_CONTROL_CE, MAX2871_CONTROL);
msleep(30);
for (i = 0; i < 2; i++) {
for (j = 5; j >= 0; j--) {
val = reg[j];
2017-04-16 21:20:52 +02:00
if (j == 4)
2017-01-22 02:05:44 +01:00
val &= 0xFFFFFEDF;
status = mod_write_max2871(dev, reg[j]);
if (status)
break;
msleep(30);
}
}
if (status == 0) {
u32 ControlReg;
if (reg[3] & (1 << 24))
msleep(100);
ControlReg = ddbreadl(dev, MAX2871_CONTROL);
if ((ControlReg & MAX2871_CONTROL_LOCK) == 0)
status = -EIO;
status = mod_write_max2871(dev, reg[4]);
}
return status;
}
2017-04-16 21:20:52 +02:00
static int mod_fsm_setup(struct ddb *dev, u32 FrequencyPlan,
u32 MaxUsedChannels)
2016-07-31 21:41:03 +02:00
{
int status = 0;
u32 Capacity;
u32 tmp = ddbreadl(dev, FSM_STATUS);
2017-04-16 21:20:52 +02:00
2016-07-31 21:41:03 +02:00
if ((tmp & FSM_STATUS_READY) == 0) {
status = mod_setup_max2871(dev, max2871_fsm);
2016-07-31 21:41:03 +02:00
if (status)
return status;
ddbwritel(dev, FSM_CMD_RESET, FSM_CONTROL);
2017-04-16 21:20:52 +02:00
msleep(20);
2016-07-31 21:41:03 +02:00
tmp = ddbreadl(dev, FSM_STATUS);
if ((tmp & FSM_STATUS_READY) == 0)
return -1;
}
Capacity = ddbreadl(dev, FSM_CAPACITY);
if (((tmp & FSM_STATUS_QAMREADY) != 0) &&
((Capacity & FSM_CAPACITY_INUSE) != 0))
return -EBUSY;
2017-04-16 21:20:52 +02:00
2016-07-31 21:41:03 +02:00
ddbwritel(dev, FSM_CMD_SETUP, FSM_CONTROL);
2017-04-16 21:20:52 +02:00
msleep(20);
2016-07-31 21:41:03 +02:00
tmp = ddbreadl(dev, FSM_STATUS);
2017-04-16 21:20:52 +02:00
2016-07-31 21:41:03 +02:00
if ((tmp & FSM_STATUS_QAMREADY) == 0)
return -1;
2017-04-16 21:20:52 +02:00
2016-07-31 21:41:03 +02:00
if (MaxUsedChannels == 0)
MaxUsedChannels = (Capacity & FSM_CAPACITY_CUR) >> 16;
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: max used chan = %u\n", MaxUsedChannels);
2017-04-16 21:20:52 +02:00
if (MaxUsedChannels <= 1)
2016-07-31 21:41:03 +02:00
ddbwritel(dev, FSM_GAIN_N1, FSM_GAIN);
else if (MaxUsedChannels <= 2)
ddbwritel(dev, FSM_GAIN_N2, FSM_GAIN);
2017-04-16 21:20:52 +02:00
else if (MaxUsedChannels <= 4)
2016-07-31 21:41:03 +02:00
ddbwritel(dev, FSM_GAIN_N4, FSM_GAIN);
else if (MaxUsedChannels <= 8)
ddbwritel(dev, FSM_GAIN_N8, FSM_GAIN);
else if (MaxUsedChannels <= 16)
ddbwritel(dev, FSM_GAIN_N16, FSM_GAIN);
else if (MaxUsedChannels <= 24)
ddbwritel(dev, FSM_GAIN_N24, FSM_GAIN);
else
ddbwritel(dev, FSM_GAIN_N96, FSM_GAIN);
ddbwritel(dev, FSM_CONTROL_ENABLE, FSM_CONTROL);
return status;
}
static int mod_set_vga(struct ddb *dev, u32 Gain)
{
2017-04-16 21:20:52 +02:00
if (Gain > 255)
2016-07-31 21:41:03 +02:00
return -EINVAL;
ddbwritel(dev, Gain, RF_VGA);
return 0;
}
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
2015-08-05 17:22:42 +02:00
static void mod_write_dac_register(struct ddb *dev, u8 Index, u8 Value)
{
u32 RegValue = 0;
ddbwritel(dev, Value, DAC_WRITE_DATA);
ddbwritel(dev, DAC_CONTROL_STARTIO | Index, DAC_CONTROL);
do {
RegValue = ddbreadl(dev, DAC_CONTROL);
} while ((RegValue & DAC_CONTROL_STARTIO) != 0);
}
static void mod_write_dac_register2(struct ddb *dev, u8 Index, u16 Value)
{
u32 RegValue = 0;
ddbwritel(dev, Value, DAC_WRITE_DATA);
ddbwritel(dev, DAC_CONTROL_STARTIO | 0x20 | Index, DAC_CONTROL);
do {
RegValue = ddbreadl(dev, DAC_CONTROL);
} while ((RegValue & DAC_CONTROL_STARTIO) != 0);
}
static int mod_read_dac_register(struct ddb *dev, u8 Index, u8 *pValue)
{
u32 RegValue = 0;
ddbwritel(dev, DAC_CONTROL_STARTIO | 0x80 | Index, DAC_CONTROL);
do {
RegValue = ddbreadl(dev, DAC_CONTROL);
} while ((RegValue & DAC_CONTROL_STARTIO) != 0);
RegValue = ddbreadl(dev, DAC_READ_DATA);
*pValue = (u8) RegValue;
return 0;
}
static void mod_set_up_converter_vco1(struct ddb *dev, u32 Value)
{
u32 RegValue = 0;
/* Extra delay before writing N divider */
if ((Value & 0x03) == 0x02)
msleep(50);
do {
RegValue = ddbreadl(dev, VCO1_CONTROL);
} while ((RegValue & VCO1_CONTROL_WRITE) != 0);
if ((RegValue & VCO1_CONTROL_CE) == 0) {
RegValue |= VCO1_CONTROL_CE;
ddbwritel(dev, RegValue, VCO1_CONTROL);
msleep(20);
}
ddbwritel(dev, Value, VCO1_DATA);
ddbwritel(dev, RegValue | VCO1_CONTROL_WRITE, VCO1_CONTROL);
}
static void mod_set_up_converter_vco2(struct ddb *dev, u32 Value)
{
u32 RegValue = 0;
/* Extra delay before writing N divider */
if ((Value & 0x03) == 0x02)
msleep(50);
do {
RegValue = ddbreadl(dev, VCO2_CONTROL);
} while ((RegValue & VCO2_CONTROL_WRITE) != 0);
if ((RegValue & VCO2_CONTROL_CE) == 0) {
RegValue |= VCO2_CONTROL_CE;
ddbwritel(dev, RegValue, VCO2_CONTROL);
msleep(20);
}
ddbwritel(dev, Value, VCO2_DATA);
ddbwritel(dev, RegValue | VCO2_CONTROL_WRITE, VCO2_CONTROL);
}
static void mod_set_down_converter_vco(struct ddb *dev, u32 Value)
{
u32 RegValue = 0;
do {
RegValue = ddbreadl(dev, VCO3_CONTROL);
} while ((RegValue & VCO3_CONTROL_WRITE) != 0);
if ((RegValue & VCO3_CONTROL_CE) == 0) {
RegValue |= VCO3_CONTROL_CE;
ddbwritel(dev, RegValue, VCO3_CONTROL);
msleep(20);
}
ddbwritel(dev, Value, VCO3_DATA);
ddbwritel(dev, RegValue | VCO3_CONTROL_WRITE, VCO3_CONTROL);
}
static int mod_set_attenuator(struct ddb *dev, u32 Value)
{
if (Value > 31)
return -EINVAL;
ddbwritel(dev, Value, RF_ATTENUATOR);
return 0;
}
static void mod_si598_readreg(struct ddb *dev, u8 index, u8 *val)
{
ddbwritel(dev, index, CLOCKGEN_INDEX);
ddbwritel(dev, 1, CLOCKGEN_CONTROL);
usleep_range(5000, 6000);
*val = ddbreadl(dev, CLOCKGEN_READDATA);
}
static void mod_si598_writereg(struct ddb *dev, u8 index, u8 val)
{
ddbwritel(dev, index, CLOCKGEN_INDEX);
ddbwritel(dev, val, CLOCKGEN_WRITEDATA);
ddbwritel(dev, 3, CLOCKGEN_CONTROL);
usleep_range(5000, 6000);
}
static int mod_set_si598(struct ddb *dev, u32 freq)
{
u8 Data[6];
u64 fDCO = 0;
u64 RFreq = 0;
u32 fOut = 10000000;
u64 m_fXtal = 0;
u32 N = 0;
u64 HSDiv = 0;
u32 fxtal;
u64 MinDiv, MaxDiv, Div;
u64 RF;
if (freq < 10000000 || freq > 525000000)
return -EINVAL;
mod_si598_writereg(dev, 137, 0x10);
if (m_fXtal == 0) {
mod_si598_writereg(dev, 135, 0x01);
mod_si598_readreg(dev, 7, &Data[0]);
mod_si598_readreg(dev, 8, &Data[1]);
mod_si598_readreg(dev, 9, &Data[2]);
mod_si598_readreg(dev, 10, &Data[3]);
mod_si598_readreg(dev, 11, &Data[4]);
mod_si598_readreg(dev, 12, &Data[5]);
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: Data = %02x %02x %02x %02x %02x %02x\n",
2015-08-05 17:22:42 +02:00
Data[0], Data[1], Data[2], Data[3], Data[4], Data[5]);
RFreq = (((u64)Data[1] & 0x3F) << 32) | ((u64)Data[2] << 24) |
((u64)Data[3] << 16) | ((u64)Data[4] << 8) |
((u64)Data[5]);
if (RFreq == 0)
return -EINVAL;
HSDiv = ((Data[0] & 0xE0) >> 5) + 4;
if (HSDiv == 8 || HSDiv == 10)
return -EINVAL;
N = (((u32)(Data[0] & 0x1F) << 2) |
((u32)(Data[1] & 0xE0) >> 6)) + 1;
fDCO = fOut * (u64)(HSDiv * N);
m_fXtal = fDCO << 28;
2017-04-16 21:20:52 +02:00
pr_info("DDBridge: fxtal %016llx rfreq %016llx\n",
m_fXtal, RFreq);
2015-08-05 17:22:42 +02:00
m_fXtal += RFreq >> 1;
m_fXtal = div64_u64(m_fXtal, RFreq);
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: fOut = %d fXtal = %d fDCO = %d HDIV = %2d, N = %3d\n",
2015-08-05 17:22:42 +02:00
(u32) fOut, (u32) m_fXtal, (u32) fDCO, (u32) HSDiv, N);
}
fOut = freq;
MinDiv = 4850000000ULL; do_div(MinDiv, freq); MinDiv += 1;
MaxDiv = 5670000000ULL; do_div(MaxDiv, freq);
Div = 5260000000ULL; do_div(Div, freq);
if (Div < MinDiv)
Div = Div + 1;
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: fOut = %u MinDiv = %llu MaxDiv = %llu StartDiv = %llu\n",
2015-08-05 17:22:42 +02:00
fOut, MinDiv, MaxDiv, Div);
if (Div <= 11) {
N = 1;
HSDiv = Div;
} else {
int retry = 100;
while (retry > 0) {
N = 0;
HSDiv = Div;
while ((HSDiv > 11) /*|| ((HSDiv * N) != Div)*/) {
N = N + 2;
HSDiv = Div;
do_div(HSDiv, N);
if (N > 128)
break;
}
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: %3d: %llu %llu %llu %u\n",
2015-08-05 17:22:42 +02:00
retry, Div, HSDiv * N, HSDiv, N);
if (HSDiv * N < MinDiv)
Div = Div + 2;
else if (HSDiv * N > MaxDiv)
Div = Div - 2;
else
break;
retry = retry - 1;
}
if (retry == 0) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: FAIL\n");
2015-08-05 17:22:42 +02:00
return -EINVAL;
}
}
if (HSDiv == 8 || HSDiv == 10) {
HSDiv = HSDiv >> 1;
N = N * 2;
}
if (HSDiv < 4)
return -EINVAL;
fDCO = (u64)fOut * (u64)N * (u64)HSDiv;
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: fdco %16llx\n", fDCO);
2015-08-05 17:22:42 +02:00
RFreq = fDCO<<28;
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: %16llx %16llx\n", fDCO, RFreq);
2015-08-05 17:22:42 +02:00
fxtal = m_fXtal;
do_div(RFreq, fxtal);
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: %16llx %d\n", RFreq, fxtal);
2015-08-05 17:22:42 +02:00
RF = RFreq;
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: fOut = %u fXtal = %llu fDCO = %llu HSDIV = %llu, N = %u, RFreq = %llu\n",
2015-08-05 17:22:42 +02:00
fOut, m_fXtal, fDCO, HSDiv, N, RFreq);
Data[0] = (u8)(((HSDiv - 4) << 5) | ((N - 1) >> 2));
Data[1] = (u8)((((N - 1) & 0x03) << 6) | ((RF >> 32) & 0x3F));
Data[2] = (u8)((RF >> 24) & 0xFF);
Data[3] = (u8)((RF >> 16) & 0xFF);
Data[4] = (u8)((RF >> 8) & 0xFF);
Data[5] = (u8)((RF) & 0xFF);
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: Data = %02x %02x %02x %02x %02x %02x\n",
2015-08-05 17:22:42 +02:00
Data[0], Data[1], Data[2], Data[3], Data[4], Data[5]);
mod_si598_writereg(dev, 7, Data[0]);
mod_si598_writereg(dev, 8, Data[1]);
mod_si598_writereg(dev, 9, Data[2]);
mod_si598_writereg(dev, 10, Data[3]);
mod_si598_writereg(dev, 11, Data[4]);
mod_si598_writereg(dev, 12, Data[5]);
mod_si598_writereg(dev, 137, 0x00);
mod_si598_writereg(dev, 135, 0x40);
return 0;
}
static void mod_bypass_equalizer(struct ddb *dev, int bypass)
{
u32 RegValue;
RegValue = ddbreadl(dev, IQOUTPUT_CONTROL);
RegValue &= ~IQOUTPUT_CONTROL_BYPASS_EQUALIZER;
RegValue |= (bypass ? IQOUTPUT_CONTROL_BYPASS_EQUALIZER : 0x00);
ddbwritel(dev, RegValue, IQOUTPUT_CONTROL);
}
static int mod_set_equalizer(struct ddb *dev, u32 Num, s16 *cTable)
{
u32 i, adr = IQOUTPUT_EQUALIZER_0;
if (Num > 11)
return -EINVAL;
for (i = 0; i < 11 - Num; i += 1) {
ddbwritel(dev, 0, adr);
adr += 4;
}
for (i = 0; i < Num; i += 1) {
ddbwritel(dev, (u32) cTable[i], adr);
adr += 4;
}
return 0;
}
#if 0
static void mod_peak(struct ddb *dev, u32 Time, s16 *pIPeak, s16 *pQPeak)
{
u32 val;
val = ddbreadl(dev, IQOUTPUT_CONTROL);
val &= ~(IQOUTPUT_CONTROL_ENABLE_PEAK | IQOUTPUT_CONTROL_RESET_PEAK);
ddbwritel(dev, val, IQOUTPUT_CONTROL);
ddbwritel(dev, val | IQOUTPUT_CONTROL_RESET_PEAK, IQOUTPUT_CONTROL);
msleep(20);
ddbwritel(dev, val, IQOUTPUT_CONTROL);
ddbwritel(dev, val | IQOUTPUT_CONTROL_ENABLE_PEAK, IQOUTPUT_CONTROL);
msleep(Time);
ddbwritel(dev, val, IQOUTPUT_CONTROL);
val = ddbreadl(dev, IQOUTPUT_PEAK_DETECTOR);
*pIPeak = val & 0xffff;
*pQPeak = (val >> 16) & 0xffff;
}
#endif
static int mod_init_dac_input(struct ddb *dev)
{
u8 Set = 0;
u8 Hld = 0;
u8 Sample = 0;
u8 Seek = 0;
u8 ReadSeek = 0;
u8 SetTable[32];
u8 HldTable[32];
u8 SeekTable[32];
u8 Sample1 = 0xFF;
u8 Sample2 = 0xFF;
u8 SelectSample = 0xFF;
u8 DiffMin = 0xFF;
for (Sample = 0; Sample < 32; Sample++) {
Set = 0;
Hld = 0;
mod_write_dac_register(dev, 0x04, Set << 4 | Hld);
mod_write_dac_register(dev, 0x05, Sample);
mod_read_dac_register(dev, 0x06, &ReadSeek);
Seek = ReadSeek & 0x01;
SeekTable[Sample] = Seek;
HldTable[Sample] = 15;
for (Hld = 1; Hld < 16; Hld += 1) {
mod_write_dac_register(dev, 0x04, Set << 4 | Hld);
mod_read_dac_register(dev, 0x06, &ReadSeek);
if ((ReadSeek & 0x01) != Seek) {
HldTable[Sample] = Hld;
break;
}
}
Hld = 0;
SetTable[Sample] = 15;
for (Set = 1; Set < 16; Set += 1) {
mod_write_dac_register(dev, 0x04, Set << 4 | Hld);
mod_read_dac_register(dev, 0x06, &ReadSeek);
if ((ReadSeek & 0x01) != Seek) {
SetTable[Sample] = Set;
break;
}
}
}
Seek = 1;
for (Sample = 0; Sample < 32; Sample += 1) {
/* printk(" %2d: %d %2d %2d\n",
2017-04-16 21:20:52 +02:00
* Sample, SeekTable[Sample], SetTable[Sample],
* HldTable[Sample]);
*/
2015-08-05 17:22:42 +02:00
if (Sample1 == 0xFF && SeekTable[Sample] == 1 && Seek == 0)
Sample1 = Sample;
if (Sample1 != 0xFF && Sample2 == 0xFF &&
SeekTable[Sample] == 0 && Seek == 1)
Sample2 = Sample;
Seek = SeekTable[Sample];
}
if (Sample1 == 0xFF || Sample2 == 0xFF) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: No valid window found\n");
2015-08-05 17:22:42 +02:00
return -EINVAL;
}
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: Window = %d - %d\n", Sample1, Sample2);
2015-08-05 17:22:42 +02:00
for (Sample = Sample1; Sample < Sample2; Sample += 1) {
if (SetTable[Sample] < HldTable[Sample]) {
if (HldTable[Sample] - SetTable[Sample] < DiffMin) {
DiffMin = HldTable[Sample] - SetTable[Sample];
SelectSample = Sample;
}
}
}
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: Select Sample %d\n", SelectSample);
2015-08-05 17:22:42 +02:00
if (SelectSample == 0xFF) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: No valid sample found\n");
2015-08-05 17:22:42 +02:00
return -EINVAL;
}
if (HldTable[SelectSample] + SetTable[SelectSample] < 8) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: Too high jitter\n");
2015-08-05 17:22:42 +02:00
return -EINVAL;
}
mod_write_dac_register(dev, 0x04, 0x00);
mod_write_dac_register(dev, 0x05, (SelectSample - 1) & 0x1F);
mod_read_dac_register(dev, 0x06, &Seek);
mod_write_dac_register(dev, 0x05, (SelectSample + 1) & 0x1F);
mod_read_dac_register(dev, 0x06, &ReadSeek);
Seek &= ReadSeek;
mod_write_dac_register(dev, 0x05, SelectSample);
mod_read_dac_register(dev, 0x06, &ReadSeek);
Seek &= ReadSeek;
if ((Seek & 0x01) == 0) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: Insufficient timing margin\n");
2015-08-05 17:22:42 +02:00
return -EINVAL;
}
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: Done\n");
2015-08-05 17:22:42 +02:00
return 0;
}
static void mod_set_up1(struct ddb *dev, u32 Frequency, u32 Ref, u32 Ext)
{
u32 RDiv = Ext / Ref;
Frequency = Frequency / Ref;
mod_set_up_converter_vco1(dev, 0x360001 | (RDiv << 2));
mod_set_up_converter_vco1(dev, 0x0ff128);
mod_set_up_converter_vco1(dev, 0x02 | (Frequency << 8));
}
static void mod_set_up2(struct ddb *dev, u32 Frequency, u32 Ref, u32 Ext)
{
u32 Rdiv = Ext / Ref;
u32 PreScale = 8;
Frequency = Frequency / Ref;
mod_set_up_converter_vco2(dev, 0x360001 | (Rdiv << 2));
mod_set_up_converter_vco2(dev, 0x0fc128 |
(((PreScale - 8) / 8) << 22));
mod_set_up_converter_vco2(dev, 0x02 | ((Frequency / PreScale) << 8)
| (Frequency & (PreScale - 1)) << 2);
}
static int mod_set_down(struct ddb *dev, u32 Frequency, u32 Ref, u32 Ext)
{
u32 BandSelect = Ref * 8;
u32 RefMul = 1;
u32 RefDiv2 = 1;
u32 RefDiv = Ext * RefMul / (Ref * RefDiv2);
if (Frequency < 2200 || Frequency > 4000)
return -EINVAL;
Frequency = Frequency / Ref;
mod_set_down_converter_vco(dev, 0x0080003C |
((BandSelect & 0xFF) << 12));
mod_set_down_converter_vco(dev, 0x00000003);
mod_set_down_converter_vco(dev, 0x18001E42 | ((RefMul-1) << 25) |
((RefDiv2-1) << 24) | (RefDiv << 14));
mod_set_down_converter_vco(dev, 0x08008021);
mod_set_down_converter_vco(dev, Frequency << 15);
return 0;
}
static int mod_set_dac_clock(struct ddb *dev, u32 Frequency)
{
int hr, i;
if (Frequency) {
ddbwritel(dev, DAC_CONTROL_RESET, DAC_CONTROL);
msleep(20);
if (mod_set_si598(dev, Frequency)) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: mod_set_si598 failed\n");
2015-08-05 17:22:42 +02:00
return -1;
}
msleep(50);
ddbwritel(dev, 0x000, DAC_CONTROL);
msleep(20);
mod_write_dac_register(dev, 0, 0x02);
}
for (i = 0; i < 10; i++) {
hr = mod_init_dac_input(dev);
if (hr == 0)
break;
msleep(100);
}
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: mod_set_dac_clock OK\n");
2015-08-05 17:22:42 +02:00
return hr;
}
static void mod_set_dac_current(struct ddb *dev, u32 Current1, u32 Current2)
{
mod_write_dac_register2(dev, 0x0b, Current1 & 0x3ff);
mod_write_dac_register2(dev, 0x0f, Current2 & 0x3ff);
}
static void mod_output_enable(struct ddb *dev, int enable)
{
u32 RegValue;
RegValue = ddbreadl(dev, IQOUTPUT_CONTROL);
RegValue &= ~(IQOUTPUT_CONTROL_ENABLE | IQOUTPUT_CONTROL_RESET);
ddbwritel(dev, RegValue, IQOUTPUT_CONTROL);
if (enable) {
ddbwritel(dev, RegValue | IQOUTPUT_CONTROL_RESET,
IQOUTPUT_CONTROL);
msleep(20);
ddbwritel(dev, RegValue, IQOUTPUT_CONTROL);
ddbwritel(dev, RegValue | IQOUTPUT_CONTROL_ENABLE,
IQOUTPUT_CONTROL);
}
}
static int mod_set_iq(struct ddb *dev, u32 steps, u32 chan, u32 freq)
{
u32 i, j, k, fac = 8;
u32 s1 = 22, s2 = 33;
u64 amp = (1ULL << 17) - 1ULL;
u64 s = 0, c = (amp << s1), ss;
u64 frq = 0xC90FDAA22168C235ULL; /* PI << 62 */
u32 *iqtab;
u32 iqtabadr;
u32 regval;
iqtab = kmalloc((steps + 1) * 4, GFP_KERNEL);
if (!iqtab)
return -ENOMEM;
frq = div64_u64(frq, steps * fac) >> (61 - s2);
/* create sine table */
for (i = 0; i <= steps * fac / 4; i++) {
if (!(i & (fac - 1))) {
j = i / fac;
ss = s >> s1;
/* round? ss = ((s >> (s1 - 1)) + 1) >> 1; */
iqtab[j] = iqtab[steps / 2 - j] = ss;
iqtab[steps / 2 + j] = iqtab[steps - j] = -ss;
}
c -= ((s * frq) >> s2);
s += ((c * frq) >> s2);
}
iqtabadr = chan << 16;
ddbwritel(dev, chan & 0x0f, MODULATOR_IQTABLE_INDEX);
for (i = j = 0, k = steps / 4; i < steps; i++) {
ddbwritel(dev, (iqtabadr + i) | MODULATOR_IQTABLE_INDEX_SEL_I,
MODULATOR_IQTABLE_INDEX);
ddbwritel(dev, iqtab[j], MODULATOR_IQTABLE_DATA);
regval = ddbreadl(dev, MODULATOR_CONTROL);
ddbwritel(dev, (iqtabadr + i) | MODULATOR_IQTABLE_INDEX_SEL_Q,
MODULATOR_IQTABLE_INDEX);
ddbwritel(dev, iqtab[k], MODULATOR_IQTABLE_DATA);
regval = ddbreadl(dev, MODULATOR_CONTROL);
j += freq;
j %= steps;
k += freq;
k %= steps;
}
ddbwritel(dev, steps - 1, MODULATOR_IQTABLE_END);
kfree(iqtab);
return 0;
}
u32 eqtab[] = {
0x0000FFDB, 0x00000121, 0x0000FF0A, 0x000003D7,
0x000001C4, 0x000005A5, 0x000009CC, 0x0000F50D,
0x00001B23, 0x0000EEB7, 0x00006A28
};
static void mod_set_channelsumshift(struct ddb *dev, u32 shift)
{
ddbwritel(dev, (shift & 3) << 2, MODULATOR_CONTROL);
}
static void mod_pre_eq_gain(struct ddb *dev, u16 gain)
{
ddbwritel(dev, gain, IQOUTPUT_PRESCALER);
}
static void mod_post_eq_gain(struct ddb *dev, u16 igain, u16 qgain)
{
ddbwritel(dev, ((u32)qgain << 16) | igain, IQOUTPUT_POSTSCALER);
}
static int set_base_frequency(struct ddb *dev, u32 freq)
{
u32 Ext = 40;
u32 UP1Frequency = 290;
u32 UP2Frequency = 1896;
u32 down, freq10;
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: set base to %u\n", freq);
2015-08-05 17:22:42 +02:00
dev->mod_base.frequency = freq;
freq /= 1000000;
freq10 = dev->mod_base.flat_start + 4;
down = freq + 9 * 8 + freq10 + UP1Frequency + UP2Frequency;
if ((freq10 + 9 * 8) > (dev->mod_base.flat_end - 4)) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: Frequency out of range %d\n", freq10);
2015-08-05 17:22:42 +02:00
return -EINVAL;
}
if (down % 8) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: Invalid Frequency %d\n", down);
2015-08-05 17:22:42 +02:00
return -EINVAL;
}
return mod_set_down(dev, down, 8, Ext);
}
2016-07-31 21:41:03 +02:00
static int mod_init_1(struct ddb *dev, u32 Frequency)
2015-08-05 17:22:42 +02:00
{
int stat = 0;
u8 *buffer;
struct DDMOD_FLASH *flash;
u32 Ext = 40;
u32 UP1Frequency = 290;
u32 UP2Frequency = 1896;
u32 DownFrequency;
u32 FrequencyCH10;
u32 iqfreq, iqsteps, i;
buffer = kmalloc(4096, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
flash = (struct DDMOD_FLASH *) buffer;
ddbridge_flashread(dev, 0, buffer, DDMOD_FLASH_START, 4096);
if (flash->Magic != DDMOD_FLASH_MAGIC && flash->Magic != 1) {
stat = -EINVAL;
goto fail;
}
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: srate = %d\n", flash->DataSet[0].Symbolrate * 1000);
2015-08-05 17:22:42 +02:00
mod_output_enable(dev, 0);
stat = mod_set_dac_clock(dev, flash->DataSet[0].DACFrequency * 1000);
if (stat < 0) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: setting DAC clock failed\n");
2015-08-05 17:22:42 +02:00
goto fail;
}
mod_set_dac_current(dev, 512, 512);
ddbwritel(dev, flash->DataSet[0].Control2, IQOUTPUT_CONTROL2);
mod_set_up1(dev, UP1Frequency, 5, Ext);
mod_set_up2(dev, UP2Frequency, 8, Ext);
dev->mod_base.flat_start = flash->DataSet[0].FlatStart;
dev->mod_base.flat_end = flash->DataSet[0].FlatEnd;
Frequency /= 1000000;
FrequencyCH10 = flash->DataSet[0].FlatStart + 4;
DownFrequency = Frequency + 9 * 8 + FrequencyCH10 +
UP1Frequency + UP2Frequency;
2017-04-16 21:20:52 +02:00
pr_info("DDBridge: CH10 = %d, Down = %d\n",
FrequencyCH10, DownFrequency);
2015-08-05 17:22:42 +02:00
if ((FrequencyCH10 + 9 * 8) > (flash->DataSet[0].FlatEnd - 4)) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: Frequency out of range %d\n", FrequencyCH10);
2015-08-05 17:22:42 +02:00
stat = -EINVAL;
goto fail;
}
if (DownFrequency % 8 != 0) {
2016-10-10 00:19:16 +02:00
pr_err("DDBridge: Invalid Frequency %d\n", DownFrequency);
2015-08-05 17:22:42 +02:00
stat = -EINVAL;
goto fail;
}
mod_set_down(dev, DownFrequency, 8, Ext);
for (i = 0; i < 10; i++) {
2016-09-02 17:36:30 +02:00
struct ddb_mod *mod = &dev->mod[i];
mod->port = &dev->port[i];
2015-08-05 17:22:42 +02:00
ddbwritel(dev, 0, CHANNEL_CONTROL(i));
iqfreq = flash->DataSet[0].FrequencyFactor *
(FrequencyCH10 + (9 - i) * 8);
2016-10-10 00:19:16 +02:00
iqfreq += (dev->link[0].ids.hwid == 0x0203dd01) ? 22 : 0;
2015-08-05 17:22:42 +02:00
iqsteps = flash->DataSet[0].IQTableLength;
mod_set_iq(dev, iqsteps, i, iqfreq);
2016-09-02 17:36:30 +02:00
mod_set_modulation(mod, QAM_256);
mod_set_symbolrate(mod, 6900000);
2015-08-05 17:22:42 +02:00
}
mod_bypass_equalizer(dev, 1);
mod_set_equalizer(dev, 11, flash->DataSet[0].EQTap);
mod_bypass_equalizer(dev, 0);
mod_post_eq_gain(dev, flash->DataSet[0].PostScaleI,
flash->DataSet[0].PostScaleQ);
mod_pre_eq_gain(dev, flash->DataSet[0].PreScale);
/*mod_pre_eq_gain(dev, 0x0680);*/
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: prescaler %04x\n", flash->DataSet[0].PreScale);
2015-08-05 17:22:42 +02:00
mod_set_channelsumshift(dev, 2);
mod_output_enable(dev, 1);
/*mod_set_attenuator(dev, 10);*/
fail:
kfree(buffer);
return stat;
}
#define PACKET_CLOCKS (27000000ULL*1504)
#define FACTOR (1ULL << 22)
/*
2017-04-16 21:20:52 +02:00
* double Increment = FACTOR*PACKET_CLOCKS/double(m_OutputBitrate);
* double Decrement = FACTOR*PACKET_CLOCKS/double(m_InputBitrate);
* 27000000 * 1504 * 2^22 / (6900000 * 188 / 204) = 26785190066.1
*/
2015-08-05 17:22:42 +02:00
void ddbridge_mod_rate_handler(unsigned long data)
{
struct ddb_output *output = (struct ddb_output *) data;
struct ddb_dma *dma = output->dma;
struct ddb *dev = output->port->dev;
2016-07-31 21:41:03 +02:00
struct ddb_mod *mod = &dev->mod[output->nr];
2015-08-05 17:22:42 +02:00
u32 chan = output->nr;
u32 OutPacketCount;
u32 InPacketCount;
u64 OutPackets, InPackets;
s64 PCRAdjust;
u32 PCRAdjustExt, PCRAdjustExtFrac, InPacketDiff, OutPacketDiff;
s32 PCRCorr;
s64 pcr;
s64 PCRIncrementDiff;
s64 PCRIncrement;
u64 mul;
if (!mod->pcr_correction)
return;
spin_lock(&dma->lock);
ddbwritel(dev, mod->Control | CHANNEL_CONTROL_FREEZE_STATUS,
CHANNEL_CONTROL(output->nr));
OutPacketCount = ddbreadl(dev, CHANNEL_PKT_COUNT_OUT(chan));
if (OutPacketCount < mod->LastOutPacketCount)
mod->OutOverflowPacketCount += 1;
mod->LastOutPacketCount = OutPacketCount;
InPacketCount = ddbreadl(dev, CHANNEL_PKT_COUNT_IN(chan));
if (InPacketCount < mod->LastInPacketCount)
mod->InOverflowPacketCount += 1;
mod->LastInPacketCount = InPacketCount;
OutPackets = ((u64) (mod->OutOverflowPacketCount) << 20) |
OutPacketCount;
InPackets = ((u64) (mod->InOverflowPacketCount) << 20) |
InPacketCount;
PCRAdjust = (s64) ((u64) ddbreadl(dev,
CHANNEL_PCR_ADJUST_ACCUL(chan)) |
(((u64) ddbreadl(dev,
CHANNEL_PCR_ADJUST_ACCUH(chan))
<< 32)));
PCRAdjustExt = (u32)((PCRAdjust & 0x7FFFFFFF) >> 22);
PCRAdjustExtFrac = (u32)((PCRAdjust & 0x003FFFFF) >> 12);
PCRAdjust >>= 31;
InPacketDiff = (u32) (InPackets - mod->LastInPackets);
OutPacketDiff = (u32) (OutPackets - mod->LastOutPackets);
PCRCorr = 0;
switch (mod->State) {
case CM_STARTUP:
if (mod->StateCounter) {
if (mod->StateCounter == 1) {
if (mod->ibitrate == 0) {
mul = (0x1000000 *
(u64) (OutPacketDiff -
InPacketDiff -
InPacketDiff/1000));
if (OutPacketDiff)
mod->rate_inc =
2016-03-24 12:10:20 +01:00
div_u64(mul,
OutPacketDiff);
2015-08-05 17:22:42 +02:00
else
mod->rate_inc = 0;
mod_set_rateinc(dev, output->nr);
mod->PCRIncrement =
div_u64(26785190066ULL,
mod->modulation + 3);
if (InPacketDiff)
mod->PCRDecrement =
div_u64(mod->PCRIncrement *
2016-03-24 12:10:20 +01:00
(u64)
OutPacketDiff,
2015-08-05 17:22:42 +02:00
InPacketDiff);
else
mod->PCRDecrement = 0;
mod_set_incs(output);
} else {
mod->PCRIncrement =
div_u64(26785190066ULL,
mod->modulation + 3);
mod->PCRDecrement =
div_u64(FACTOR*PACKET_CLOCKS,
mod->ibitrate >> 32);
mod_set_incs(output);
}
}
mod->StateCounter--;
break;
} else if (InPacketDiff >= mod->MinInputPackets) {
mod->State = CM_ADJUST;
mod->Control |= CHANNEL_CONTROL_ENABLE_PCRADJUST;
mod->InPacketsSum = 0;
mod->OutPacketsSum = 0;
mod->PCRAdjustSum = 0;
mod->StateCounter = CM_AVERAGE;
}
break;
case CM_ADJUST:
if (InPacketDiff < mod->MinInputPackets) {
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: PCR Adjust reset IN: %u Min: %u\n",
2015-08-05 17:22:42 +02:00
InPacketDiff, mod->MinInputPackets);
mod->InPacketsSum = 0;
mod->OutPacketsSum = 0;
mod->PCRAdjustSum = 0;
mod->StateCounter = CM_AVERAGE;
ddbwritel(dev,
(mod->Control |
CHANNEL_CONTROL_FREEZE_STATUS) &
~CHANNEL_CONTROL_ENABLE_PCRADJUST,
CHANNEL_CONTROL(chan));
break;
}
mod->PCRAdjustSum += (s32) PCRAdjust;
mod->InPacketsSum += InPacketDiff;
mod->OutPacketsSum += OutPacketDiff;
if (mod->StateCounter--)
break;
if (mod->OutPacketsSum)
PCRIncrement = div_s64((s64)mod->InPacketsSum *
(s64)mod->PCRDecrement +
(s64)(mod->OutPacketsSum >> 1),
mod->OutPacketsSum);
else
PCRIncrement = 0;
if (mod->PCRAdjustSum > 0)
PCRIncrement = RoundPCRDown(PCRIncrement);
else
PCRIncrement = RoundPCRUp(PCRIncrement);
PCRIncrementDiff = PCRIncrement - mod->PCRIncrement;
if (PCRIncrementDiff > HW_LSB_MASK)
PCRIncrementDiff = HW_LSB_MASK;
if (PCRIncrementDiff < -HW_LSB_MASK)
PCRIncrementDiff = -HW_LSB_MASK;
mod->PCRIncrement += PCRIncrementDiff;
pcr = ConvertPCR(mod->PCRIncrement);
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: outl %016llx\n", pcr);
2015-08-05 17:22:42 +02:00
ddbwritel(dev, pcr & 0xffffffff,
CHANNEL_PCR_ADJUST_OUTL(output->nr));
ddbwritel(dev, (pcr >> 32) & 0xffffffff,
CHANNEL_PCR_ADJUST_OUTH(output->nr));
mod_busy(dev, chan);
PCRCorr = (s32) (PCRIncrementDiff >> HW_LSB_SHIFT);
mod->PCRRunningCorr += PCRCorr;
mod->InPacketsSum = 0;
mod->OutPacketsSum = 0;
mod->PCRAdjustSum = 0;
mod->StateCounter = CM_AVERAGE;
break;
default:
break;
}
ddbwritel(dev, mod->Control, CHANNEL_CONTROL(chan));
mod->LastInPackets = InPackets;
mod->LastOutPackets = OutPackets;
mod->LastPCRAdjust = (s32) PCRAdjust;
spin_unlock(&dma->lock);
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: chan %d out %016llx in %016llx indiff %08x\n",
2015-08-05 17:22:42 +02:00
chan, OutPackets, InPackets, InPacketDiff);
2016-10-10 00:19:16 +02:00
pr_info("DDBridge: cnt %d pcra %016llx pcraext %08x pcraextfrac %08x pcrcorr %08x pcri %016llx\n",
2015-08-05 17:22:42 +02:00
mod->StateCounter, PCRAdjust, PCRAdjustExt,
PCRAdjustExtFrac, PCRCorr, mod->PCRIncrement);
}
static int mod3_set_base_frequency(struct ddb *dev, u32 frequency)
{
u64 tmp;
2017-04-16 21:20:52 +02:00
if (frequency % 1000)
return -EINVAL;
if ((frequency < 114000000) || (frequency > 874000000))
return -EINVAL;
dev->mod_base.frequency = frequency;
tmp = frequency;
tmp <<= 33;
tmp = div64_s64(tmp, 4915200000);
2017-04-16 21:20:52 +02:00
pr_info("set base frequency = %u regs = 0x%08llx\n", frequency, tmp);
ddbwritel(dev, (u32) tmp, RFDAC_FCW);
return 0;
}
static void mod3_set_cfcw(struct ddb_mod *mod, u32 f)
{
struct ddb *dev = mod->port->dev;
s32 freq = f;
s32 dcf = dev->mod_base.frequency;
s64 tmp, srdac = 245760000;
u32 cfcw;
2017-04-16 21:20:52 +02:00
tmp = ((s64) (freq - dcf)) << 32;
tmp = div64_s64(tmp, srdac);
cfcw = (u32) tmp;
2017-04-16 21:20:52 +02:00
pr_info("f=%u cfcw = %08x nr = %u\n", f, cfcw, mod->port->nr);
ddbwritel(dev, cfcw, SDR_CHANNEL_CFCW(mod->port->nr));
}
static int mod3_set_frequency(struct ddb_mod *mod, u32 frequency)
{
2017-04-25 19:00:19 +02:00
#if 0
struct ddb *dev = mod->port->dev;
2017-04-16 21:20:52 +02:00
if (frequency % 1000)
return -EINVAL;
if ((frequency < 114000000) || (frequency > 874000000))
return -EINVAL;
if (frequency > dev->mod_base.frequency)
if (frequency - dev->mod_base.frequency > 100000000)
return -EINVAL;
else
if (dev->mod_base.frequency - frequency > 100000000)
return -EINVAL;
#endif
mod3_set_cfcw(mod, frequency);
return 0;
}
static int mod3_set_ari(struct ddb_mod *mod, u32 rate)
{
ddbwritel(mod->port->dev, rate, SDR_CHANNEL_ARICW(mod->port->nr));
return 0;
}
static int mod3_prop_proc(struct ddb_mod *mod, struct dtv_property *tvp)
{
2017-04-16 21:20:52 +02:00
switch (tvp->cmd) {
case MODULATOR_OUTPUT_ARI:
return mod3_set_ari(mod, tvp->u.data);
case MODULATOR_FREQUENCY:
return mod3_set_frequency(mod, tvp->u.data);
case MODULATOR_BASE_FREQUENCY:
return mod3_set_base_frequency(mod->port->dev, tvp->u.data);
}
return -EINVAL;
}
2016-09-02 17:36:30 +02:00
static int mod_prop_proc(struct ddb_mod *mod, struct dtv_property *tvp)
2015-08-05 17:22:42 +02:00
{
if (mod->port->dev->link[0].info->version == 3)
return mod3_prop_proc(mod, tvp);
2017-04-16 21:20:52 +02:00
switch (tvp->cmd) {
case MODULATOR_SYMBOL_RATE:
2016-09-02 17:36:30 +02:00
return mod_set_symbolrate(mod, tvp->u.data);
case MODULATOR_MODULATION:
2016-09-02 17:36:30 +02:00
return mod_set_modulation(mod, tvp->u.data);
case MODULATOR_FREQUENCY:
2016-09-02 17:36:30 +02:00
return mod_set_frequency(mod, tvp->u.data);
case MODULATOR_ATTENUATOR:
2016-09-02 17:36:30 +02:00
return mod_set_attenuator(mod->port->dev, tvp->u.data);
case MODULATOR_INPUT_BITRATE:
2016-09-02 17:36:30 +02:00
return mod_set_ibitrate(mod, tvp->u.data);
2016-09-02 17:36:30 +02:00
}
return 0;
2015-08-05 17:22:42 +02:00
}
int ddbridge_mod_do_ioctl(struct file *file, unsigned int cmd, void *parg)
2016-07-31 21:41:03 +02:00
{
struct dvb_device *dvbdev = file->private_data;
struct ddb_output *output = dvbdev->priv;
struct ddb *dev = output->port->dev;
2016-09-02 17:36:30 +02:00
struct ddb_mod *mod = &dev->mod[output->nr];
2016-07-31 21:41:03 +02:00
int ret = 0;
2017-04-16 21:20:52 +02:00
if (dev->link[0].info->version == 3 && cmd != FE_SET_PROPERTY)
return -EINVAL;
2016-07-31 21:41:03 +02:00
switch (cmd) {
2016-09-02 17:36:30 +02:00
case FE_SET_PROPERTY:
{
2017-04-16 21:20:52 +02:00
struct dtv_properties *tvps =
(struct dtv_properties __user *) parg;
2016-09-02 17:36:30 +02:00
struct dtv_property *tvp = NULL;
int i;
2017-04-16 21:20:52 +02:00
2016-09-02 17:36:30 +02:00
if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
return -EINVAL;
2017-04-16 21:20:52 +02:00
tvp = kmalloc(tvps->num * sizeof(struct dtv_property),
GFP_KERNEL);
2016-09-02 17:36:30 +02:00
if (!tvp) {
ret = -ENOMEM;
goto out;
}
if (copy_from_user(tvp, tvps->props, tvps->num *
sizeof(struct dtv_property))) {
ret = -EFAULT;
goto out;
}
for (i = 0; i < tvps->num; i++) {
2017-04-16 21:20:52 +02:00
ret = mod_prop_proc(mod, tvp + i);
if (ret < 0)
2016-09-02 17:36:30 +02:00
goto out;
(tvp + i)->result = ret;
}
2017-04-16 21:20:52 +02:00
out:
2016-09-02 17:36:30 +02:00
kfree(tvp);
return ret;
}
2016-07-31 21:41:03 +02:00
case DVB_MOD_SET:
{
struct dvb_mod_params *mp = parg;
if (dev->link[0].info->version < 2) {
if (mp->base_frequency != dev->mod_base.frequency)
if (set_base_frequency(dev, mp->base_frequency))
return -EINVAL;
} else {
int i, streams = dev->link[0].info->port_num;
dev->mod_base.frequency = mp->base_frequency;
for (i = 0; i < streams; i++) {
struct ddb_mod *mod = &dev->mod[i];
mod->port = &dev->port[i];
mod_set_modulation(mod, QAM_256);
mod_set_symbolrate(mod, 6900000);
mod_set_frequency(mod, dev->mod_base.frequency +
i * 8000000);
}
}
2016-07-31 21:41:03 +02:00
mod_set_attenuator(dev, mp->attenuator);
break;
}
case DVB_MOD_CHANNEL_SET:
{
struct dvb_mod_channel_params *cp = parg;
2016-09-02 17:36:30 +02:00
struct ddb_mod *mod = &dev->mod[output->nr];
2016-07-31 21:41:03 +02:00
int res;
2016-09-02 17:36:30 +02:00
res = mod_set_modulation(mod, cp->modulation);
2016-07-31 21:41:03 +02:00
if (res)
return res;
2016-09-02 17:36:30 +02:00
res = mod_set_ibitrate(mod, cp->input_bitrate);
if (res)
return res;
mod->pcr_correction = cp->pcr_correction;
2016-07-31 21:41:03 +02:00
break;
}
default:
ret = -EINVAL;
break;
}
return ret;
}
static int mod_init_2(struct ddb *dev, u32 Frequency)
{
int status, i;
2016-08-02 21:08:40 +02:00
int streams = dev->link[0].info->port_num;
2016-07-31 21:41:03 +02:00
dev->mod_base.frequency = Frequency;
status = mod_fsm_setup(dev, 0, 0);
2017-01-22 02:05:44 +01:00
if (status) {
pr_err("FSM setup failed!\n");
return -1;
}
2017-04-16 21:20:52 +02:00
2016-09-02 17:36:30 +02:00
for (i = 0; i < streams; i++) {
struct ddb_mod *mod = &dev->mod[i];
mod->port = &dev->port[i];
mod_set_modulation(mod, QAM_256);
mod_set_symbolrate(mod, 6900000);
mod_set_frequency(mod, dev->mod_base.frequency + i * 8000000);
2016-09-02 17:36:30 +02:00
}
2016-08-02 21:08:40 +02:00
if (streams <= 8)
2016-07-31 21:41:03 +02:00
mod_set_vga(dev, RF_VGA_GAIN_N8);
2016-08-02 21:08:40 +02:00
else if (streams <= 16)
2016-07-31 21:41:03 +02:00
mod_set_vga(dev, RF_VGA_GAIN_N16);
else
mod_set_vga(dev, RF_VGA_GAIN_N24);
2017-04-16 21:20:52 +02:00
2016-07-31 21:41:03 +02:00
mod_set_attenuator(dev, 0);
return 0;
}
/****************************************************************************/
static u32 vsb13500[64] = {
0x0000000E, 0x00010004, 0x00020003, 0x00030009,
0x0004FFFA, 0x00050002, 0x0006FFF8, 0x0007FFF0,
0x00080000, 0x0009FFEA, 0x000A0001, 0x000B0003,
0x000CFFF9, 0x000D0025, 0x000E0004, 0x000F001F,
0x00100023, 0x0011FFEE, 0x00120020, 0x0013FFD0,
0x0014FFD5, 0x0015FFED, 0x0016FF8B, 0x0017000B,
0x0018FFC8, 0x0019FFF1, 0x001A009E, 0x001BFFEF,
0x001C013B, 0x001D00CB, 0x001E0031, 0x001F05F6,
0x0040FFFF, 0x00410004, 0x0042FFF8, 0x0043FFFE,
0x0044FFFA, 0x0045FFF3, 0x00460003, 0x0047FFF4,
0x00480005, 0x0049000D, 0x004A0000, 0x004B0022,
0x004C0005, 0x004D000D, 0x004E0013, 0x004FFFDF,
0x00500007, 0x0051FFD4, 0x0052FFD2, 0x0053FFFD,
0x0054FFB7, 0x00550021, 0x00560009, 0x00570010,
0x00580097, 0x00590003, 0x005A009D, 0x005B004F,
0x005CFF89, 0x005D0097, 0x005EFD42, 0x005FFCBE
};
static u32 stage2[16] = {
0x0080FFFF, 0x00810000, 0x00820005, 0x00830000,
0x0084FFF0, 0x00850000, 0x00860029, 0x00870000,
0x0088FFA2, 0x0089FFFF, 0x008A00C9, 0x008B000C,
0x008CFE49, 0x008DFF9B, 0x008E04D4, 0x008F07FF
};
static void mod_set_sdr_table(struct ddb_mod *mod, u32 *tab, u32 len)
{
struct ddb *dev = mod->port->dev;
u32 i;
2017-04-16 21:20:52 +02:00
for (i = 0; i < len; i++)
ddbwritel(dev, tab[i], SDR_CHANNEL_SETFIR(mod->port->nr));
}
static int rfdac_init(struct ddb *dev)
{
int i;
u32 tmp;
2017-04-16 21:20:52 +02:00
ddbwritel(dev, RFDAC_CMD_POWERDOWN, RFDAC_CONTROL);
for (i = 0; i < 10; i++) {
2017-04-16 21:20:52 +02:00
msleep(20);
tmp = ddbreadl(dev, RFDAC_CONTROL);
if ((tmp & RFDAC_CMD_STATUS) == 0x00)
break;
}
if (tmp & 0x80)
return -1;
2017-04-16 21:20:52 +02:00
pr_info("sync %d:%08x\n", i, tmp);
ddbwritel(dev, RFDAC_CMD_RESET, RFDAC_CONTROL);
for (i = 0; i < 10; i++) {
2017-04-16 21:20:52 +02:00
msleep(20);
tmp = ddbreadl(dev, RFDAC_CONTROL);
if ((tmp & RFDAC_CMD_STATUS) == 0x00)
break;
}
if (tmp & 0x80)
return -1;
2017-04-16 21:20:52 +02:00
pr_info("sync %d:%08x\n", i, tmp);
ddbwritel(dev, RFDAC_CMD_SETUP, RFDAC_CONTROL);
for (i = 0; i < 10; i++) {
2017-04-16 21:20:52 +02:00
msleep(20);
tmp = ddbreadl(dev, RFDAC_CONTROL);
if ((tmp & RFDAC_CMD_STATUS) == 0x00)
break;
}
if (tmp & 0x80)
return -1;
2017-04-16 21:20:52 +02:00
pr_info("sync %d:%08x\n", i, tmp);
ddbwritel(dev, 0x01, JESD204B_BASE);
for (i = 0; i < 400; i++) {
2017-04-16 21:20:52 +02:00
msleep(20);
tmp = ddbreadl(dev, JESD204B_BASE);
if ((tmp & 0xc0000000) == 0xc0000000)
break;
}
2017-04-16 21:20:52 +02:00
pr_info("sync %d:%08x\n", i, tmp);
if ((tmp & 0xc0000000) != 0xc0000000)
return -1;
return 0;
}
2017-01-09 19:28:26 +01:00
static int mod_init_3(struct ddb *dev, u32 Frequency)
{
int streams = dev->link[0].info->port_num;
int i, ret = 0;
2017-01-09 19:28:26 +01:00
2017-01-22 02:05:44 +01:00
mod_set_vga(dev, 64);
ret = mod_setup_max2871(dev, max2871_sdr);
2017-01-22 02:05:44 +01:00
if (ret)
pr_err("DDBridge: PLL setup failed\n");
ret = rfdac_init(dev);
if (ret)
ret = rfdac_init(dev);
if (ret)
pr_err("DDBridge: RFDAC setup failed\n");
for (i = 0; i < streams; i++) {
struct ddb_mod *mod = &dev->mod[i];
2017-04-16 21:20:52 +02:00
mod->port = &dev->port[i];
mod_set_sdr_table(mod, vsb13500, 64);
mod_set_sdr_table(mod, stage2, 16);
}
2017-03-13 13:00:12 +01:00
ddbwritel(dev, 0x1800, 0x244);
ddbwritel(dev, 0x01, 0x240);
2017-04-16 14:55:00 +02:00
mod3_set_base_frequency(dev, 602000000);
for (i = 0; i < streams; i++) {
struct ddb_mod *mod = &dev->mod[i];
ddbwritel(dev, 0x00, SDR_CHANNEL_CONTROL(i));
ddbwritel(dev, 0x06, SDR_CHANNEL_CONFIG(i));
ddbwritel(dev, 0x70800000, SDR_CHANNEL_ARICW(i));
mod3_set_frequency(mod, Frequency + 7000000 * i);
ddbwritel(dev, 0x00011f80, SDR_CHANNEL_RGAIN(i));
ddbwritel(dev, 0x00001000, SDR_CHANNEL_FM1GAIN(i));
ddbwritel(dev, 0x00000800, SDR_CHANNEL_FM2GAIN(i));
}
2017-01-22 02:05:44 +01:00
return ret;
2017-01-09 19:28:26 +01:00
}
2015-08-05 17:22:42 +02:00
int ddbridge_mod_init(struct ddb *dev)
{
2016-07-31 21:41:03 +02:00
if (dev->link[0].info->version <= 1)
return mod_init_1(dev, 722000000);
if (dev->link[0].info->version == 2)
return mod_init_2(dev, 114000000);
2017-01-09 19:28:26 +01:00
if (dev->link[0].info->version == 3)
return mod_init_3(dev, 503250000);
2016-07-31 21:41:03 +02:00
return -1;
2015-08-05 17:22:42 +02:00
}