Added support for the Digital Devices Resi DVB-C Modulator card.

Signed-off-by: Maik Broemme <mbroe...@parallels.com>
---
 drivers/media/pci/ddbridge/ddbridge-mod.c | 1033 +++++++++++++++++++++++++++++
 1 file changed, 1033 insertions(+)
 create mode 100644 drivers/media/pci/ddbridge/ddbridge-mod.c

diff --git a/drivers/media/pci/ddbridge/ddbridge-mod.c 
b/drivers/media/pci/ddbridge/ddbridge-mod.c
new file mode 100644
index 0000000..8f7c256
--- /dev/null
+++ b/drivers/media/pci/ddbridge/ddbridge-mod.c
@@ -0,0 +1,1033 @@
+/*
+ *  ddbridge-mod.c: Digital Devices DVB modulator driver
+ *
+ *  Copyright (C) 2010-2013 Digital Devices GmbH
+ *  Copyright (C) 2013 Maik Broemme <mbroe...@parallels.com>
+ *
+ *  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
+ */
+
+#include "ddbridge.h"
+#include "ddbridge-regs.h"
+
+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);
+}
+
+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;
+       struct mod_state *mod= &dev->mod[output->nr];
+
+       mod->State = CM_IDLE;
+       mod->Control = 0;
+       ddbwritel(dev, 0, CHANNEL_CONTROL(output->nr));
+
+       mod_busy(dev, output->nr);
+       printk("mod_output_stop %d.%d\n", dev->nr, output->nr);
+}
+
+static void mod_set_incs(struct ddb_output *output)
+{
+       s64 pcr;
+       struct ddb *dev = output->port->dev;
+       struct mod_state *mod= &dev->mod[output->nr];
+
+       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 u32 qamtab[6] = { 0x000, 0x600, 0x601, 0x602, 0x903, 0x604 };
+
+void ddbridge_mod_output_start(struct ddb_output *output)
+{
+       struct ddb *dev = output->port->dev;
+       struct mod_state *mod= &dev->mod[output->nr];
+
+       // PCRIncrement = RoundPCR(PCRIncrement);
+       // PCRDecrement = RoundPCR(PCRDecrement);
+
+       mod->LastInPacketCount = 0;
+       mod->LastOutPacketCount = 0;
+       mod->InOverflowPacketCount = 0;
+       mod->OutOverflowPacketCount = 0;
+       mod->LastInPackets = 0;
+       mod->LastOutPackets = 0;
+       mod->LastPCRAdjust = 0;
+       mod->PCRRunningCorr = 0;
+       mod->MinInputPackets = 524288/2;   // we interrupt every 0x80000=524288 
packets
+       mod->PCRIncrement = 0;   // PCRIncrement;
+       mod->PCRDecrement = 0;   // PCRDecrement;
+
+       mod->State = CM_STARTUP;
+       mod->StateCounter = CM_STARTUP_DELAY;
+
+       ddbwritel(dev, 0, CHANNEL_CONTROL(output->nr));
+       udelay(10);
+       ddbwritel(dev, CHANNEL_CONTROL_RESET, CHANNEL_CONTROL(output->nr));
+       udelay(10);
+       ddbwritel(dev, 0, CHANNEL_CONTROL(output->nr));
+
+       // QAM: 600 601 602 903 604 = 16 32 64 128 256
+       // ddbwritel(dev, 0x604, CHANNEL_SETTINGS(output->nr));
+       ddbwritel(dev, qamtab[mod->modulation], CHANNEL_SETTINGS(output->nr));
+
+       ddbwritel(dev,  mod->rate_inc, CHANNEL_RATE_INCR(output->nr));
+       mod_busy(dev, output->nr);
+
+       mod_set_incs(output);
+
+       mod->Control = (CHANNEL_CONTROL_ENABLE_IQ | 
+                       CHANNEL_CONTROL_ENABLE_DVB |
+                       CHANNEL_CONTROL_ENABLE_SOURCE);
+
+       ddbwritel(dev, mod->Control, CHANNEL_CONTROL(output->nr));
+       printk("mod_output_start %d.%d\n", dev->nr, output->nr);
+}
+
+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(10);
+       }
+       
+       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(10);
+       }
+       
+       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(10);
+       }
+       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);
+       msleep(5);
+       *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);
+       msleep(5);
+}
+
+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]);
+               
+               printk(" Data = %02x %02x %02x %02x %02x %02x\n",
+                      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;
+               printk("fxtal %016llx  rfreq %016llx\n", m_fXtal, RFreq);
+               
+               m_fXtal += RFreq >> 1;
+               m_fXtal = div64_u64(m_fXtal, RFreq);
+               
+               printk(" fOut = %d fXtal = %d fDCO = %d HDIV = %2d, N = %3d \n",
+                      (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;
+       printk(" fOut = %d MinDiv = %4d MaxDiv = %4d StartDiv = %d\n", fOut, 
(u32) MinDiv, (u32) MaxDiv, (u32) 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;
+                       }
+                       printk(" %3d: %llu %u %u %u\n", retry, Div, (u32) 
HSDiv*N, (u32) 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 ) {
+                       printk(" FAIL \n");
+                       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;
+       printk("fdco %16llx\n", fDCO);
+       RFreq = fDCO<<28;
+       printk("%16llx %16llx\n", fDCO, RFreq);
+
+       fxtal = m_fXtal; 
+       do_div(RFreq, fxtal);
+       printk("%16llx %d\n", RFreq, fxtal);
+       RF = RFreq;
+       
+       // printk("fOut = %d fXtal = %d fDCO = %d HDIV = %d, N = %d, RFreq = 
%d\n",fOut,m_fXtal,fDCO,HSDiv,N,RFreq);
+       // printk("%16llx\n", RF);
+
+       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 );
+       
+       printk(" Data = %02x %02x %02x %02x %02x %02x\n",
+              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;
+}
+
+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", 
Sample,SeekTable[Sample],SetTable[Sample],HldTable[Sample]);
+               
+               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 ) {
+               printk(" No valid window found\n");
+               return -EINVAL;
+       }
+       
+       printk(" Window = %d - %d\n", Sample1, Sample2);
+
+       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;
+                       }
+               }
+       }
+       
+       printk("Select Sample %d\n", SelectSample);
+       
+       if (SelectSample == 0xFF) {
+               printk("No valid sample found\n");
+               return -EINVAL;
+       }
+       
+       if (HldTable[SelectSample] + SetTable[SelectSample] < 8 ) {
+               printk("Too high jitter\n");
+               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 ) {
+               printk("Insufficient timing margin\n");
+               return -EINVAL;
+       }
+       printk("Done\n");
+       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(10);
+               if (mod_set_si598(dev, Frequency)) {
+                       printk("mod_set_si598 failed\n");
+                       return -1;
+               }
+               msleep(50);
+               ddbwritel(dev, 0x000, DAC_CONTROL);
+               msleep(10);
+               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);
+       }
+       printk("mod_set_dac_clock OK\n");
+       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(10);
+               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 = 0xC90FDAA22168C234ULL;
+       u32 *iqtab;
+       u32 iqtabadr;
+       u32 volatile 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;
+                       // 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;
+
+       printk("set base to %u\n", freq);
+       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)) {
+               printk("Frequency out of range %d\n", freq10);
+               return -EINVAL;
+        }
+        if (down % 8) {
+               printk(" Invalid Frequency %d\n", down);
+               return -EINVAL;
+        }
+       return mod_set_down(dev, down, 8, Ext);
+}
+
+static int mod_init(struct ddb *dev, u32 Frequency)
+{
+       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, buffer, DDMOD_FLASH_START, 4096);
+       
+       if (flash->Magic != DDMOD_FLASH_MAGIC && flash->Magic != 1) {
+               stat = -EINVAL;
+               goto fail;
+       }
+       printk("srate = %d\n", flash->DataSet[0].Symbolrate * 1000);
+
+       mod_output_enable(dev, 0);
+       stat = mod_set_dac_clock(dev, flash->DataSet[0].DACFrequency * 1000);
+       if (stat < 0) {
+               printk("setting DAC clock failed\n");
+               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;
+       printk("CH10 = %d, Down = %d\n", FrequencyCH10, DownFrequency);
+
+
+        if ((FrequencyCH10 + 9 * 8) > (flash->DataSet[0].FlatEnd - 4)) {
+               printk("Frequency out of range %d\n", FrequencyCH10);
+               stat = -EINVAL;
+               goto fail;
+        }
+
+        if (DownFrequency % 8 != 0 ) {
+               printk(" Invalid Frequency %d\n", DownFrequency);
+               stat = -EINVAL;
+               goto fail;
+        }
+
+       mod_set_down(dev, DownFrequency, 8, Ext);
+
+       for (i = 0; i < 10; i++) {
+               ddbwritel(dev, 0, CHANNEL_CONTROL(i));
+               
+               iqfreq = flash->DataSet[0].FrequencyFactor * (FrequencyCH10 + 
(9 - i) * 8);
+               iqsteps = flash->DataSet[0].IQTableLength;
+               mod_set_iq(dev, iqsteps, i, iqfreq);
+
+               dev->mod[i].modulation = QAM_256;
+       }
+       
+       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);
+       printk("prescaler %04x\n", flash->DataSet[0].PreScale);
+       mod_set_channelsumshift(dev, 2);
+       mod_output_enable(dev, 1);
+
+       // mod_set_attenuator(dev, 10);
+fail:
+       kfree(buffer);
+       return stat;
+}
+
+
+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;
+       struct mod_state *mod= &dev->mod[output->nr];
+       
+       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->do_handle)
+               return;
+       printk("rate_handler\n");
+
+       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) {
+                               mul = (0x1000000 * (u64) (OutPacketDiff - 
InPacketDiff - InPacketDiff/1000));
+                               if (OutPacketDiff)
+                                       mod->rate_inc = div_u64(mul, 
OutPacketDiff);
+                               else
+                                       mod->rate_inc = 0;
+                               printk("RI %08x\n", mod->rate_inc);
+                               ddbwritel(dev,  mod->rate_inc, 
CHANNEL_RATE_INCR(output->nr));
+                               mod_busy(dev, output->nr);
+// #define PACKET_CLOCKS  (27000000ULL*1504)
+// #define FACTOR  (1024<<12)
+// double Increment =  FACTOR*PACKET_CLOCKS/double(m_OutputBitrate);
+// double Decrement =  FACTOR*PACKET_CLOCKS/double(m_InputBitrate);
+                               mod->PCRIncrement = 3348148758ULL;
+                               if (InPacketDiff)
+                                       mod->PCRDecrement = 
div_u64(3348148758ULL * (u64) OutPacketDiff, 
+                                                                   
InPacketDiff); 
+                               else
+                                       mod->PCRDecrement = 0;
+                               mod_set_incs(output);
+                       }
+                       mod->StateCounter--; 
+                       break;
+               }
+               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) {
+                       printk("PCR Adjust reset  IN: %u  Min: %u\n",
+                              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);
+               printk("outl %016llx\n", pcr);
+               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;
+
+       printk("chan %d out %016llx in %016llx indiff %08x\n", chan, 
OutPackets, InPackets, InPacketDiff);
+       printk("cnt  %d pcra %016llx pcraext %08x pcraextfrac %08x pcrcorr %08x 
pcri %016llx\n", 
+              mod->StateCounter, PCRAdjust, PCRAdjustExt, PCRAdjustExtFrac, 
PCRCorr, mod->PCRIncrement);
+       // 
Channel,OutPackets,InPackets,InPacketDiff,PCRAdjust,PCRAdjustExt,PCRAdjustExtFrac,PCRCorr,
 mod->PCRRunningCorr,mod->StateCounter ));
+       spin_unlock(&dma->lock);
+}
+             
+int ddbridge_mod_do_ioctl(struct file *file, unsigned int cmd, void *parg)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct ddb_output *output = dvbdev->priv;
+       struct ddb *dev = output->port->dev;
+       
+       // unsigned long arg = (unsigned long) parg;
+       int ret = 0;
+
+       switch (cmd) {
+       case DVB_MOD_SET:
+       {
+               struct dvb_mod_params *mp = parg;
+               
+               if (mp->base_frequency != dev->mod_base.frequency)
+                       if (set_base_frequency(dev, mp->base_frequency))
+                               return -EINVAL;
+               mod_set_attenuator(dev, mp->attenuator);
+               break;
+       }
+       case DVB_MOD_CHANNEL_SET:
+       {
+               struct dvb_mod_channel_params *cp = parg;
+
+               if (cp->modulation > QAM_256)
+                       return -EINVAL;
+               dev->mod[output->nr].modulation = cp->modulation;
+               dev->mod[output->nr].rate_inc = cp->rate_increment;
+               ddbwritel(dev, dev->mod[output->nr].rate_inc, 
CHANNEL_RATE_INCR(output->nr));
+               break;
+       }
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+int ddbridge_mod_init(struct ddb *dev)
+{
+       return mod_init(dev, 722000000);
+}
-- 
1.8.4.2
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to