]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
V4L/DVB (7870): mxl5005s: Basic digital support.
authorSteven Toth <stoth@hauppauge.com>
Thu, 1 May 2008 10:04:09 +0000 (07:04 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Wed, 14 May 2008 05:56:39 +0000 (02:56 -0300)
ATSC and QAM should be working but basic testing is required.

Signed-off-by: Steven Toth <stoth@hauppauge.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/common/tuners/mxl5005s.c
drivers/media/common/tuners/mxl5005s.h

index f7ed9a72db4a1a0084915f91e35d7f4489f59adc..64aa864c5dbfff021380b46d3f1549e7965bf1c8 100644 (file)
@@ -1,69 +1,40 @@
 /*
-    MaxLinear MXL5005S VSB/QAM/DVBT tuner driver
-
-    Copyright (C) 2008 MaxLinear
-    Copyright (C) 2006 Steven Toth <stoth@hauppauge.com>
-      Functions:
-       mxl5005s_reset()
-       mxl5005s_writereg()
-       mxl5005s_writeregs()
-       mxl5005s_init()
-       mxl5005s_reconfigure()
-       mxl5005s_AssignTunerMode()
-       mxl5005s_set_params()
-       mxl5005s_get_frequency()
-       mxl5005s_get_bandwidth()
-       mxl5005s_release()
-       mxl5005s_attach()
-
-    Copyright (c) 2008 Realtek
-    Copyright (c) 2008 Jan Hoogenraad, Barnaby Shearer, Andy Hasper
-      Functions:
-       mxl5005s_SetRfFreqHz()
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/*
-    History of this driver (Steven Toth):
-      I was given a public release of a linux driver that included
-      support for the MaxLinear MXL5005S silicon tuner. Analysis of
-      the tuner driver showed clearly three things.
-
-      1. The tuner driver didn't support the LinuxTV tuner API
-        so the code Realtek added had to be removed.
-
-      2. A significant amount of the driver is reference driver code
-        from MaxLinear, I felt it was important to identify and
-        preserve this.
-
-      3. New code has to be added to interface correctly with the
-        LinuxTV API, as a regular kernel module.
-
-      Other than the reference driver enum's, I've clearly marked
-      sections of the code and retained the copyright of the
-      respective owners.
-*/
+ * For the Realtek RTL chip RTL2831U
+ * Realtek Release Date: 2008-03-14, ver 080314
+ * Realtek version RTL2831 Linux driver version 080314
+ * ver 080314
+ *
+ * for linux kernel version 2.6.21.4 - 2.6.22-14
+ * support MXL5005s and MT2060 tuners (support tuner auto-detecting)
+ * support two IR types -- RC5 and NEC
+ *
+ * Known boards with Realtek RTL chip RTL2821U
+ *    Freecom USB stick 14aa:0160 (version 4)
+ *    Conceptronic CTVDIGRCU
+ *
+ * Copyright (c) 2008 Realtek
+ * Copyright (c) 2008 Jan Hoogenraad, Barnaby Shearer, Andy Hasper
+ * This code is placed under the terms of the GNU General Public License
+ *
+ * Released by Realtek under GPLv2.
+ * Thanks to Realtek for a lot of support we received !
+ *
+ *  Revision: 080314 - original version
+ */
 
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
 #include "mxl5005s.h"
 
-static int debug = 2;
+static int debug;
 
 #define dprintk(level, arg...) do {    \
-       if (level <= debug)            \
+       if (debug >= level)            \
                printk(arg);    \
        } while (0)
 
@@ -79,6 +50,13 @@ static int debug = 2;
 #define MXLCTRL_NUM             189
 #define MASTER_CONTROL_ADDR     9
 
+/* Enumeration of AGC Mode */
+typedef enum
+{
+       MXL_DUAL_AGC = 0,
+       MXL_SINGLE_AGC
+} AGC_Mode;
+
 /* Enumeration of Master Control Register State */
 typedef enum
 {
@@ -88,6 +66,51 @@ typedef enum
        MC_SEQ_OFF
 } Master_Control_State;
 
+/* Enumeration of MXL5005 Tuner Mode */
+typedef enum
+{
+       MXL_ANALOG_MODE = 0,
+       MXL_DIGITAL_MODE
+} Tuner_Mode;
+
+/* Enumeration of MXL5005 Tuner IF Mode */
+typedef enum
+{
+       MXL_ZERO_IF = 0,
+       MXL_LOW_IF
+} Tuner_IF_Mode;
+
+/* Enumeration of MXL5005 Tuner Clock Out Mode */
+typedef enum
+{
+       MXL_CLOCK_OUT_DISABLE = 0,
+       MXL_CLOCK_OUT_ENABLE
+} Tuner_Clock_Out;
+
+/* Enumeration of MXL5005 Tuner Div Out Mode */
+typedef enum
+{
+       MXL_DIV_OUT_1 = 0,
+       MXL_DIV_OUT_4
+
+} Tuner_Div_Out;
+
+/* Enumeration of MXL5005 Tuner Pull-up Cap Select Mode */
+typedef enum
+{
+       MXL_CAP_SEL_DISABLE = 0,
+       MXL_CAP_SEL_ENABLE
+
+} Tuner_Cap_Select;
+
+/* Enumeration of MXL5005 Tuner RSSI Mode */
+typedef enum
+{
+       MXL_RSSI_DISABLE = 0,
+       MXL_RSSI_ENABLE
+
+} Tuner_RSSI;
+
 /* Enumeration of MXL5005 Tuner Modulation Type */
 typedef enum
 {
@@ -99,6 +122,22 @@ typedef enum
        MXL_ANALOG_OTA
 } Tuner_Modu_Type;
 
+/* Enumeration of MXL5005 Tuner Tracking Filter Type */
+typedef enum
+{
+       MXL_TF_DEFAULT = 0,
+       MXL_TF_OFF,
+       MXL_TF_C,
+       MXL_TF_C_H,
+       MXL_TF_D,
+       MXL_TF_D_L,
+       MXL_TF_E,
+       MXL_TF_F,
+       MXL_TF_E_2,
+       MXL_TF_E_NA,
+       MXL_TF_G
+} Tuner_TF_Type;
+
 /* MXL5005 Tuner Register Struct */
 typedef struct _TunerReg_struct
 {
@@ -229,6 +268,33 @@ enum
 };
 #define MXL5005S_BANDWIDTH_MODE_NUM            3
 
+/* Top modes */
+enum
+{
+       MXL5005S_TOP_5P5  =  55,
+       MXL5005S_TOP_7P2  =  72,
+       MXL5005S_TOP_9P2  =  92,
+       MXL5005S_TOP_11P0 = 110,
+       MXL5005S_TOP_12P9 = 129,
+       MXL5005S_TOP_14P7 = 147,
+       MXL5005S_TOP_16P8 = 168,
+       MXL5005S_TOP_19P4 = 194,
+       MXL5005S_TOP_21P2 = 212,
+       MXL5005S_TOP_23P2 = 232,
+       MXL5005S_TOP_25P2 = 252,
+       MXL5005S_TOP_27P1 = 271,
+       MXL5005S_TOP_29P2 = 292,
+       MXL5005S_TOP_31P7 = 317,
+       MXL5005S_TOP_34P9 = 349,
+};
+
+/* IF output load */
+enum
+{
+       MXL5005S_IF_OUTPUT_LOAD_200_OHM = 200,
+       MXL5005S_IF_OUTPUT_LOAD_300_OHM = 300,
+};
+
 /* MXL5005 Tuner Control Struct */
 typedef struct _TunerControl_struct {
        u16 Ctrl_Num;   /* Control Number */
@@ -283,15 +349,13 @@ struct mxl5005s_state
                TunerRegs[TUNER_REGS_NUM];      /* Tuner Register Array Pointer */
 
        /* Linux driver framework specific */
-       struct mxl5005s_config *config;
+       const struct mxl5005s_config *config;
+
        struct dvb_frontend *frontend;
        struct i2c_adapter *i2c;
-
-       /* Cache values */
-       u32 current_mode;
-
 };
 
+// funcs
 u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value);
 u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value);
 u16 MXL_GetMasterControl(u8 *MasterReg, int state);
@@ -308,26 +372,14 @@ u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq);
 void MXL_SynthIFLO_Calc(struct dvb_frontend *fe);
 void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe);
 u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal, int *count);
-int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, u8 *datatable, u8 len);
+int mxl5005s_SetRegsWithTable(struct dvb_frontend *fe, u8 *pAddrTable, u8 *pByteTable, int TableLen);
 u16 MXL_IFSynthInit(struct dvb_frontend *fe);
-int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, u32 bandwidth);
-int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, u32 bandwidth);
-
-/* ----------------------------------------------------------------
- * Begin: Custom code salvaged from the Realtek driver.
- * Copyright (c) 2008 Realtek
- * Copyright (c) 2008 Jan Hoogenraad, Barnaby Shearer, Andy Hasper
- * This code is placed under the terms of the GNU General Public License
- *
- * Released by Realtek under GPLv2.
- * Thanks to Realtek for a lot of support we received !
- *
- *  Revision: 080314 - original version
- */
+static int mxl5005s_init2(struct dvb_frontend *fe);
 
 int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
+       u8 AgcMasterByte = state->config->AgcMasterByte;
        unsigned char AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
        unsigned char ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
        int TableLen;
@@ -344,7 +396,7 @@ int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz)
        AddrTable[0] = MASTER_CONTROL_ADDR;
        ByteTable[0] |= state->config->AgcMasterByte;
 
-       mxl5005s_writeregs(fe, AddrTable, ByteTable, 1);
+       mxl5005s_SetRegsWithTable(fe, AddrTable, ByteTable, 1);
 
        // Tuner RF frequency setting stage 1
        MXL_TuneRF(fe, RfFreqHz);
@@ -358,13 +410,13 @@ int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz)
 
        MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START) ;
        AddrTable[TableLen] = MASTER_CONTROL_ADDR ;
-       ByteTable[TableLen] = MasterControlByte | state->config->AgcMasterByte;
+       ByteTable[TableLen] = MasterControlByte | AgcMasterByte;
        TableLen += 1;
 
-       mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
+       mxl5005s_SetRegsWithTable(fe, AddrTable, ByteTable, TableLen);
 
        // Wait 30 ms.
-       msleep(150);
+       msleep(30);
 
        // Tuner RF frequency setting stage 2
        MXL_ControlWrite(fe, SEQ_FSM_PULSE, 1) ;
@@ -373,21 +425,101 @@ int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz)
 
        MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START) ;
        AddrTable[TableLen] = MASTER_CONTROL_ADDR ;
-       ByteTable[TableLen] = MasterControlByte | state->config->AgcMasterByte ;
+       ByteTable[TableLen] = MasterControlByte | AgcMasterByte ;
        TableLen += 1;
 
-       mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
+       mxl5005s_SetRegsWithTable(fe, AddrTable, ByteTable, TableLen);
 
-       msleep(100);
+       return 0;
+}
 
+/* Write a single byte to a single reg */
+static int mxl5005s_writereg(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+       struct mxl5005s_state *state = fe->tuner_priv;
+       u8 buf[2] = { reg, val };
+       struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
+                              .buf = buf, .len = 2 };
+
+       if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+               printk(KERN_WARNING "mxl5005s I2C write failed\n");
+               return -EREMOTEIO;
+       }
        return 0;
 }
-/* End: Custom code taken from the Realtek driver */
 
-/* ----------------------------------------------------------------
- * Begin: Reference driver code found in the Realtek driver.
- * Copyright (c) 2008 MaxLinear
- */
+/* Write a word to a single reg */
+static int mxl5005s_writereg16(struct dvb_frontend *fe, u8 reg, u16 val)
+{
+       struct mxl5005s_state *state = fe->tuner_priv;
+       u8 buf[3] = { reg, val >> 8 , val & 0xff };
+       struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
+                              .buf = buf, .len = 3 };
+
+       if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+               printk(KERN_WARNING "mxl5005s I2C write16 failed\n");
+               return -EREMOTEIO;
+       }
+       return 0;
+}
+
+int mxl5005s_SetRegsWithTable(struct dvb_frontend *fe, u8 *pAddrTable, u8 *pByteTable, int TableLen)
+{
+       int     i, ret;
+       u8      end_two_bytes_buf[]={ 0 , 0 };
+
+       for( i = 0 ; i < TableLen - 1 ; i++)
+       {
+               ret = mxl5005s_writereg(fe, pAddrTable[i], pByteTable[i]);
+               if (!ret)
+                       return ret;
+       }
+
+       end_two_bytes_buf[0] = pByteTable[i];
+       end_two_bytes_buf[1] = MXL5005S_LATCH_BYTE;
+
+       ret = mxl5005s_writereg16(fe, pAddrTable[i], (end_two_bytes_buf[0] << 8) | end_two_bytes_buf[1]);
+
+       return ret;
+}
+
+int mxl5005s_SetRegMaskBits(struct dvb_frontend *fe,
+       unsigned char RegAddr,
+       unsigned char Msb,
+       unsigned char Lsb,
+       const unsigned char WritingValue
+       )
+{
+       int i;
+
+       unsigned char Mask;
+       unsigned char Shift;
+       unsigned char RegByte;
+
+       /* Generate mask and shift according to MSB and LSB. */
+       Mask = 0;
+       for(i = Lsb; i < (unsigned char)(Msb + 1); i++)
+               Mask |= 0x1 << i;
+
+       Shift = Lsb;
+
+       /* Get tuner register byte according to register adddress. */
+       MXL_RegRead(fe, RegAddr, &RegByte);
+
+       /* Reserve register byte unmask bit with mask and inlay writing value into it. */
+       RegByte &= ~Mask;
+       RegByte |= (WritingValue << Shift) & Mask;
+
+       /* Update tuner register byte table. */
+       MXL_RegWrite(fe, RegAddr, RegByte);
+
+       /* Write tuner register byte with writing byte. */
+       return mxl5005s_SetRegsWithTable(fe, &RegAddr, &RegByte, 1);
+}
+
+// The following context is source code provided by MaxLinear.
+// MaxLinear source code - MXL5005_Initialize.cpp
+// DONE
 u16 MXL5005_RegisterInit(struct dvb_frontend *fe)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -709,6 +841,7 @@ u16 MXL5005_RegisterInit(struct dvb_frontend *fe)
        return 0 ;
 }
 
+// DONE
 u16 MXL5005_ControlInit(struct dvb_frontend *fe)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -1652,6 +1785,7 @@ u16 MXL5005_ControlInit(struct dvb_frontend *fe)
 // MaxLinear source code - MXL5005_c.cpp
 // MXL5005.cpp : Defines the initialization routines for the DLL.
 // 2.6.12
+// DONE
 void InitTunerControls(struct dvb_frontend *fe)
 {
        MXL5005_RegisterInit(fe);
@@ -1694,6 +1828,7 @@ void InitTunerControls(struct dvb_frontend *fe)
 //               > 0 : Failed                                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 u16 MXL5005_TunerConfig(struct dvb_frontend *fe,
                u8      Mode,           /* 0: Analog Mode ; 1: Digital Mode */
                u8      IF_mode,        /* for Analog Mode, 0: zero IF; 1: low IF */
@@ -1763,6 +1898,7 @@ u16 MXL5005_TunerConfig(struct dvb_frontend *fe,
 //               > 0 : Failed                                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 void MXL_SynthIFLO_Calc(struct dvb_frontend *fe)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -1801,6 +1937,7 @@ void MXL_SynthIFLO_Calc(struct dvb_frontend *fe)
 //               > 0 : Failed                                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -1839,6 +1976,7 @@ void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe)
 //               > 0 : Failed                                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 u16 MXL_OverwriteICDefault(struct dvb_frontend *fe)
 {
        u16 status = 0;
@@ -1876,6 +2014,7 @@ u16 MXL_OverwriteICDefault(struct dvb_frontend *fe)
 //               > 0 : Failed                                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 u16 MXL_BlockInit(struct dvb_frontend *fe)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -1903,7 +2042,6 @@ u16 MXL_BlockInit(struct dvb_frontend *fe)
                                status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 2);
                                break;
                        case 6000000:
-                               printk("%s() doing 6MHz digital\n", __func__);
                                status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 3);
                                break;
                }
@@ -1934,6 +2072,7 @@ u16 MXL_BlockInit(struct dvb_frontend *fe)
        else /*  Single AGC Mode Dig  Ana */
                status += MXL_ControlWrite(fe, AGC_RF, state->Mode ? 15 : 12);
 
+
        if (state->TOP == 55) /* TOP == 5.5 */
                status += MXL_ControlWrite(fe, AGC_IF, 0x0);
 
@@ -2163,8 +2302,6 @@ u16 MXL_BlockInit(struct dvb_frontend *fe)
                        status += MXL_ControlWrite(fe, BB_IQSWAP, 0);
                else /* High IF */
                        status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
-               status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2);
-
        }
        if (state->Mod_Type == MXL_ANALOG_CABLE) {
                /* Analog Cable Mode */
@@ -2201,7 +2338,7 @@ u16 MXL_BlockInit(struct dvb_frontend *fe)
        }
 
        /* RSSI disable */
-       if(state->EN_RSSI == 0) {
+       if(state->EN_RSSI==0) {
                status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
                status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
                status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0);
@@ -2410,7 +2547,6 @@ u16 MXL_IFSynthInit(struct dvb_frontend *fe)
                        Fref = 324000000UL ;
                }
                if (state->IF_LO == 5380000UL) {
-                       printk("%s() doing 5.38\n", __func__);
                        status += MXL_ControlWrite(fe, IF_DIVVAL,   0x07) ;
                        status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C) ;
                        Fref = 322800000UL ;
@@ -3093,7 +3229,6 @@ u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
 
        if (state->TF_Type == MXL_TF_C_H) // Tracking Filter type C-H for Hauppauge only
        {
-               printk("%s() CH filter\n", __func__);
                status += MXL_ControlWrite(fe, DAC_DIN_A, 0) ;
 
                if (state->RF_IN >= 43000000 && state->RF_IN < 150000000)
@@ -3632,6 +3767,7 @@ u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
        return status ;
 }
 
+// DONE
 u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val)
 {
        u16 status = 0;
@@ -3698,6 +3834,7 @@ u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val)
 //                 >0 : Value exceed maximum allowed for control number      //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value)
 {
        u16 status = 0;
@@ -3738,6 +3875,7 @@ u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value)
 //                 2 : Control name not found                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, u32 value, u16 controlGroup)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -3844,6 +3982,7 @@ u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, u32 value, u
 //                 -1 : Invalid Register Address                             //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 u16 MXL_RegWrite(struct dvb_frontend *fe, u8 RegNum, u8 RegVal)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -3883,6 +4022,7 @@ u16 MXL_RegWrite(struct dvb_frontend *fe, u8 RegNum, u8 RegVal)
 //                 -1 : Invalid Register Address                             //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -3919,6 +4059,7 @@ u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal)
 //                 -1 : Invalid control name                                 //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -3990,6 +4131,7 @@ u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value)
 //                 -1 : Invalid control name                                 //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 u16 MXL_ControlRegRead(struct dvb_frontend *fe, u16 controlNum, u8 *RegNum, int * count)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -4095,6 +4237,7 @@ u16 MXL_ControlRegRead(struct dvb_frontend *fe, u16 controlNum, u8 *RegNum, int
 //                 NONE                                                      //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit, u8 bitVal)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -4142,6 +4285,7 @@ void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit, u8 bitVal)
 //                Computed value                                             //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
+// DONE
 u32 MXL_Ceiling(u32 value, u32 resolution)
 {
        return (value/resolution + (value % resolution > 0 ? 1 : 0));
@@ -4150,6 +4294,7 @@ u32 MXL_Ceiling(u32 value, u32 resolution)
 //
 // Retrieve the Initialzation Registers
 //
+// DONE
 u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *count)
 {
        u16 status = 0;
@@ -4172,6 +4317,7 @@ u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *c
        return status;
 }
 
+// DONE
 u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *count)
 {
        u16 status = 0;
@@ -4199,6 +4345,7 @@ u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *cou
        return status;
 }
 
+// DONE
 u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *count)
 {
        u16 status = 0;
@@ -4216,6 +4363,7 @@ u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, i
        return status;
 }
 
+// DONE
 u16 MXL_GetCHRegister_LowIF(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *count)
 {
        u16 status = 0;
@@ -4233,6 +4381,7 @@ u16 MXL_GetCHRegister_LowIF(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, in
        return status;
 }
 
+// DONE
 u16 MXL_GetMasterControl(u8 *MasterReg, int state)
 {
        if (state == 1) /* Load_Start */
@@ -4377,6 +4526,7 @@ u16 MXL_VCORange_Test(struct dvb_frontend *fe, int VCO_Range)
        return status;
 }
 
+// DONE
 u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
@@ -4387,224 +4537,141 @@ u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis)
 
        return status;
 }
+
 #endif
-/* End: Reference driver code found in the Realtek driver that
- * is copyright MaxLinear */
 
-/* ----------------------------------------------------------------
- * Begin: Everything after here is new code to adapt the
- * proprietary Realtek driver into a Linux API tuner.
- * Copyright (C) 2008 Steven Toth <stoth@hauppauge.com>
- */
-static int mxl5005s_reset(struct dvb_frontend *fe)
+/* Linux driver related functions */
+
+
+int mxl5005s_init(struct dvb_frontend *fe)
 {
-       struct mxl5005s_state *state = fe->tuner_priv;
-       int ret = 0;
+       int            MxlModMode;
+       int            MxlIfMode;
+       unsigned long  MxlBandwitdh;
+       unsigned long  MxlIfFreqHz;
+       unsigned long  MxlCrystalFreqHz;
+       int            MxlAgcMode;
+       unsigned short MxlTop;
+       unsigned short MxlIfOutputLoad;
+       int            MxlClockOut;
+       int            MxlDivOut;
+       int            MxlCapSel;
+       int            MxlRssiOnOff;
+       unsigned char  MxlStandard;
+       unsigned char  MxlTfType;
 
-       u8 buf[2] = { 0xff, 0x00 };
-       struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
-                              .buf = buf, .len = 2 };
+       /* Set MxL5005S parameters. */
+       MxlModMode       = MXL_DIGITAL_MODE;
+       MxlIfMode        = MXL_ZERO_IF;
+// steve
+       //MxlBandwitdh     = MXL5005S_BANDWIDTH_8MHZ;
+       //MxlIfFreqHz      = IF_FREQ_4570000HZ;
+       MxlBandwitdh     = MXL5005S_BANDWIDTH_6MHZ; // config
+       MxlIfFreqHz      = IF_FREQ_5380000HZ; // config
+       MxlCrystalFreqHz = CRYSTAL_FREQ_16000000HZ; // config
+       MxlAgcMode       = MXL_SINGLE_AGC;
+       MxlTop           = MXL5005S_TOP_25P2;
+       MxlIfOutputLoad  = MXL5005S_IF_OUTPUT_LOAD_200_OHM;
+       MxlClockOut      = MXL_CLOCK_OUT_DISABLE;
+       MxlDivOut        = MXL_DIV_OUT_4;
+       MxlCapSel        = MXL_CAP_SEL_ENABLE;
+       MxlRssiOnOff     = MXL_RSSI_ENABLE; // config
+       MxlTfType        = MXL_TF_C_H; // config
+
+       MxlStandard = MXL_ATSC; // config
+
+       // TODO: this is bad, it trashes other configs
+       // Set MxL5005S extra module.
+       //pExtra->AgcMasterByte = (MxlAgcMode == MXL_DUAL_AGC) ? 0x4 : 0x0;
 
-       dprintk(2, "%s()\n", __func__);
+       MXL5005_TunerConfig(
+               fe,
+               (unsigned char)MxlModMode,
+               (unsigned char)MxlIfMode,
+               MxlBandwitdh,
+               MxlIfFreqHz,
+               MxlCrystalFreqHz,
+               (unsigned char)MxlAgcMode,
+               MxlTop,
+               MxlIfOutputLoad,
+               (unsigned char)MxlClockOut,
+               (unsigned char)MxlDivOut,
+               (unsigned char)MxlCapSel,
+               (unsigned char)MxlRssiOnOff,
+               MxlStandard, MxlTfType);
+
+       return mxl5005s_init2(fe);
+}
 
-       if (fe->ops.i2c_gate_ctrl)
-               fe->ops.i2c_gate_ctrl(fe, 1);
+static int mxl5005s_set_params(struct dvb_frontend *fe,
+                              struct dvb_frontend_parameters *params)
+{
+       u32 freq;
+       u32 bw;
 
-       if (i2c_transfer(state->i2c, &msg, 1) != 1) {
-               printk(KERN_WARNING "mxl5005s I2C reset failed\n");
-               ret = -EREMOTEIO;
-       }
+       if (fe->ops.info.type == FE_OFDM)
+               bw = params->u.ofdm.bandwidth;
+       else
+               bw = MXL5005S_BANDWIDTH_6MHZ;
 
-       if (fe->ops.i2c_gate_ctrl)
-               fe->ops.i2c_gate_ctrl(fe, 0);
+       freq = params->frequency; /* Hz */
+       dprintk(1, "%s() freq=%d bw=%d\n", __func__, freq, bw);
 
-       return ret;
+       return mxl5005s_SetRfFreqHz(fe, freq);
 }
 
-/* Write a single byte to a single reg, latch the value if required by
- * following the transaction with the latch byte.
- */
-static int mxl5005s_writereg(struct dvb_frontend *fe, u8 reg, u8 val, int latch)
+static int mxl5005s_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
-       u8 buf[3] = { reg, val, MXL5005S_LATCH_BYTE };
-       struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
-                              .buf = buf, .len = 3 };
-
-       if (latch == 0)
-               msg.len = 2;
+       dprintk(1, "%s()\n", __func__);
 
-       dprintk(2, "%s(reg = 0x%x val = 0x%x addr = 0x%x)\n", __func__, reg, val, msg.addr);
+       *frequency = state->RF_IN;
 
-       if (i2c_transfer(state->i2c, &msg, 1) != 1) {
-               printk(KERN_WARNING "mxl5005s I2C write failed\n");
-               return -EREMOTEIO;
-       }
        return 0;
 }
 
-int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, u8 *datatable, u8 len)
+static int mxl5005s_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
 {
-       int ret = 0, i;
-
-       if (fe->ops.i2c_gate_ctrl)
-               fe->ops.i2c_gate_ctrl(fe, 1);
-
-       for (i = 0 ; i < len-1; i++) {
-               ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 0);
-               if (ret < 0)
-                       break;
-       }
-
-       ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 1);
+       struct mxl5005s_state *state = fe->tuner_priv;
+       dprintk(1, "%s()\n", __func__);
 
-       if (fe->ops.i2c_gate_ctrl)
-               fe->ops.i2c_gate_ctrl(fe, 0);
+       *bandwidth = state->Chan_Bandwidth;
 
-       return ret;
+       return 0;
 }
 
-
-int mxl5005s_init(struct dvb_frontend *fe)
+static int mxl5005s_get_status(struct dvb_frontend *fe, u32 *status)
 {
        dprintk(1, "%s()\n", __func__);
-       return mxl5005s_reconfigure(fe, MXL_QAM, MXL5005S_BANDWIDTH_6MHZ);
+
+       *status = 0;
+       // *status = TUNER_STATUS_LOCKED;
+
+       return 0;
 }
 
-int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, u32 bandwidth)
+static int mxl5005s_init2(struct dvb_frontend *fe)
 {
        struct mxl5005s_state *state = fe->tuner_priv;
-
        u8 AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
        u8 ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
        int TableLen;
 
-       dprintk(1, "%s(type=%d, bw=%d)\n", __func__, mod_type, bandwidth);
+       dprintk(1, "%s()\n", __func__);
 
-       mxl5005s_reset(fe);
+       /* Initialize MxL5005S tuner according to MxL5005S tuner example code. */
 
        /* Tuner initialization stage 0 */
        MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET);
        AddrTable[0] = MASTER_CONTROL_ADDR;
        ByteTable[0] |= state->config->AgcMasterByte;
 
-       mxl5005s_writeregs(fe, AddrTable, ByteTable, 1);
-
-       mxl5005s_AssignTunerMode(fe, mod_type, bandwidth);
+       mxl5005s_SetRegsWithTable(fe, AddrTable, ByteTable, 1);
 
        /* Tuner initialization stage 1 */
        MXL_GetInitRegister(fe, AddrTable, ByteTable, &TableLen);
 
-       mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
-
-       return 0;
-}
-
-int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, u32 bandwidth)
-{
-       struct mxl5005s_state *state = fe->tuner_priv;
-       struct mxl5005s_config *c = state->config;
-
-       InitTunerControls(fe);
-
-       /* Set MxL5005S parameters. */
-       MXL5005_TunerConfig(
-               fe,
-               c->mod_mode,
-               c->if_mode,
-               bandwidth,
-               c->if_freq,
-               c->xtal_freq,
-               c->agc_mode,
-               c->top,
-               c->output_load,
-               c->clock_out,
-               c->div_out,
-               c->cap_select,
-               c->rssi_enable,
-               mod_type,
-               c->tracking_filter);
-
-       return 0;
-}
-
-static int mxl5005s_set_params(struct dvb_frontend *fe,
-                              struct dvb_frontend_parameters *params)
-{
-       struct mxl5005s_state *state = fe->tuner_priv;
-       u32 req_mode, req_bw = 0;
-       int ret;
-
-       dprintk(1, "%s()\n", __func__);
-
-       if (fe->ops.info.type == FE_ATSC) {
-               switch (params->u.vsb.modulation) {
-               case VSB_8:
-                       req_mode = MXL_ATSC; break;
-               default:
-               case QAM_64:
-               case QAM_256:
-               case QAM_AUTO:
-                       req_mode = MXL_QAM; break;
-               }
-       }
-       else req_mode = MXL_DVBT;
-
-       /* Change tuner for new modulation type if reqd */
-       if (req_mode != state->current_mode) {
-               switch (req_mode) {
-               case VSB_8:
-               case QAM_64:
-               case QAM_256:
-               case QAM_AUTO:
-                       req_bw  = MXL5005S_BANDWIDTH_6MHZ;
-                       break;
-               default:
-                       /* Assume DVB-T */
-                       switch (params->u.ofdm.bandwidth) {
-                       case BANDWIDTH_6_MHZ:
-                               req_bw  = MXL5005S_BANDWIDTH_6MHZ;
-                               break;
-                       case BANDWIDTH_7_MHZ:
-                               req_bw  = MXL5005S_BANDWIDTH_7MHZ;
-                               break;
-                       case BANDWIDTH_AUTO:
-                       case BANDWIDTH_8_MHZ:
-                               req_bw  = MXL5005S_BANDWIDTH_8MHZ;
-                               break;
-                       }
-               }
-
-               state->current_mode = req_mode;
-               ret = mxl5005s_reconfigure(fe, req_mode, req_bw);
-
-       } else
-               ret = 0;
-
-       if (ret == 0) {
-               dprintk(1, "%s() freq=%d\n", __func__, params->frequency);
-               ret = mxl5005s_SetRfFreqHz(fe, params->frequency);
-       }
-
-       return ret;
-}
-
-static int mxl5005s_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
-       struct mxl5005s_state *state = fe->tuner_priv;
-       dprintk(1, "%s()\n", __func__);
-
-       *frequency = state->RF_IN;
-
-       return 0;
-}
-
-static int mxl5005s_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
-{
-       struct mxl5005s_state *state = fe->tuner_priv;
-       dprintk(1, "%s()\n", __func__);
-
-       *bandwidth = state->Chan_Bandwidth;
+       mxl5005s_SetRegsWithTable(fe, AddrTable, ByteTable, TableLen);
 
        return 0;
 }
@@ -4631,6 +4698,7 @@ static const struct dvb_tuner_ops mxl5005s_tuner_ops = {
        .set_params    = mxl5005s_set_params,
        .get_frequency = mxl5005s_get_frequency,
        .get_bandwidth = mxl5005s_get_bandwidth,
+       .get_status    = mxl5005s_get_status
 };
 
 struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
@@ -4647,7 +4715,6 @@ struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
        state->frontend = fe;
        state->config = config;
        state->i2c = i2c;
-       state->current_mode = MXL_QAM;
 
        printk(KERN_INFO "MXL5005S: Attached at address 0x%02x\n", config->i2c_address);
 
@@ -4659,5 +4726,8 @@ struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
 EXPORT_SYMBOL(mxl5005s_attach);
 
 MODULE_DESCRIPTION("MaxLinear MXL5005S silicon tuner driver");
+MODULE_AUTHOR("Jan Hoogenraad");
+MODULE_AUTHOR("Barnaby Shearer");
+MODULE_AUTHOR("Andy Hasper");
 MODULE_AUTHOR("Steven Toth");
 MODULE_LICENSE("GPL");
index 7658401f3cdd07f0ac9a56d480f2ebef0bb3fe50..7d0727d44536ab9155c64d8e101d80a7effd8b2d 100644 (file)
 /*
-    MaxLinear MXL5005S VSB/QAM/DVBT tuner driver
+ * For the Realtek RTL chip RTL2831U
+ * Realtek Release Date: 2008-03-14, ver 080314
+ * Realtek version RTL2831 Linux driver version 080314
+ * ver 080314
+ *
+ * for linux kernel version 2.6.21.4 - 2.6.22-14
+ * support MXL5005s and MT2060 tuners (support tuner auto-detecting)
+ * support two IR types -- RC5 and NEC
+ *
+ * Known boards with Realtek RTL chip RTL2821U
+ *    Freecom USB stick 14aa:0160 (version 4)
+ *    Conceptronic CTVDIGRCU
+ *
+ * Copyright (c) 2008 Realtek
+ * Copyright (c) 2008 Jan Hoogenraad, Barnaby Shearer, Andy Hasper
+ * This code is placed under the terms of the GNU General Public License
+ *
+ * Released by Realtek under GPLv2.
+ * Thanks to Realtek for a lot of support we received !
+ *
+ *  Revision: 080314 - original version
+ */
 
-    Copyright (C) 2008 MaxLinear
-    Copyright (C) 2008 Steven Toth <stoth@hauppauge.com>
 
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    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.
+#ifndef __MXL5005S_H
+#define __MXL5005S_H
 
-    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#include <linux/dvb/frontend.h>
 
-*/
+/* IF frequency */
+enum IF_FREQ_HZ
+{
+       IF_FREQ_4570000HZ  =  4570000,                  ///<   IF frequency =   4.57 MHz
+       IF_FREQ_4571429HZ  =  4571429,                  ///<   IF frequency =  4.571 MHz
+       IF_FREQ_5380000HZ  =  5380000,                  ///<   IF frequency =   5.38 MHz
+       IF_FREQ_36000000HZ = 36000000,                  ///<   IF frequency = 36.000 MHz
+       IF_FREQ_36125000HZ = 36125000,                  ///<   IF frequency = 36.125 MHz
+       IF_FREQ_36166667HZ = 36166667,                  ///<   IF frequency = 36.167 MHz
+       IF_FREQ_44000000HZ = 44000000,                  ///<   IF frequency = 44.000 MHz
+};
 
-#ifndef __MXL5005S_H
-#define __MXL5005S_H
+/* Crystal frequency */
+enum CRYSTAL_FREQ_HZ
+{
+       CRYSTAL_FREQ_4000000HZ  =  4000000,                     ///<   Crystal frequency =  4.0 MHz
+       CRYSTAL_FREQ_16000000HZ = 16000000,                     ///<   Crystal frequency = 16.0 MHz
+       CRYSTAL_FREQ_25000000HZ = 25000000,                     ///<   Crystal frequency = 25.0 MHz
+       CRYSTAL_FREQ_28800000HZ = 28800000,                     ///<   Crystal frequency = 28.8 MHz
+};
 
 struct mxl5005s_config
 {
-       /* 7 bit i2c address */
        u8 i2c_address;
 
-#define IF_FREQ_4570000HZ    4570000
-#define IF_FREQ_4571429HZ    4571429
-#define IF_FREQ_5380000HZ    5380000
-#define IF_FREQ_36000000HZ  36000000
-#define IF_FREQ_36125000HZ  36125000
-#define IF_FREQ_36166667HZ  36166667
-#define IF_FREQ_44000000HZ  44000000
-       u32 if_freq;
-
-#define CRYSTAL_FREQ_4000000HZ    4000000
-#define CRYSTAL_FREQ_16000000HZ  16000000
-#define CRYSTAL_FREQ_25000000HZ  25000000
-#define CRYSTAL_FREQ_28800000HZ  28800000
-       u32 xtal_freq;
-
-#define MXL_DUAL_AGC   0
-#define MXL_SINGLE_AGC 1
-       u8 agc_mode;
-
-#define MXL_TF_DEFAULT 0
-#define MXL_TF_OFF     1
-#define MXL_TF_C       2
-#define MXL_TF_C_H     3
-#define MXL_TF_D       4
-#define MXL_TF_D_L     5
-#define MXL_TF_E       6
-#define MXL_TF_F       7
-#define MXL_TF_E_2     8
-#define MXL_TF_E_NA    9
-#define MXL_TF_G       10
-       u8 tracking_filter;
-
-#define MXL_RSSI_DISABLE       0
-#define MXL_RSSI_ENABLE                1
-       u8 rssi_enable;
-
-#define MXL_CAP_SEL_DISABLE    0
-#define MXL_CAP_SEL_ENABLE     1
-       u8 cap_select;
-
-#define MXL_DIV_OUT_1  0
-#define MXL_DIV_OUT_4  1
-       u8 div_out;
-
-#define MXL_CLOCK_OUT_DISABLE  0
-#define MXL_CLOCK_OUT_ENABLE   1
-       u8 clock_out;
-
-#define MXL5005S_IF_OUTPUT_LOAD_200_OHM 200
-#define MXL5005S_IF_OUTPUT_LOAD_300_OHM 300
-       u32 output_load;
-
-#define MXL5005S_TOP_5P5   55
-#define MXL5005S_TOP_7P2   72
-#define MXL5005S_TOP_9P2   92
-#define MXL5005S_TOP_11P0 110
-#define MXL5005S_TOP_12P9 129
-#define MXL5005S_TOP_14P7 147
-#define MXL5005S_TOP_16P8 168
-#define MXL5005S_TOP_19P4 194
-#define MXL5005S_TOP_21P2 212
-#define MXL5005S_TOP_23P2 232
-#define MXL5005S_TOP_25P2 252
-#define MXL5005S_TOP_27P1 271
-#define MXL5005S_TOP_29P2 292
-#define MXL5005S_TOP_31P7 317
-#define MXL5005S_TOP_34P9 349
-       u32 top;
-
-#define MXL_ANALOG_MODE  0
-#define MXL_DIGITAL_MODE 1
-       u8 mod_mode;
-
-#define MXL_ZERO_IF 0
-#define MXL_LOW_IF  1
-       u8 if_mode;
-
        /* Stuff I don't know what to do with */
        u8 AgcMasterByte;
 };
 
-#if defined(CONFIG_DVB_TUNER_MXL5005S) || \
-       (defined(CONFIG_DVB_TUNER_MXL5005S_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_MXL5005S) || (defined(CONFIG_MEDIA_TUNER_MXL5005S_MODULE) && defined(MODULE))
 extern struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
                                            struct i2c_adapter *i2c,
-                                           struct mxl5005s_config *config)
+                                           struct mxl5005s_config *config);
 #else
 static inline struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
                                            struct i2c_adapter *i2c,