[PATCH 09/12] ddbridge: Support for the Digital Devices Resi DVB-C Modulator card

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

Signed-off-by: Maik Broemme <mbroemme@xxxxxxxxxxxxx>
---
 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 <mbroemme@xxxxxxxxxxxxx>
+ *
+ *  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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux