},
#endif
+#if defined (CONFIG_SND_SOC_CX2070X)
+ {
+ .type = "cx2070x",
+ .addr = 0x14,
+ .flags = 0,
+ },
+#endif
#if defined (CONFIG_SND_SOC_RT5640)
{
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
select SND_SOC_WM9713 if SND_SOC_AC97_BUS
+ select SND_SOC_CX2070X if SND_SOC_I2C_AND_SPI
help
Normally ASoC codec drivers are only built if a machine driver which
uses them is also built since they are only usable with a machine
config SND_SOC_RT5512
tristate
+config SND_SOC_CX2070X
+ tristate
snd-soc-rk3026-objs := rk3026_codec.o
snd-soc-rt5639-objs := rt5639.o rt5639_ioctl.o rt56xx_ioctl.o
snd-soc-rt5512-objs := rt5512.o
+snd-soc-cx2070x-objs := cx2070x.o cxdebug.o cxpump.o
# Amp
snd-soc-lm4857-objs := lm4857.o
obj-$(CONFIG_SND_SOC_RK2928) += snd-soc-rk2928.o
obj-$(CONFIG_SND_SOC_RK3026) += snd-soc-rk3026.o
obj-$(CONFIG_SND_SOC_RT5512) += snd-soc-rt5512.o
+obj-$(CONFIG_SND_SOC_CX2070X) += snd-soc-cx2070x.o
# Amp
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
--- /dev/null
+/*
+ * Cx2070x ASoc codec driver.
+ *
+ * Copyright: (C) 2010/2011 Conexant Systems
+ *
+ * Based on sound/soc/codecs/tlv320aic2x.c by Vladimir Barinov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The software 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 software. If not, see <http//www.gnu.org/licenses/>
+ *
+ * All copies of the software must include all unaltered copyright notices,
+ * disclaimers of warranty, all notices that refer to the General Public License
+ * and the absence of any warranty.
+ *
+ * History
+ * Added support for CX2070x codec [www.conexant.com]
+*/
+
+
+// force to enable TX/RX on 2nd PCM interface.
+
+#ifndef _PASS1_COMPLETE_
+# ifdef CONFIG_SND_DIGICOLOR_SOC_CHANNEL_VER_3_13F
+# endif
+# ifdef CONFIG_SND_DIGICOLOR_SOC_CHANNEL_VER_4_30F
+# endif
+#endif
+
+ __REG(PLAYBACK_REGISTER, 0xffff, 0xffff, 0x00, 0, DM, B)
+ __REG(CAPTURE_REGISTER, 0xffff, 0xffff, 0x00, 0, DM, B)
+ __REG(MICBIAS_REGISTER, 0xffff, 0xffff, 0x00, 0, DM, B)
+
+/////////////////////////////////////////////////////////////////////////
+// General codec operations registers
+/////////////////////////////////////////////////////////////////////////
+// id addr data bias type
+ __REG(ABORT_CODE, 0x1000, 0x1000, 0x00, 0, RO,B)
+ __REG(FIRMWARE_VERSION, 0x1001, 0x1001, 0x00, 0, RO,W)
+ __REG(PATCH_VERSION, 0x1003, 0x1003, 0x00, 0, RO,W)
+ __REG(CHIP_VERSION, 0x1005, 0x1005, 0x00, 0, RO,B)
+ __REG(RELEASE_TYPE, 0x1006, 0x1006, 0x00, 0, RO,B)
+
+ __REG(ROM_PATCH_VER_HB, 0x1584, 0xFFFF, 0x00, 0, RO,B)
+ __REG(ROM_PATCH_VER_MB, 0x1585, 0xFFFF, 0x00, 0, RO,B)
+ __REG(ROM_PATCH_VER_LB, 0x1586, 0xFFFF, 0x00, 0, RO,B)
+
+ __REG(DAC1_GAIN_LEFT, 0x100D, 0x100D, 0xc9, 0x4A, RW,B)
+ __REG(DAC2_GAIN_RIGHT, 0x100E, 0x100E, 0xc9, 0x4A, RW,B)
+ __REG(DSP_MAX_VOLUME, 0x100F, 0x100F, 0x00, 0, RW,B)
+
+ __REG(CLASS_D_GAIN, 0x1011, 0x1010, b_00000000, 0, RW,B)
+#ifndef _PASS1_COMPLETE_
+#define CLASS_D_GAIN_2W8 b_00000000 // 2.8W
+#define CLASS_D_GAIN_2W6 b_00000001 // 2.6W
+#define CLASS_D_GAIN_2W5 b_00000010 // 2.5W
+#define CLASS_D_GAIN_2W4 b_00000011 // 2.4W
+#define CLASS_D_GAIN_2W3 b_00000100 // 2.3W
+#define CLASS_D_GAIN_2W2 b_00000101 // 2.2W
+#define CLASS_D_GAIN_2W1 b_00000110 // 2.1W
+#define CLASS_D_GAIN_2W0 b_00000111 // 2.0W
+#define CLASS_D_GAIN_1W3 b_00001000 // 1.3W
+#define CLASS_D_GAIN_1W25 b_00001001 // 1.25W
+#define CLASS_D_GAIN_1W2 b_00001010 // 1.2W
+#define CLASS_D_GAIN_1W15 b_00001011 // 1.15W
+#define CLASS_D_GAIN_1W1 b_00001100 // 1.1W
+#define CLASS_D_GAIN_1W05 b_00001101 // 1.05W
+#define CLASS_D_GAIN_1W0 b_00001110 // 1.0W
+#define CLASS_D_GAIN_0W9 b_00001111 // 0.9W
+#endif
+
+ __REG(DAC3_GAIN_SUB, 0x1012, 0x1011, 0x00, 0x4A, RW,B)
+
+ __REG(ADC1_GAIN_LEFT, 0x1013, 0x1012, 0x00, 0x4A, RW,B)
+ __REG(ADC1_GAIN_RIGHT, 0x1014, 0x1013, 0x00, 0x4A, RW,B)
+ __REG(ADC2_GAIN_LEFT, 0x1015, 0x1014, 0x00, 0x4A, RW,B)
+ __REG(ADC2_GAIN_RIGHT, 0x1016, 0x1015, 0x00, 0x4A, RW,B)
+ __REG(DSP_MAX_MIC_GAIN, 0x1017, 0x1016, 0x00, 0, RW,B)
+
+ __REG(VOLUME_MUTE, 0x1018, 0x1017, 0, 0, WI,B)
+#ifndef _PASS1_COMPLETE_
+#define LEFT_AUX_MUTE b_01000000
+#define RIGH_AUX_MUTE b_00100000
+#define LEFT_MIC_MUTE b_00010000
+#define RIGH_MIC_MUTE b_00001000
+#define SUB_SPEAKER_MUTE b_00000100
+#define LEFT_SPEAKER_MUTE b_00000010
+#define RIGH_SPEAKER_MUTE b_00000001
+#define VOLUME_MUTE_ALL b_01111111
+#endif
+
+//since the playback path is determined in register value,we have to enable one port.
+ __REG(OUTPUT_CONTROL, 0x1019, 0x1018, b_10000000, 0, WC,B) //class -d is selected by default.
+#ifndef _PASS1_COMPLETE_
+#define OUT_CTRL_AUTO b_10000000 // Automatic FW Control base on Jack Sense and DAC enables, 1= Auto, 0= Manual
+#define OUT_CTRL_SUB_DIFF b_01000000 // Sub Differential control, 1=Differential, 0=Single Ended
+#define OUT_CTRL_LO_DIFF b_00100000 // Line Out Differential control, 1=Differential, 0=Single Ended
+#define OUT_CTRL_CLASSD_OUT b_00010000 // ClassD Output, 1=PWM, 0=Speakers
+#define OUT_CTRL_CLASSD_MONO b_00001000 // ClassD Mono, 1=Mono, 0=Stereo
+#define OUT_CTRL_CLASSD_EN b_00000100 // If OutCTL[7]=0, 1=Enable ClassD Speakers, 0=Disable ClassD Speakers
+#define OUT_CTRL_LO_EN b_00000010 // If OutCTL[7]=0, 1=Enable Line Out, 0=Disable Line Out
+#define OUT_CTRL_HP_EN b_00000001 // If OutCTL[7]=0, 1=Enable Headphone, 0=Disable Headphone
+#endif
+
+ __REG(INPUT_CONTROL, 0x101A, 0x1019, b_10000000, 0, WI,B)
+#ifndef _PASS1_COMPLETE_
+#define IN_CTRL_AUTO b_10000000 // Automatic FW Control base on Jack Sense and ADC enables, 1=Auto, 0=Manual
+#define IN_CTRL_L1_DIFF b_00001000 // Line In 1 Differential control, 1=Differential, 0=Single Ended
+#define IN_CTRL_L3_EN b_00000100 // If LineCTL[7]=0, 1=Enable Line In 3, 0=Disable Line In 3
+#define IN_CTRL_L2_EN b_00000010 // If LineCTL[7]=0, 1=Enable Line In 2, 0=Disable Line In 2
+#define IN_CTRL_L1_EN b_00000001 // If LineCTL[7]=0, 1=Enable Line In 1, 0=Disable Line In 1
+#endif
+
+ __REG(LINE1_GAIN, 0x101B, 0x101A, b_00000000, 0, RW,B)
+#ifndef _PASS1_COMPLETE_
+#define LINE1_MUTE b_10000000 // 1=mute, 0=unmute
+#define LINE1_GAIN_MASK b_00011111 // range 0x00-0x1F (-35.5dB to +12dB)
+#endif
+
+ __REG(LINE2_GAIN, 0x101C, 0x101B, b_00000000, 0, RW,B)
+#ifndef _PASS1_COMPLETE_
+#define LINE2_MUTE b_10000000 // 1=mute, 0=unmute
+#define LINE2_GAIN_MASK b_00011111 // range 0x00-0x1F (-35.5dB to +12dB)
+#endif
+
+ __REG(LINE3_GAIN, 0x101D, 0x101C, b_00000000, 0, RW,B)
+#ifndef _PASS1_COMPLETE_
+#define LINE3_MUTE b_10000000 // 1=mute, 0=unmute
+#define LINE3_GAIN_MASK b_00011111 // range 0x00-0x1F (-35.5dB to +12dB)
+#endif
+
+ __REG(MIC_CONTROL, 0x101E, 0x101D, b_00000110, 0, WC,B)
+#ifndef _PASS1_COMPLETE_
+#define MICROPHONE_POWER_ALWAYS b_00010000 // 1 = leave microphone and bias always on to avoid pops (burns power), 0 = microphone powered up as needed, mute for 400ms to remove pops
+#define MICROPHONE_BIAS SELECT b_00001000 // 1= 80%, 0= 50%
+#define MICROPHONE_BOOST_MASK b_00000111 // 2:0 MicCTL [2:0] Microphone Boost in 6dB Steps, 0= 0dB, 7= +42dB
+#endif
+
+#if defined(CONFIG_SND_DIGICOLOR_SOC_CHANNEL_VER_3_13E)
+// adc
+ __REG(STREAM1_MIX, 0xffff, 0x101E, b_00000000, 0, WI,B)
+#ifndef _PASS1_COMPLETE_
+#define STREAM1_MUTE b_10000000 // 1=mute, 0=unmute
+#define STREAM1_GAIN_MASK b_00011111 // range 0x00-0x4A (0dB to -74dB)
+#endif
+// i2s
+ __REG(STREAM3_MIX, 0xffff, 0x101F, b_00000000, 0, WI,B)
+#ifndef _PASS1_COMPLETE_
+#define STREAM3_MUTE b_10000000 // 1=mute, 0=unmute
+#define STREAM3_GAIN_MASK b_00011111 // range 0x00-0x4A (0dB to -74dB)
+#endif
+// usb?
+ __REG(STREAM4_MIX, 0xffff, 0x1020, b_00000000, 0, WI,B)
+#ifndef _PASS1_COMPLETE_
+#define STREAM4_MUTE b_10000000 // 1=mute, 0=unmute
+#define STREAM4_GAIN_MASK b_00011111 // range 0x00-0x4A (0dB to -74dB)
+#endif
+#endif
+#if defined(CONFIG_SND_DIGICOLOR_SOC_CHANNEL_VER_4_30F)
+ __REG(MIX0_INPUT0, 0x101F, 0xffff, b_00000000, 0, WI,B) // stream1 out
+ __REG(MIX0_INPUT1, 0x1020, 0xffff, b_00000000, 0, WI,B) // stream3 out
+ __REG(MIX0_INPUT2, 0x1021, 0xffff, b_00000000, 0, WI,B) // stream4 out
+ __REG(MIX0_INPUT3, 0x1022, 0xffff, b_10000000, 0, WI,B) // none
+ __REG(MIX1_INPUT0, 0x1023, 0xffff, b_10000000, 0, WI,B) // none
+ __REG(MIX1_INPUT1, 0x1024, 0xffff, b_10000000, 0, WI,B) // none
+ __REG(MIX1_INPUT2, 0x1025, 0xffff, b_10000000, 0, WI,B) // none
+ __REG(MIX1_INPUT3, 0x1026, 0xffff, b_10000000, 0, WI,B) // nonw
+ __REG(MIX0_SOURCE0, 0x1184, 0xffff, b_00000000, 0, WI,B) // stream1 out
+ __REG(MIX0_SOURCE1, 0x1185, 0xffff, b_00000011, 0, WI,B) // stream3 out
+ __REG(MIX0_SOURCE2, 0x1186, 0xffff, b_00000100, 0, WI,B) // stream4 out
+ __REG(MIX0_SOURCE3, 0x1187, 0xffff, b_00000000, 0, WI,B) // none
+ __REG(MIX1_SOURCE0, 0x1188, 0xffff, b_00000001, 0, WI,B) // none
+ __REG(MIX1_SOURCE1, 0x1189, 0xffff, b_00000000, 0, WI,B) // none
+ __REG(MIX1_SOURCE2, 0x118a, 0xffff, b_00000000, 0, WI,B) // none
+ __REG(MIX1_SOURCE3, 0x118b, 0xffff, b_00000000, 0, WI,B) // none
+ __REG(VOICE_IN_SOURCE, 0x118c, 0xffff, b_00000010, 0, WI,B) // stream2
+ //__REG(VOICE_IN_SOURCE, 0x118c, 0xffff, 0x04, 0, WI,B) // stream2
+#endif
+
+/////////////////////////////////////////////////////////////////////////
+// Hardware registers
+/////////////////////////////////////////////////////////////////////////
+// id addr data bias type
+// __REG(CLOCK_DIVIDER, 0x0F50, 0x0F50, b_00001111, 0, WI,B) // Port1 external clock enabled
+ __REG(CLOCK_DIVIDER, 0x0F50, 0x0F50, 0xFF, 0, WI,B) // Port1 slave, Port2 Master 2.048 MHz
+#ifndef _PASS1_COMPLETE_
+#define PORT2_DIV_SEL_6_144MHz b_00000000 // 0x0 = 6.144 MHz
+#define PORT2_DIV_SEL_4_096MHz b_00010000 // 0x1 = 4.096 MHz
+#define PORT2_DIV_SEL_3_072MHz b_00100000 // 0x2 = 3.072 MHz
+#define PORT2_DIV_SEL_2_048MHz b_00110000 // 0x3 = 2.048 MHz
+#define PORT2_DIV_SEL_1_536MHz b_01000000 // 0x4 = 1.536 MHz
+#define PORT2_DIV_SEL_1_024MHz b_01010000 // 0x5 = 1.024 MHz
+#define PORT2_DIV_SEL_768kHz b_01100000 // 0x6 = 768kHz
+#define PORT2_DIV_SEL_512kHz b_01110000 // 0x7 = 512 kHz
+#define PORT2_DIV_SEL_384kHz b_10000000 // 0x8 = 384 kHz
+#define PORT2_DIV_SEL_256kHz b_10010000 // 0x9 = 256 kHz
+#define PORT2_DIV_SEL_5_644MHz b_10100000 // 0xa = 5.644 MHz
+#define PORT2_DIV_SEL_2_822MHz b_10110000 // 0xb = 2.822 MHz
+#define PORT2_DIV_SEL_1_411MHz b_11000000 // 0xc = 1.411 MHz
+#define PORT2_DIV_SEL_705kHz b_11010000 // 0xd = 705 kHz
+#define PORT2_DIV_SEL_352kHz b_11100000 // 0xe = 352 kHz
+#define PORT2_DIV_SEL_EXT b_11110000 // 0xf = external clock enabled
+#define PORT1_DIV_SEL_6_144MHz b_00000000 // 0x0 = 6.144 MHz
+#define PORT1_DIV_SEL_4_096MHz b_00000001 // 0x1 = 4.096 MHz
+#define PORT1_DIV_SEL_3_072MHz b_00000010 // 0x2 = 3.072 MHz
+#define PORT1_DIV_SEL_2_048MHz b_00000011 // 0x3 = 2.048 MHz
+#define PORT1_DIV_SEL_1_536MHz b_00000100 // 0x4 = 1.536 MHz
+#define PORT1_DIV_SEL_1_024MHz b_00000101 // 0x5 = 1.024 MHz
+#define PORT1_DIV_SEL_768kHz b_00000110 // 0x6 = 768kHz
+#define PORT1_DIV_SEL_512kHz b_00000111 // 0x7 = 512 kHz
+#define PORT1_DIV_SEL_384kHz b_00001000 // 0x8 = 384 kHz
+#define PORT1_DIV_SEL_256kHz b_00001001 // 0x9 = 256 kHz
+#define PORT1_DIV_SEL_5_644MHz b_00001010 // 0xa = 5.644 MHz
+#define PORT1_DIV_SEL_2_822MHz b_00001011 // 0xb = 2.822 MHz
+#define PORT1_DIV_SEL_1_411MHz b_00001100 // 0xc = 1.411 MHz
+#define PORT1_DIV_SEL_705kHz b_00001101 // 0xd = 705 kHz
+#define PORT1_DIV_SEL_352kHz b_00001110 // 0xe = 352 kHz
+#define PORT1_DIV_SEL_EXT b_00001111 // 0xf = external clock enabled
+#endif
+
+ __REG(PORT1_CONTROL, 0x0F51, 0x0F51, b_10110000, 0, WI,B)
+#ifndef _PASS1_COMPLETE_
+#define PORT1_DELAY b_10000000 // 1=Data delayed 1 bit (I2S standard), 0=no delay (sony mode)
+#define PORT1_JUSTR_LSBF b_01000000 // [1/0]=Right/Left Justify (I2S) or LSB/MSB First (PCM)
+#define PORT1_RX_EN b_00100000 // 1=RX Clock Enable, 0=RX Clock Disabled
+#define PORT1_TX_EN b_00010000 // 1=TX Clock Enable, 0=TX Clock Disabled
+//#define PORT1_ b_00001000 //
+#define PORT1_BITCLK_POL b_00000100 // 0=Normal clock, 1=Inverted clock
+#define PORT1_WS_POL b_00000010 // 0=Rising Edge Active for Word Strobe, 1=Falling Edge Active for Word Strobe
+#define PORT1_PCM_MODE b_00000001 // 0=I2S mode, 1=PCM Mode
+#endif
+
+ __REG(PORT1_TX_CLOCKS_PER_FRAME_PHASE,0x0F52, 0x0F52, b_00000011, 0, WI,B) // clocks/frame=(N+1)*8
+ __REG(PORT1_RX_CLOCKS_PER_FRAME_PHASE,0x0F53, 0x0F53, b_00000011, 0, WI,B) // clocks/frame=(N+1)*8
+ __REG(PORT1_TX_SYNC_WIDTH, 0x0F54, 0x0F54, b_00001111, 0, WI,B) // clocks=(N+1)
+ __REG(PORT1_RX_SYNC_WIDTH, 0x0F55, 0x0F55, b_00001111, 0, WI,B) // clocks=(N+1)
+
+ __REG(PORT1_CONTROL_2, 0x0F56, 0x0F56, b_00000101, 0, WI,B)
+#ifndef _PASS1_COMPLETE_
+#define PORT1_CTRL_TX_PT b_00100000 // Tx passthrough mode, 0=off, 1=on
+#define PORT1_CTRL_RX_PT b_00010000 // Rx passthrough mode, 0=off, 1=on
+#define PORT1_CTRL_RX_SIZE_8 b_00000000 // RX Sample Size, 00=8 bits
+#define PORT1_CTRL_RX_SIZE_16 b_00000100 // RX Sample Size, 01=16 bit
+#define PORT1_CTRL_RX_SIZE_24T b_00001000 // RX Sample Size, 10=24 bit truncated to 16 bits
+#define PORT1_CTRL_RX_SIZE_24 b_00001100 // RX Sample Size, 11=24 bit
+#define PORT1_CTRL_TX_SIZE_8 b_00000000 // TX Sample Size, 00=8 bits
+#define PORT1_CTRL_TX_SIZE_16 b_00000001 // TX Sample Size, 01=16 bit
+#define PORT1_CTRL_TX_SIZE_24T b_00000010 // TX Sample Size, 10=24 bit truncated to 16 bits
+#define PORT1_CTRL_TX_SIZE_24 b_00000011 // TX Sample Size, 11=24 bit
+#endif
+
+/////////////////////////////////////////////////////////////////////////
+// Codec registers, most need NEWC to be set
+/////////////////////////////////////////////////////////////////////////
+// id addr data bias type
+ __REG(STREAM2_RATE, 0x116b, 0xffff, 0xa2, 0, WI,B) // Mic
+#ifndef _PASS1_COMPLETE_
+#define STREAM2_STREAM_MONO_LEFT 0x00 //
+#define STREAM2_STREAM_MONO_RIGHT 0x40 //
+#define STREAM2_STREAM_STEREO 0x80 //
+#define STREAM2_SAMPLE_A_LAW 0x00 // 8-bit A law
+#define STREAM2_SAMPLE_U_LAW 0x10 // 8-bit µ law
+#define STREAM2_SAMPLE_16_LIN 0x20 // 16 bit linear
+#define STREAM2_SAMPLE_24_LIN 0x30 // 24 bit linear
+#define STREAM2_RATE_8000 0x00 // 8000 samples/sec
+#define STREAM2_RATE_11025 0x01 // 11025 samples/sec
+#define STREAM2_RATE_16000 0x02 // 16000 samples/sec
+#define STREAM2_RATE_22050 0x03 // 22050 samples/sec
+#define STREAM2_RATE_24000 0x04 // 24000 samples/sec
+#define STREAM2_RATE_32000 0x05 // 32000 samples/sec
+#define STREAM2_RATE_44100 0x06 // 44100 samples/sec
+#define STREAM2_RATE_48000 0x07 // 48000 samples/sec
+#define STREAM2_RATE_88200 0x08 // 88200 samples/sec
+#define STREAM2_RATE_96000 0x09 // 96000 samples/sec
+#endif
+
+ __REG(STREAM5_RATE, 0x1171, 0x112D, 0x26, 0, WI,B) // Mic -> I2S (5 wire)
+#ifndef _PASS1_COMPLETE_
+#define STREAM5_SAMPLE_A_LAW 0x00 // 8-bit A law
+#define STREAM5_SAMPLE_U_LAW 0x10 // 8-bit µ law
+#define STREAM5_SAMPLE_16_LIN 0x20 // 16 bit linear
+#define STREAM5_SAMPLE_24_LIN 0x30 // 24 bit linear
+#define STREAM5_RATE_8000 0x00 // 8000 samples/sec
+#define STREAM5_RATE_11025 0x01 // 11025 samples/sec
+#define STREAM5_RATE_16000 0x02 // 16000 samples/sec
+#define STREAM5_RATE_22050 0x03 // 22050 samples/sec
+#define STREAM5_RATE_24000 0x04 // 24000 samples/sec
+#define STREAM5_RATE_32000 0x05 // 32000 samples/sec
+#define STREAM5_RATE_44100 0x06 // 44100 samples/sec
+#define STREAM5_RATE_48000 0x07 // 48000 samples/sec
+#define STREAM5_RATE_88200 0x08 // 88200 samples/sec
+#define STREAM5_RATE_96000 0x09 // 96000 samples/sec
+#endif
+
+ __REG(STREAM3_RATE, 0x116D, 0x112F, 0xA6, 0, WI,B) // 44.1kHz, 16 bit linear
+#ifndef _PASS1_COMPLETE_
+#define STREAM3_STREAM_MONO_LEFT 0x00 //
+#define STREAM3_STREAM_MONO_RIGHT 0x40 //
+#define STREAM3_STREAM_STEREO 0x80 //
+#define STREAM3_SAMPLE_A_LAW 0x00 // 8-bit A law
+#define STREAM3_SAMPLE_U_LAW 0x10 // 8-bit µ law
+#define STREAM3_SAMPLE_16_LIN 0x20 // 16 bit linear
+#define STREAM3_SAMPLE_24_LIN 0x30 // 24 bit linear
+#define STREAM3_RATE_8000 0x00 // 8000 samples/sec
+#define STREAM3_RATE_11025 0x01 // 11025 samples/sec
+#define STREAM3_RATE_16000 0x02 // 16000 samples/sec
+#define STREAM3_RATE_22050 0x03 // 22050 samples/sec
+#define STREAM3_RATE_24000 0x04 // 24000 samples/sec
+#define STREAM3_RATE_32000 0x05 // 32000 samples/sec
+#define STREAM3_RATE_44100 0x06 // 44100 samples/sec
+#define STREAM3_RATE_48000 0x07 // 48000 samples/sec
+#define STREAM3_RATE_88200 0x08 // 88200 samples/sec
+#define STREAM3_RATE_96000 0x09 // 96000 samples/sec
+#endif
+
+ __REG(STREAM_3_ROUTING, 0x116E, 0x1130, 0x02, 0, WI,B)
+#ifndef _PASS1_COMPLETE_
+#define STREAM3_ROUTE_SRC_D1 0x00 // Source = Digital Port1
+#define STREAM3_ROUTE_SRC_D2 0x10 // Source = Digital Port2
+#define STREAM3_ROUTE_DST_D1 0x00 // Destination = Digital Port1
+#define STREAM3_ROUTE_DST_D2 0x01 // Destination = Digital Port2
+#define STREAM3_ROUTE_DST_DAC 0x02 // Destination = DAC
+#define STREAM3_ROUTE_DST_SUB 0x03 // Destination = DAC (sub)
+#define STREAM3_ROUTE_DST_SPDIF 0x04 // Destination = SPDIF
+#define STREAM3_ROUTE_DST_USB 0x05 // Destination = USB
+#endif
+
+ __REG(EQ_GAIN, 0x10D7, 0x10D1, 0x1000, 0, WI,W)
+ __REG(EQS_GAIN, 0x10D9, 0x10D3, 0x1000, 0, WI,W)
+
+// __REG(SPDIF_CODE, 0x1178, 0x1134, 0x00, 0, WI,B)
+// __REG(SPDIF_CONTROL, 0x1179, 0x1135, 0x00, 0, WI,B)
+
+ __REG(DSP_PROCESSING_ENABLE_1, 0x117A, 0x1136, 0x00, 0, WC,B)
+#ifndef _PASS1_COMPLETE_
+#define RIGHT_MIKE b_01000000
+#define IN_NOISE_REDUCTION b_00100000
+#define MIC_AGC b_00010000
+#define BEAM_FORMING b_00001000
+#define NOICE_REDUCTION b_00000100
+#define LEC b_00000010
+#define AEC b_00000001
+#endif
+ __REG(DSP_PROCESSING_ENABLE_2, 0x117B, 0x1137, 0x00, 0, WC,B)
+#ifndef _PASS1_COMPLETE_
+#define DSP_MONO_OUTPUT b_00100000 // 0=Stereo, 1=Mono (L+R)=L (L+R)=R
+#define LOUDNESS_ADAPTER b_00010000 // 1=Enable, 0=Disable
+#define STAGE_ENHANCER b_00001000 // 1=Enable 3D processing, 0=Disable
+#define DYNAMIC_RANGE_COMPRESSION b_00000100 // 1=Enable, 0=Disable
+#define SUBWOOFER_CROSSOVER b_00000010 // 1=Enable, 0=Disable
+#define EQUALIZER_10_BAND b_00000001 // 1=Enable, 0=Disable
+#endif
+
+#if defined(CONFIG_SND_DIGICOLOR_SOC_CHANNEL_VER_4_30F)
+ __REG(DSP_INIT_H, 0x117C, 0xffff, 0x00, 0, WI,B) // special
+ __REG(DSP_INIT, 0x117D, 0xffff, 0x00, 0, WC,B) // special
+ __REG(DSP_POWER, 0x117E, 0xffff, 0xE0, 0, WC,B) // special
+
+#ifndef _PASS1_COMPLETE_
+#define DSP_INIT_NEWC b_00000001
+#define DSP_INIT_STREAM_OFF b_00000001
+#define DSP_INIT_STREAM_3 b_10001001 // enable stream 3 and 7
+#define DSP_INIT_STREAM_5 b_00100101 // enable stream 2 and 5
+#define DSP_INIT_STREAM_5_3 b_10101101 // enable streams 2,3,5,7
+#endif
+#else
+ __REG(DSP_INIT, 0xffff, 0x1138, 0x00, 0, WI,B) // special
+#ifndef _PASS1_COMPLETE_000000000000000000000000
+#define DSP_INIT_NEWC b_00000001
+#define DSP_INIT_STREAM_OFF b_00000001
+#define DSP_INIT_STREAM_3 b_00001001
+#define DSP_INIT_STREAM_5 b_00100001
+#define DSP_INIT_STREAM_5_3 b_00101001
+#endif
+#endif
+
+#ifdef CX20709_TRISTATE_EEPROM
+ __REG(PAD, 0x0004, 0x0004, 0x00, 0, WO,B)
+ __REG(PBD, 0x0005, 0x0005, 0x00, 0, W0,B)
+#endif
+// temp code.
+// added rerouting render stream to second I2S output ( 16 Kbps/ 16BITS)
+ __REG(STREAM4_RATE, 0x116F, 0xffff, 0xA7, 0, WI,B) // dsp -> PCM-2 8Kbps output
+ __REG(STREAM4_ROUTING, 0x1170, 0xffff, 0x12, 0, WI,B)
+ __REG(STREAM6_RATE, 0x1172, 0xffff, 0x27, 0, WI,B) // dsp -> PCM-2 8Kbps output
+ __REG(STREAM7_RATE, 0x1173, 0xffff, 0x07, 0, WC,B) // dsp -> I2S-2 (5 wire)
+ __REG(STREAMOP_ROUTING, 0x1176, 0xffff, 0x60, 0, WC,B) // AEC narrow band. 48 KHz
+ __REG(STREAM6_ROUTING, 0x1182, 0xffff, 0x06, 0, WI,B)
+ __REG(STREAM7_SOURCE, 0x117F, 0xffff, 0x05, 0, WI,B) // dsp -> I2S-2 (5 wire)
+ __REG(STREAM8_SOURCE, 0x1180, 0xffff, 0x05, 0, WC,B)
+ __REG(STREAM8_RATE, 0x1175, 0xffff, 0x07, 0, WC,B)
+ __REG(PORT2_CONTROL, 0x0F5E, 0x0F5E, 0XB0, 0, WI,B) // Delay 1 bit, RX/TX en, mode =i2s
+ __REG(PORT2_CLOCK_PER_FRAME, 0x0F5F, 0x0F5F, 0X07, 0, WI,B) // 64-bits per frame.
+ __REG(PORT2_SYNC_WIDTH, 0x0F60, 0x0F60, 0X0f, 0, WI,B) // clocks=(N+1)
+ __REG(PORT2_SAMPLE_WIDTH, 0x0F61, 0x0F61, 0X01, 0, WI,B) // 16 bits.
+ __REG(PORT2_RX_STREAM1, 0x0F62, 0x0F62, 0X20, 0, WI,B) // RX 1 <- Slot 0
+ __REG(PORT2_RX_STREAM2, 0x0F63, 0x0F63, 0X24, 0, WI,B) // RX 2 <- Slot 4
+ __REG(PORT2_TX_STREAM1, 0x0F65, 0x0F65, 0X20, 0, WI,B) // TX 1 -> Slot 0
+ __REG(PORT2_TX_STREAM2, 0x0F66, 0x0F66, 0X24, 0, WI,B) // TX 2 -> Slot 4
+
+#ifndef _PASS1_COMPLETE_
+#define _PASS1_COMPLETE_
+#endif
--- /dev/null
+/*
+* ALSA SoC CX2070X codec driver
+*
+* Copyright: (C) 2009/2010 Conexant Systems
+*
+* Based on sound/soc/codecs/tlv320aic2x.c by Vladimir Barinov
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+*
+*
+*************************************************************************
+* Modified Date: 09/14/12
+* File Version: 3.1.10.13
+*************************************************************************
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/gpio.h>
+#include <sound/jack.h>
+#include <linux/slab.h>
+
+#include "cx2070x.h"
+
+#define CX2070X_DRIVER_VERSION AUDDRV_VERSION( 3, 1 ,0x10 ,0x13)
+
+#ifdef USING_I2C
+#include <linux/i2c.h>
+#endif
+
+#ifdef USING_SPI
+#include <linux/spi/spi.h>
+#endif
+
+#if defined(CONFIG_SND_CXLIFEGUARD)
+#include "cxdebug.h"
+#endif
+
+#ifdef CONFIG_SND_CX2070X_LOAD_FW
+#ifdef CONFIG_SND_CX2070X_USE_FW_H
+#include "cx2070x_fw.h"
+#else
+#include <linux/firmware.h>
+#endif
+#include "cxpump.h"
+#endif
+
+
+#define CX2070X_TRISTATE_EEPROM 0
+#define CX2070X_REG_NAMES 1
+#define CX2070X_REG_WIDE 1
+
+
+#define AUDIO_NAME "cx2070x"
+
+
+#define CX2070X_RATES ( \
+ SNDRV_PCM_RATE_8000 \
+ | SNDRV_PCM_RATE_11025 \
+ | SNDRV_PCM_RATE_16000 \
+ | SNDRV_PCM_RATE_22050 \
+ | SNDRV_PCM_RATE_32000 \
+ | SNDRV_PCM_RATE_44100 \
+ | SNDRV_PCM_RATE_48000 \
+ | SNDRV_PCM_RATE_88200 \
+ | SNDRV_PCM_RATE_96000 )
+
+#if defined(CONFIG_SND_DIGICOLOR_SOC_CHANNEL_VER_4_30F)
+
+#define CX2070X_FORMATS ( SNDRV_PCM_FMTBIT_S16_LE \
+ | SNDRV_PCM_FMTBIT_S16_BE \
+ | SNDRV_PCM_FMTBIT_MU_LAW \
+ | SNDRV_PCM_FMTBIT_A_LAW )
+#else
+#define CX2070X_FORMATS ( SNDRV_PCM_FMTBIT_S16_LE \
+ | SNDRV_PCM_FMTBIT_S16_BE )
+#endif
+
+
+
+#define noof(a) (sizeof(a)/sizeof(a[0]))
+#define NOINLINE __attribute__((__noinline__))
+//#ifdef DEBUG
+#if 1
+# define INFO(fmt,...) printk(KERN_INFO fmt, ##__VA_ARGS__)
+# define _INFO(fmt,...) printk(KERN_INFO fmt, ##__VA_ARGS__)
+# define _INFO_ 1
+#else
+# define INFO(fmt,...)
+# define _INFO(fmt,...)
+# define _INFO_ 1
+#endif
+#define MSG(fmt,...) printk(KERN_INFO fmt, ##__VA_ARGS__)
+#define ERROR(fmt,...) printk(KERN_ERR fmt, ##__VA_ARGS__)
+
+enum {
+ b_00000000,b_00000001,b_00000010,b_00000011, b_00000100,b_00000101,b_00000110,b_00000111,
+ b_00001000,b_00001001,b_00001010,b_00001011, b_00001100,b_00001101,b_00001110,b_00001111,
+ b_00010000,b_00010001,b_00010010,b_00010011, b_00010100,b_00010101,b_00010110,b_00010111,
+ b_00011000,b_00011001,b_00011010,b_00011011, b_00011100,b_00011101,b_00011110,b_00011111,
+ b_00100000,b_00100001,b_00100010,b_00100011, b_00100100,b_00100101,b_00100110,b_00100111,
+ b_00101000,b_00101001,b_00101010,b_00101011, b_00101100,b_00101101,b_00101110,b_00101111,
+ b_00110000,b_00110001,b_00110010,b_00110011, b_00110100,b_00110101,b_00110110,b_00110111,
+ b_00111000,b_00111001,b_00111010,b_00111011, b_00111100,b_00111101,b_00111110,b_00111111,
+ b_01000000,b_01000001,b_01000010,b_01000011, b_01000100,b_01000101,b_01000110,b_01000111,
+ b_01001000,b_01001001,b_01001010,b_01001011, b_01001100,b_01001101,b_01001110,b_01001111,
+ b_01010000,b_01010001,b_01010010,b_01010011, b_01010100,b_01010101,b_01010110,b_01010111,
+ b_01011000,b_01011001,b_01011010,b_01011011, b_01011100,b_01011101,b_01011110,b_01011111,
+ b_01100000,b_01100001,b_01100010,b_01100011, b_01100100,b_01100101,b_01100110,b_01100111,
+ b_01101000,b_01101001,b_01101010,b_01101011, b_01101100,b_01101101,b_01101110,b_01101111,
+ b_01110000,b_01110001,b_01110010,b_01110011, b_01110100,b_01110101,b_01110110,b_01110111,
+ b_01111000,b_01111001,b_01111010,b_01111011, b_01111100,b_01111101,b_01111110,b_01111111,
+ b_10000000,b_10000001,b_10000010,b_10000011, b_10000100,b_10000101,b_10000110,b_10000111,
+ b_10001000,b_10001001,b_10001010,b_10001011, b_10001100,b_10001101,b_10001110,b_10001111,
+ b_10010000,b_10010001,b_10010010,b_10010011, b_10010100,b_10010101,b_10010110,b_10010111,
+ b_10011000,b_10011001,b_10011010,b_10011011, b_10011100,b_10011101,b_10011110,b_10011111,
+ b_10100000,b_10100001,b_10100010,b_10100011, b_10100100,b_10100101,b_10100110,b_10100111,
+ b_10101000,b_10101001,b_10101010,b_10101011, b_10101100,b_10101101,b_10101110,b_10101111,
+ b_10110000,b_10110001,b_10110010,b_10110011, b_10110100,b_10110101,b_10110110,b_10110111,
+ b_10111000,b_10111001,b_10111010,b_10111011, b_10111100,b_10111101,b_10111110,b_10111111,
+ b_11000000,b_11000001,b_11000010,b_11000011, b_11000100,b_11000101,b_11000110,b_11000111,
+ b_11001000,b_11001001,b_11001010,b_11001011, b_11001100,b_11001101,b_11001110,b_11001111,
+ b_11010000,b_11010001,b_11010010,b_11010011, b_11010100,b_11010101,b_11010110,b_11010111,
+ b_11011000,b_11011001,b_11011010,b_11011011, b_11011100,b_11011101,b_11011110,b_11011111,
+ b_11100000,b_11100001,b_11100010,b_11100011, b_11100100,b_11100101,b_11100110,b_11100111,
+ b_11101000,b_11101001,b_11101010,b_11101011, b_11101100,b_11101101,b_11101110,b_11101111,
+ b_11110000,b_11110001,b_11110010,b_11110011, b_11110100,b_11110101,b_11110110,b_11110111,
+ b_11111000,b_11111001,b_11111010,b_11111011, b_11111100,b_11111101,b_11111110,b_11111111,
+};
+
+#define REG_TYPE_RO 0 // read only, read during initialization
+#define REG_TYPE_RW 1 // read/write, read during initialization
+#define REG_TYPE_WI 2 // write only, written during initialization
+#define REG_TYPE_WC 3 // write/init, needs NEWC to be set when written
+#define REG_TYPE_DM 4 // dummy register, read/write to cache only
+#if CX2070X_REG_WIDE
+# define REG_TYPE_MASK 0x0F
+# define REG_WIDTH_B 0x00 // 8-bit data
+# define REG_WIDTH_W 0x10 // 16-bit data
+# define REG_WIDTH_MASK 0xF0
+#endif
+enum {
+#define __REG(a,b2,b1,c,d,e,f) a,
+#include "cx2070x-i2c.h"
+#undef __REG
+};
+
+#if CX2070X_REG_WIDE
+typedef u16 cx2070x_reg_t;
+#else
+typedef u8 cx2070x_reg_t;
+#endif
+static const cx2070x_reg_t cx2070x_data[]=
+{
+#define __REG(a,b2,b1,c,d,e,f) c,
+#include "cx2070x-i2c.h"
+#undef __REG
+};
+
+struct cx2070x_reg
+{
+#if CX2070X_REG_NAMES
+ char *name;
+#endif
+ u16 addr;
+ u8 bias;
+ u8 type;
+};
+
+static const struct cx2070x_reg cx2070x_regs[]=
+{
+#if defined(CONFIG_SND_DIGICOLOR_SOC_CHANNEL_VER_3_13E)
+# if CX2070X_REG_NAMES
+# define __REG(a,b2,b1,c,d,e,f) { #a,b1,d,REG_TYPE_##e|REG_WIDTH_##f },
+# else
+# define __REG(a,b2,b1,c,d,e,f) { b1,d,REG_TYPE_##e|REG_WIDTH_##f },
+# endif
+#elif defined(CONFIG_SND_DIGICOLOR_SOC_CHANNEL_VER_4_30F)
+# if CX2070X_REG_NAMES
+# define __REG(a,b2,b1,c,d,e,f) { #a,b2,d,REG_TYPE_##e|REG_WIDTH_##f },
+# else
+# define __REG(a,b2,b1,c,d,e,f) { b2,d,REG_TYPE_##e|REG_WIDTH_##f },
+# endif
+#else
+# if CX2070X_REG_NAMES
+# define __REG(a,b2,b1,c,d,e,f) { #a,b2,d,REG_TYPE_##e|REG_WIDTH_##f },
+# else
+# define __REG(a,b2,b1,c,d,e,f) { b2,d,REG_TYPE_##e|REG_WIDTH_##f },
+# endif
+#endif
+#include "cx2070x-i2c.h"
+#undef __REG
+};
+
+// codec private data
+struct cx2070x_priv
+{
+ enum snd_soc_control_type control_type;
+ void *control_data;
+ unsigned int sysclk;
+ int master;
+ enum Cx_INPUT_SEL input_sel;
+ enum Cx_OUTPUT_SEL output_sel;
+ unsigned int mute;
+};
+
+#define get_cx2070x_priv(_codec_) ((struct cx2070x_priv *)snd_soc_codec_get_drvdata(codec))
+
+#if defined(CONFIG_CXNT_SOFTWOARE_SIMULATION)
+static int bNoHW = 1;
+#else
+static int bNoHW = 0;
+#endif
+
+
+/*
+ * Playback Volume
+ *
+ * max : 0x00 : 0 dB
+ * ( 1 dB step )
+ * min : 0xB6 : -74 dB
+ */
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7400 , 100, 0);
+
+
+/*
+ * Capture Volume
+ *
+ * max : 0x00 : 0 dB
+ * ( 1 dB step )
+ * min : 0xB6 : -74 dB
+ */
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7400 , 100, 0);
+
+
+#if defined (CONFIG_SND_CX2070X_GPIO_JACKSENSE)
+// TODO : the jack sensing code should be moved to machine layer.
+static struct snd_soc_jack hs_jack ;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ /*.list_head list*/{},
+ /*.pin*/"Headphone",
+ /*.mask*/SND_JACK_HEADPHONE,
+ /*.invert*/1
+ },
+ {
+ /*.list_head list*/{},
+ /*.pin*/"INT SPK",
+ /*.mask*/SND_JACK_HEADPHONE,
+ /*.invert*/0
+ }
+};
+
+/* Headset jack detection gpios */
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+ {
+ /*.gpio*/ JACK_SENSE_GPIO_PIN,
+ /*.name*/ "hsdet-gpio",
+ /*.report*/ SND_JACK_HEADSET,
+ /*.invert*/ 0,
+ /*.debounce_time*/ 200,
+ /*.jack*/ NULL,
+ /*.work*/ NULL,
+ },
+};
+
+#endif //CONFIG_SND_CX2070X_GPIO_JACKSENSE
+
+#if defined(CONFIG_SND_CX2070X_LOAD_FW)
+int I2cWrite( struct snd_soc_codec *codec, unsigned char ChipAddr, unsigned long cbBuf, unsigned char* pBuf);
+int I2cWriteThenRead( struct snd_soc_codec *codec, unsigned char ChipAddr, unsigned long cbBuf,
+ unsigned char* pBuf, unsigned long cbReadBuf, unsigned char*pReadBuf);
+#endif
+
+#define GET_REG_CACHE(_codec_) (cx2070x_reg_t *) (_codec_)->reg_cache
+static inline unsigned int cx2070x_read_reg_cache(struct snd_soc_codec *codec, unsigned int reg)
+{
+ cx2070x_reg_t *reg_cache;
+ if (reg >= noof(cx2070x_regs))
+ return (unsigned int)0;
+ reg_cache = GET_REG_CACHE(codec);
+ return reg_cache[reg];
+}
+
+static inline void cx2070x_write_reg_cache(struct snd_soc_codec *codec, unsigned int reg, unsigned int value)
+{
+ cx2070x_reg_t *reg_cache;
+ if (reg >= noof(cx2070x_regs))
+ return;
+ reg_cache=GET_REG_CACHE(codec);
+ reg_cache[reg] = value;
+}
+
+#ifdef USING_SPI
+static int NOINLINE cx2070x_real_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value)
+{ /* SPI bus */
+ int ret;
+ u8 data[4];
+ struct spi_device * spi = (struct spi_device *) codec->control_data;
+ int len=0;
+ const struct cx2070x_reg *ri;
+
+ ri=&cx2070x_regs[reg];
+
+
+ switch(ri->type®_TYPE_MASK)
+ {
+ case REG_TYPE_RO: // read only, read during initialization
+#if CX2070X_REG_NAMES
+ ERROR("%s(): write to Read-only register '%s'\n",__func__,ri->name);
+#endif
+ break;
+
+ case REG_TYPE_RW: // read/write, read during initialization
+ case REG_TYPE_WI: // write only, written during initialization
+ case REG_TYPE_WC: // write/init, needs NEWC to be set when written
+ // msg[0].addr = client->addr;
+ // msg[0].flags = client->flags & I2C_M_TEN;
+ data[0]=(u8)(ri->addr>>8);
+ data[1]=(u8)(ri->addr>>0);
+ switch(ri->type®_WIDTH_MASK)
+ {
+ case REG_WIDTH_B:
+ data[2]=(u8)(value-ri->bias);
+ len=3;
+ break;
+ case REG_WIDTH_W:
+ data[2]=(u8)((value-ri->bias)>>0)&0xFF;
+ data[3]=(u8)((value-ri->bias)>>8)&0xFF;
+ len=4;
+ break;
+ default:
+ return -EIO;
+ }
+ data[0] |= 0x80; //Write flag.
+#ifdef DBG_MONITOR_REG
+ printk(KERN_ERR "Write REG %02x%02x %02x\n",data[0],data[1],data[2]);
+#endif
+ spi_write(spi, data, len);
+ break;
+
+#if defined(REG_TYPE_DM)
+ case REG_TYPE_DM: // dummy register, no I2C transfers
+ break;
+#endif
+ }
+
+ cx2070x_write_reg_cache(codec,reg,value);
+ return 0;
+}
+
+static int NOINLINE cx2070x_real_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ struct spi_device * spi = (struct spi_device *) codec->control_data;
+ int len=0;
+
+ u8 data[4];
+ const struct cx2070x_reg *ri;
+ int dat;
+ int ret;
+ ri=&cx2070x_regs[reg];
+
+ if ((ri->type®_TYPE_MASK)==REG_TYPE_DM)
+ return cx2070x_read_reg_cache(codec,reg);
+
+ data[0]=(u8)(ri->addr>>8);
+ data[1]=(u8)(ri->addr>>0);
+ len = ((ri->type®_WIDTH_MASK)==REG_WIDTH_W)?2:1;
+ data[2] = 0;
+ if (spi_write_then_read(spi, &data[0], 3, &data[2],len))
+ {
+
+ }
+ switch(ri->type®_WIDTH_MASK)
+ {
+ case REG_WIDTH_B:
+ dat=ri->bias+data[2];
+ break;
+ case REG_WIDTH_W:
+ dat=ri->bias+(data[2]<<0)+(data[3]<<8);
+ break;
+ default:
+ return -EIO;
+ }
+ cx2070x_write_reg_cache(codec,reg,dat);
+ return dat;
+}
+#else
+static int NOINLINE cx2070x_real_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value)
+{
+ struct i2c_client *client = (struct i2c_client *) codec->control_data;
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg[2];
+ u8 data[4];
+ const struct cx2070x_reg *ri;
+ if(reg == MIC_CONTROL)
+ printk(">>>>>>>>>>>>>%s value = %0x\n", __func__, value);
+ if(reg == MIC_CONTROL)
+ dump_stack();
+
+ ri=&cx2070x_regs[reg];
+
+ switch(ri->type®_TYPE_MASK)
+ {
+ case REG_TYPE_RO: // read only, read during initialization
+#if CX2070X_REG_NAMES
+ ERROR("%s(): write to Read-only register '%s'\n",__func__,ri->name);
+#endif
+ break;
+
+ case REG_TYPE_RW: // read/write, read during initialization
+ case REG_TYPE_WI: // write only, written during initialization
+ case REG_TYPE_WC: // write/init, needs NEWC to be set when written
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags & I2C_M_TEN;
+ msg[0].buf = &data[0];
+ data[0]=(u8)(ri->addr>>8);
+ data[1]=(u8)(ri->addr>>0);
+ switch(ri->type®_WIDTH_MASK)
+ {
+ case REG_WIDTH_B:
+ data[2]=(u8)(value-ri->bias);
+ msg[0].len=3;
+ break;
+ case REG_WIDTH_W:
+ data[2]=(u8)((value-ri->bias)>>0)&0xFF;
+ data[3]=(u8)((value-ri->bias)>>8)&0xFF;
+ msg[0].len=4;
+ break;
+ default:
+ return -EIO;
+ }
+#ifdef DBG_MONITOR_REG
+ printk(KERN_ERR "Write REG %02x%02x %02x\n",data[0],data[1],data[2]);
+#endif
+
+ if (i2c_transfer(adap,msg,1)!=1)
+ return -EIO;
+ break;
+
+#if defined(REG_TYPE_DM)
+ case REG_TYPE_DM: // dummy register, no I2C transfers
+ break;
+#endif
+ }
+
+ cx2070x_write_reg_cache(codec,reg,value);
+ return 0;
+}
+
+static int NOINLINE cx2070x_real_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ struct i2c_client *client =(struct i2c_client *) codec->control_data;
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg[2];
+ u8 data[4];
+ const struct cx2070x_reg *ri;
+ int dat;
+
+ ri=&cx2070x_regs[reg];
+
+ if ((ri->type®_TYPE_MASK)==REG_TYPE_DM)
+ return cx2070x_read_reg_cache(codec,reg);
+
+ data[0]=(u8)(ri->addr>>8);
+ data[1]=(u8)(ri->addr>>0);
+
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags & I2C_M_TEN;
+ msg[0].len = 2;
+ msg[0].buf = &data[0];
+
+ msg[1].addr = client->addr;
+ msg[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+ msg[1].len = ((ri->type®_WIDTH_MASK)==REG_WIDTH_W)?2:1;
+ msg[1].buf = &data[2];
+
+ if (i2c_transfer(adap,msg,2)!=2)
+ return -EIO;
+
+ switch(ri->type®_WIDTH_MASK)
+ {
+ case REG_WIDTH_B:
+ dat=ri->bias+data[2];
+ break;
+ case REG_WIDTH_W:
+ dat=ri->bias+(data[2]<<0)+(data[3]<<8);
+ break;
+ default:
+ return -EIO;
+ }
+ cx2070x_write_reg_cache(codec,reg,dat);
+ return dat;
+}
+#endif //#!ENABLE_SPI
+
+// reset codec via gpio pin.
+#if defined(CONFIG_SND_CX2070X_GPIO_RESET)
+static int cx2070x_reset_device(void)
+{
+
+ int err = 0;
+ int reset_pin = CODEC_RESET_GPIO_PIN;
+ INFO("%lu: %s() called\n",jiffies,__func__);
+ if (gpio_is_valid(reset_pin)) {
+ if (gpio_request(reset_pin, "reset_pin")) {
+ printk( KERN_ERR "cx2070x: reset pin %d not available\n",reset_pin);
+ err = -ENODEV;
+ } else {
+ gpio_direction_output(reset_pin, 1);
+ mdelay(3);
+ gpio_set_value(reset_pin, 0);
+ //udelay(1);// simon :need to re-check the reset timing.
+ mdelay(3);
+ gpio_set_value(reset_pin, 1);
+ gpio_free(reset_pin);
+ mdelay(200); //simon :not sure how long the device become ready.
+ }
+ }
+ else
+ {
+ printk( KERN_ERR "cx2070x: reset pin %d is not valid\n",reset_pin);
+ err = -ENODEV;
+ }
+ return err;
+}
+#endif //#if defined(CONFIG_SND_CX2070X_GPIO_RESET)
+
+
+static int cx2070x_dsp_init(struct snd_soc_codec *codec,unsigned mode)
+{
+ unsigned r;
+ cx2070x_real_write(codec,DSP_INIT,mode);
+ printk("******************%s mode = %0x\n",__func__, mode);
+ // maximum time for the NEWC to clear is about 2ms.
+ for(r=1000;;)
+ if (!(cx2070x_real_read(codec,DSP_INIT)&DSP_INIT_NEWC))
+ return 0;
+ else if (--r==0)
+ return -EIO;
+ else
+ msleep(1);
+}
+
+static int NOINLINE cx2070x_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value)
+{
+ int err = 0;
+
+ if ((err=cx2070x_real_write(codec,reg,value))<0)
+ return err;
+
+ switch(cx2070x_regs[reg].type®_TYPE_MASK)
+ {
+ case REG_TYPE_WC:
+ printk("^^^^^^^^^%0x\n",cx2070x_read_reg_cache(codec,DSP_INIT));
+ printk("^^^^^^^^^%0x\n",cx2070x_read_reg_cache(codec,DSP_INIT)|DSP_INIT_NEWC);
+ return cx2070x_dsp_init(codec,cx2070x_read_reg_cache(codec,DSP_INIT)|DSP_INIT_NEWC);
+ default:
+ return err;
+ }
+}
+
+
+static int output_select_event_set(struct snd_kcontrol *kcontrol,
+struct snd_ctl_elem_value *ucontrol)
+{
+
+ int changed = 0;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *control = (struct soc_enum *)kcontrol->private_value;
+ struct cx2070x_priv *channel = get_cx2070x_priv(codec);
+
+ // unsigned short sel;
+
+ /* Refuse any mode changes if we are not able to control the codec. */
+ if (!codec->control_data)
+ return -EUNATCH;
+
+ if (ucontrol->value.enumerated.item[0] >= control->max)
+ return -EINVAL;
+
+ mutex_lock(&codec->mutex);
+
+ /* Translate selection to bitmap */
+ channel->output_sel = (enum Cx_OUTPUT_SEL) ucontrol->value.enumerated.item[0];
+
+
+ switch(ucontrol->value.enumerated.item[0])
+ {
+ case Cx_OUTPUT_SEL_BY_GPIO:
+ {
+ //disable BT output.
+ snd_soc_dapm_disable_pin(&codec->dapm, "BT OUT");
+ //snd_soc_dapm_disable_pin(codec, "Headphone");
+ snd_soc_dapm_disable_pin(&codec->dapm, "BT OUT");
+ //enable analog pin
+#if defined(CONFIG_SND_CX2070X_GPIO_JACKSENSE)
+ #ifdef CONFIG_GPIOLIB
+ // snd_soc_jack_gpio_detect(&hs_jack_gpios[0]);
+ //snd_soc_dapm_enable_pin(codec, "INT SPK");
+ #else
+ snd_soc_dapm_enable_pin(&codec->dapm, "INT SPK");
+ #endif
+#else
+ snd_soc_dapm_enable_pin(&codec->dapm, "INT SPK");
+#endif //if defined(CONFIG_SND_CX2070X_GPIO_JACKSENSE)
+ break;
+ }
+ case Cx_OUTPUT_SEL_SPK:
+ case Cx_OUTPUT_SEL_LINE:
+ {
+ snd_soc_dapm_disable_pin(&codec->dapm, "BT OUT");
+ snd_soc_dapm_disable_pin(&codec->dapm, "Headphone");
+
+ snd_soc_dapm_enable_pin(&codec->dapm, "INT SPK");
+ break;
+ }
+ case Cx_OUTPUT_SEL_HP:
+ {
+ snd_soc_dapm_disable_pin(&codec->dapm, "BT OUT");
+ snd_soc_dapm_disable_pin(&codec->dapm, "INT SPK");
+ snd_soc_dapm_enable_pin(&codec->dapm, "Headphone");
+ break;
+ }
+ case Cx_OUTPUT_SEL_DPORT2:
+ {
+ snd_soc_dapm_disable_pin(&codec->dapm, "INT SPK");
+ snd_soc_dapm_disable_pin(&codec->dapm, "Headphone");
+ snd_soc_dapm_enable_pin(&codec->dapm, "BT OUT");
+ break;
+ }
+ default:;
+ printk( KERN_ERR "output mode is not valid\n");
+ }
+
+ snd_soc_dapm_sync(&codec->dapm);
+ mutex_unlock(&codec->mutex);
+ return changed;
+}
+
+static int output_select_event_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cx2070x_priv *channel = get_cx2070x_priv(codec);
+ ucontrol->value.enumerated.item[0] = channel->output_sel;
+ return 0;
+}
+
+
+static const char *output_select_mode[] =
+{"AUTO", "SPK" ,"LINE", "HP" ,"PCM2"};
+
+static const struct soc_enum output_select_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(output_select_mode),
+ output_select_mode),
+};
+
+static const char *input_select_mode[] =
+{"MIC", "PCM" };
+
+static const struct soc_enum input_select_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_select_mode),
+ input_select_mode),
+};
+
+static const struct snd_kcontrol_new input_select_controls =
+SOC_DAPM_ENUM("Route", input_select_enum);
+
+static const struct snd_kcontrol_new cx2070x_snd_controls[]=
+{
+ // Output
+ SOC_DOUBLE_R_TLV("Master Playback Volume", DAC1_GAIN_LEFT, DAC2_GAIN_RIGHT, 0, 74, 0,dac_tlv),
+ SOC_SINGLE( "EQ Switch", DSP_PROCESSING_ENABLE_2, 0, 0x01, 0),
+ SOC_SINGLE( "SWC Switch", DSP_PROCESSING_ENABLE_2, 1, 0x01, 0),
+ SOC_SINGLE( "DRC Switch", DSP_PROCESSING_ENABLE_2, 2, 0x01, 0),
+ SOC_SINGLE( "Stage Enhancer Switch", DSP_PROCESSING_ENABLE_2, 3, 0x01, 0),
+ SOC_SINGLE( "Loudness Switch", DSP_PROCESSING_ENABLE_2, 4, 0x01, 0),
+ SOC_SINGLE( "DSP Mono Out Switch", DSP_PROCESSING_ENABLE_2, 5, 0x01, 0),
+
+ //// Input
+
+ SOC_DOUBLE_R_TLV("Mic Pga Volume", ADC2_GAIN_LEFT, ADC2_GAIN_RIGHT, 0, 74, 0,adc_tlv),
+ SOC_SINGLE( "Right Microphone Switch", DSP_PROCESSING_ENABLE_1, 6, 0x01, 0),
+ SOC_SINGLE( "Inbound Noice Reduction Switch", DSP_PROCESSING_ENABLE_1, 5, 0x01, 0),
+ SOC_SINGLE( "Mic AGC Switch", DSP_PROCESSING_ENABLE_1, 4, 0x01, 0),
+ SOC_SINGLE( "Beam Forming Switch", DSP_PROCESSING_ENABLE_1, 3, 0x01, 0),
+ SOC_SINGLE( "Noise Reduction Switch", DSP_PROCESSING_ENABLE_1, 2, 0x01, 0),
+ SOC_SINGLE( "LEC Switch", DSP_PROCESSING_ENABLE_1, 1, 0x01, 0),
+ SOC_SINGLE( "AEC Switch", DSP_PROCESSING_ENABLE_1, 0, 0x01, 0),
+ SOC_ENUM_EXT("Master Playback Switch", output_select_enum[0], output_select_event_get, output_select_event_set),
+};
+
+// add non dapm controls
+static int cx2070x_add_controls(struct snd_soc_codec *codec)
+{
+ INFO("%lu: %s() called\n",jiffies,__func__);
+
+ return (snd_soc_add_controls(codec, cx2070x_snd_controls, ARRAY_SIZE(cx2070x_snd_controls)));
+}
+
+static int hpportpga_event(struct snd_soc_dapm_widget *w,
+struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned int old;
+ struct snd_soc_codec * codec = w->codec;
+ unsigned int on = 0x1;
+ old = cx2070x_read_reg_cache(codec,OUTPUT_CONTROL);
+ old &=~ on;
+ old &= 0xFF;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ cx2070x_write(codec,OUTPUT_CONTROL,old|on );
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cx2070x_write(codec,OUTPUT_CONTROL,old);
+ break;
+ }
+ return 0;
+
+}
+
+static int lineoutpga_event(struct snd_soc_dapm_widget *w,
+struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned int val;
+ struct snd_soc_codec * codec = w->codec;
+ unsigned int on = (unsigned int)1<<1;
+ val = cx2070x_read_reg_cache(codec,OUTPUT_CONTROL);
+ val &=~ on;
+ val &= 0xFF;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ cx2070x_write(codec,OUTPUT_CONTROL,val|on);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cx2070x_write(codec,OUTPUT_CONTROL,val);
+ break;
+ }
+ return 0;
+}
+static int clsdportpga_event(struct snd_soc_dapm_widget *w,
+struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned int val;
+ struct snd_soc_codec * codec = w->codec;
+ unsigned int on = (unsigned int)1<<2;
+ val = cx2070x_read_reg_cache(codec,OUTPUT_CONTROL);
+ val &=~ on;
+ val &= 0xFF;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ cx2070x_write(codec,OUTPUT_CONTROL,val|on);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cx2070x_write(codec,OUTPUT_CONTROL,val);
+ break;
+ }
+ return 0;
+}
+
+static int lineinpga_event(struct snd_soc_dapm_widget *w,
+struct snd_kcontrol *kcontrol, int event)
+{
+ //unsigned int val;
+ //struct snd_soc_codec * codec = w->codec;
+ //unsigned int on = (unsigned int)1<<2;
+ //val = cx2070x_read_reg_cache(codec,OUTPUT_CONTROL);
+ //val &=~ on;
+ //val &= 0xFF;
+ //switch (event) {
+ //case SND_SOC_DAPM_POST_PMU:
+ // cx2070x_write(codec,OUTPUT_CONTROL,val|on);
+ // break;
+ //case SND_SOC_DAPM_POST_PMD:
+ // cx2070x_write(codec,OUTPUT_CONTROL,val);
+ // break;
+ //}
+ return 0;
+}
+
+static int micportpga_event(struct snd_soc_dapm_widget *w,
+struct snd_kcontrol *kcontrol, int event)
+{
+ //unsigned int val;
+ //struct snd_soc_codec * codec = w->codec;
+ //unsigned int on = (unsigned int)1<<1;
+ //val = cx2070x_read_reg_cache(codec,OUTPUT_CONTROL);
+ //val &=~ on;
+ //val &= 0xFF;
+ //switch (event) {
+ //case SND_SOC_DAPM_POST_PMU:
+ // cx2070x_write(codec,OUTPUT_CONTROL,val|on);
+ // break;
+ //case SND_SOC_DAPM_POST_PMD:
+ // cx2070x_write(codec,OUTPUT_CONTROL,val);
+ // break;
+ //}
+ return 0;
+}
+
+static int aout_pga_event(struct snd_soc_dapm_widget *w,
+struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned int val;
+ struct snd_soc_codec * codec = w->codec;
+ unsigned int on = (unsigned int)1<<7; //steam 7
+ unsigned int reg = DSP_INIT;
+ val = cx2070x_read_reg_cache(codec,reg);
+ val &=~ on;
+ val &= 0xFF;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ cx2070x_write(codec,STREAMOP_ROUTING,0x60 ); // scal_out
+ cx2070x_write(codec,STREAM7_SOURCE,5 ); //Scale_out
+ cx2070x_write(codec,reg,val|on );
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cx2070x_write(codec,STREAM7_SOURCE,0 ); //disconnect
+ cx2070x_write(codec,reg,val);
+ break;
+ }
+ return 0;
+}
+
+static int dout_pga_event(struct snd_soc_dapm_widget *w,
+struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned int val;
+ struct snd_soc_codec * codec = w->codec;
+ unsigned int on = (unsigned int)1<<4;
+ unsigned int reg = DSP_INIT;
+ val = cx2070x_read_reg_cache(codec,reg);
+ val &=~ on;
+ val &= 0xFF;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ cx2070x_write(codec,STREAMOP_ROUTING,0x60 ); // scal_out
+ snd_soc_update_bits(codec, MIC_CONTROL, 3, 1);
+ //cx2070x_write(codec,STREAM6_ROUTING,5 ); // scal_out
+ cx2070x_write(codec,reg,val|on );
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cx2070x_write(codec,STREAMOP_ROUTING,0x62 ); // scal_out
+ cx2070x_write(codec,STREAM6_ROUTING,0 ); //disconnect
+ cx2070x_write(codec,reg,val);
+ break;
+ }
+ return 0;
+}
+
+static int ain_pga_event(struct snd_soc_dapm_widget *w,
+struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned int val;
+ struct snd_soc_codec * codec = w->codec;
+ unsigned int on = (unsigned int)1<<2; //steam 2
+ unsigned int reg = DSP_INIT;
+ val = cx2070x_read_reg_cache(codec,reg);
+ val &=~ on;
+ val &= 0xFF;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ //cx2070x_write(codec,VOICE_IN_SOURCE, 0x2 ); //stream 2 -> Voice In
+ cx2070x_write(codec,reg,val|on );
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cx2070x_write(codec,reg,val);
+ break;
+ }
+ return 0;
+}
+
+static int din_pga_event(struct snd_soc_dapm_widget *w,
+struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned int val;
+ struct snd_soc_codec * codec = w->codec;
+ unsigned int on = (unsigned int)1<<4; //stream 4
+ unsigned int reg = DSP_INIT;
+ val = cx2070x_read_reg_cache(codec,reg);
+ val &=~ on;
+ val &= 0xFF;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ cx2070x_write(codec,VOICE_IN_SOURCE, 0x4 ); //stream 4 -> Voice In
+ cx2070x_write(codec,reg,val|on );
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cx2070x_write(codec,reg,val);
+ break;
+ }
+ return 0;
+}
+
+static int adc_pga_event(struct snd_soc_dapm_widget *w,
+struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned int val;
+ struct snd_soc_codec * codec = w->codec;
+ unsigned int on = (unsigned int)1<<5; //stream 5
+ unsigned int reg = DSP_INIT;
+ val = cx2070x_read_reg_cache(codec,reg);
+ val &=~ on;
+ val &= 0xFF;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ cx2070x_write(codec,reg,val|on );
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ cx2070x_write(codec,reg,val);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cx2070x_dapm_widgets[]=
+{
+
+ //Playback
+ SND_SOC_DAPM_DAC( "DAC", "Playback", DSP_INIT,3,0), //stream 3
+
+ SND_SOC_DAPM_PGA_E("AOUT PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, aout_pga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("DOUT PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, dout_pga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("HP Port", SND_SOC_NOPM,
+ 0, 0, NULL, 0, hpportpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("CLASSD Port", SND_SOC_NOPM,
+ 0, 0, NULL, 0, clsdportpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("LINEOUT Port", SND_SOC_NOPM,
+ 0, 0, NULL, 0, lineoutpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ //Output Pin.
+ SND_SOC_DAPM_OUTPUT("SPK OUT"),
+ SND_SOC_DAPM_OUTPUT("LINE OUT"),
+ SND_SOC_DAPM_OUTPUT("HP OUT"),
+ SND_SOC_DAPM_OUTPUT("PCM OUT"),
+
+ //
+ // Captuer
+ //
+ SND_SOC_DAPM_ADC_E("ADC", "Capture", SND_SOC_NOPM,
+ 0, 0, adc_pga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+
+ SND_SOC_DAPM_PGA_E("AIN PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, ain_pga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("DIN PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, din_pga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("MIC Port", SND_SOC_NOPM,
+ 0, 0, NULL, 0, micportpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("LINEIN Port", SND_SOC_NOPM,
+ 0, 0, NULL, 0, lineinpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ //DECLARE_TLV_DB_SCALE
+
+ SND_SOC_DAPM_INPUT("MIC IN"),
+ SND_SOC_DAPM_INPUT("PCM IN"),
+ SND_SOC_DAPM_INPUT("LINE IN"),
+
+ SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
+ &input_select_controls),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias",MIC_CONTROL,3,0),
+#if 0
+ //machina layer.
+ SND_SOC_DAPM_MIC("INT MIC", NULL),
+ SND_SOC_DAPM_MIC("BT IN", NULL),
+ SND_SOC_DAPM_HP("BT OUT", NULL),
+ SND_SOC_DAPM_SPK("INT SPK", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+#endif
+};
+
+static const struct snd_soc_dapm_route cx2070x_routes[] =
+{
+ //playback.
+ { "AOUT PGA", NULL,"DAC" },
+ { "DOUT PGA", NULL,"DAC" },
+
+ { "HP Port", NULL,"AOUT PGA" },
+ { "LINEOUT Port", NULL,"AOUT PGA" },
+ { "CLASSD Port", NULL,"AOUT PGA" },
+
+ { "HP OUT", NULL,"HP Port" },
+ { "LINE OUT", NULL,"LINEOUT Port" },
+ { "SPK OUT", NULL,"CLASSD Port" },
+ { "PCM OUT", NULL,"DOUT PGA" },
+
+ //capture.
+ { "ADC", NULL,"Capture Source" },
+
+ { "Capture Source", "MIC","AIN PGA" },
+ { "Capture Source", "PCM","DIN PGA" },
+
+ { "AIN PGA", NULL,"MIC Port" },
+ // { "MIC Port", NULL,"Mic Bias" },
+ { "MIC Port", NULL,"MIC IN" },
+
+ // { "Mic Bias", NULL,"MIC IN" },
+ { "DIN PGA", NULL,"PCM IN" },
+#if 0
+ //machina layer.
+ { "Headphone", NULL,"HP OUT" },
+ { "INT SPK", NULL,"SPK OUT" },
+ { "BT OUT", NULL,"PCM OUT" },
+ { "MIC IN", NULL,"INT MIC" },
+ { "PCM IN", NULL,"BT IN" },
+#endif
+};
+
+static int cx2070x_add_widgets(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ INFO("%lu: %s() called\n",jiffies,__func__);
+
+ snd_soc_dapm_new_controls(dapm, cx2070x_dapm_widgets, ARRAY_SIZE(cx2070x_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, cx2070x_routes, ARRAY_SIZE(cx2070x_routes));
+ return 0;
+}
+
+static int cx2070x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int s5,s3,i2s,dsp;
+#define _3_3_f_f_5 1
+#define _1_1_7_7_0 0
+ int err =0;
+
+ ERROR("%lu: %s() called\n",jiffies,__func__);
+ ERROR("\tformat:%u speed:%u\n",params_format(params),params_rate(params));
+
+ switch(params_format(params))
+ {
+ case SNDRV_PCM_FORMAT_S16_LE: s5=STREAM5_SAMPLE_16_LIN; s3=STREAM5_SAMPLE_16_LIN; i2s=_3_3_f_f_5; break;
+ case SNDRV_PCM_FORMAT_S16_BE: s5=STREAM5_SAMPLE_16_LIN; s3=STREAM5_SAMPLE_16_LIN; i2s=_3_3_f_f_5; break;
+ case SNDRV_PCM_FORMAT_MU_LAW: s5=STREAM5_SAMPLE_U_LAW; s3=STREAM5_SAMPLE_U_LAW; i2s=_1_1_7_7_0; break;
+ case SNDRV_PCM_FORMAT_A_LAW: s5=STREAM5_SAMPLE_A_LAW; s3=STREAM5_SAMPLE_A_LAW; i2s=_1_1_7_7_0; break;
+ default:
+ printk(KERN_ERR "cx2070x: unsupported PCM format 0x%u\n",params_format(params));
+ return -EINVAL;
+ }
+
+ switch(params_rate(params))
+ {
+ case 8000: s5|= STREAM5_RATE_8000;
+ s3|=STREAM3_STREAM_STEREO|STREAM3_RATE_8000; break;
+ case 11025: s5|= STREAM5_RATE_11025;
+ s3|=STREAM3_STREAM_STEREO|STREAM3_RATE_11025; break;
+ case 16000: s5|= STREAM5_RATE_16000;
+ s3|=STREAM3_STREAM_STEREO|STREAM3_RATE_16000; break;
+ case 22050: s5|= STREAM5_RATE_22050;
+ s3|=STREAM3_STREAM_STEREO|STREAM3_RATE_22050; break;
+ case 24000: s5|= STREAM5_RATE_24000;
+ s3|=STREAM3_STREAM_STEREO|STREAM3_RATE_24000; break;
+ case 32000: s5|= STREAM5_RATE_32000;
+ s3|=STREAM3_STREAM_STEREO|STREAM3_RATE_32000; break;
+ case 44100: s5|= STREAM5_RATE_44100;
+ s3|=STREAM3_STREAM_STEREO|STREAM3_RATE_44100; break;
+ case 48000: s5|= STREAM5_RATE_48000;
+ s3|=STREAM3_STREAM_STEREO|STREAM3_RATE_48000; break;
+ case 88200: s5|= STREAM5_RATE_88200;
+ s3|=STREAM3_STREAM_STEREO|STREAM3_RATE_88200; break;
+ case 96000: s5|= STREAM5_RATE_96000;
+ s3|=STREAM3_STREAM_STEREO|STREAM3_RATE_96000; break;
+ default:
+ printk(KERN_ERR "cx2070x: unsupported rate %d\n",params_rate(params));
+ return -EINVAL;
+ }
+
+ cx2070x_real_write(codec,PORT1_TX_CLOCKS_PER_FRAME_PHASE,i2s?0x07:0x01);
+ cx2070x_real_write(codec,PORT1_RX_CLOCKS_PER_FRAME_PHASE,i2s?0x07:0x01);
+ cx2070x_real_write(codec,PORT1_TX_SYNC_WIDTH, i2s?0x0f:0x07);
+ cx2070x_real_write(codec,PORT1_RX_SYNC_WIDTH, i2s?0x0f:0x07);
+ cx2070x_real_write(codec,PORT1_CONTROL_2, i2s?0x05:0x00);
+ /*params for port2 by showy.zhang*/
+ cx2070x_real_write(codec,STREAM5_RATE,s5);
+ cx2070x_real_write(codec,STREAM3_RATE,s3);// cause by incorrect parameter
+
+ dsp=cx2070x_read_reg_cache(codec,DSP_INIT);
+
+ if ((err=cx2070x_dsp_init(codec,dsp|DSP_INIT_NEWC))<0)
+ return err;
+
+ return 0;
+}
+
+static int cx2070x_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ ERROR("%lu: %s(%d) called\n",jiffies,__func__,mute);
+
+ cx2070x_real_write(codec,VOLUME_MUTE,mute?VOLUME_MUTE_ALL:b_00000000);
+
+/*
+ if( mute)
+ {
+ cx2070x_real_write(codec,DSP_POWER,0xe0); // deep sleep mode
+ cx2070x_dsp_init(codec,DSP_INIT_STREAM_OFF);
+ }
+ else
+ {
+ cx2070x_dsp_init(codec,DSP_INIT_NEWC | 0xff);
+ }
+*/
+ return 0;
+}
+
+static int cx2070x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cx2070x_priv *channel = get_cx2070x_priv(codec);
+
+ INFO("%lu: %s() called\n",jiffies,__func__);
+
+ // sysclk is not used where, but store it anyway
+ channel->sysclk = freq;
+ return 0;
+}
+
+static int cx2070x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cx2070x_priv *channel = get_cx2070x_priv(codec);
+
+ INFO("%lu: %s() called\n",jiffies,__func__);
+
+ // set master/slave audio interface
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK)
+ {
+ case SND_SOC_DAIFMT_CBS_CFS: // This design only supports slave mode
+ channel->master = 0;
+ break;
+ default:
+ printk(KERN_ERR "unsupport DAI format, driver only supports slave mode\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK)
+ {
+ case SND_SOC_DAIFMT_NB_NF: // This design only supports normal bclk + frm
+ break;
+ default:
+ printk(KERN_ERR "unsupport DAI format, driver only supports normal bclk+ frm\n");
+ return -EINVAL;
+ }
+
+ // interface format
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK)
+ {
+ case SND_SOC_DAIFMT_I2S: // This design only supports I2S
+ break;
+ default:
+ printk( KERN_ERR "unspoort DAI format, driver only supports I2S interface.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct snd_soc_dai_ops cx2070x_dai_ops =
+{
+ .set_sysclk=cx2070x_set_dai_sysclk,
+ .set_fmt = cx2070x_set_dai_fmt,
+ .digital_mute=cx2070x_mute,
+ .hw_params = cx2070x_hw_params,
+};
+
+struct snd_soc_dai_driver soc_codec_cx2070x_dai =
+{
+ .name = "cx2070x-hifi",
+ .ops = &cx2070x_dai_ops,
+ .capture = {
+ .stream_name="Capture",
+ .formats = CX2070X_FORMATS,
+ .rates = CX2070X_RATES,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .playback= {
+ .stream_name="Playback",
+ .formats = CX2070X_FORMATS,
+ .rates = CX2070X_RATES,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(soc_codec_cx2070x_dai);
+
+static int cx2070x_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level)
+{
+ INFO("%lu: %s(,%d) called\n",jiffies,__func__,level);
+
+ switch (level)
+ {
+ // Fully on
+ case SND_SOC_BIAS_ON:
+ cx2070x_real_write(codec,DSP_POWER,0x20);
+ // all power is driven by DAPM system
+ break;
+
+ // Partial on
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ // Off, with power
+ case SND_SOC_BIAS_STANDBY:
+ //cx2070x_real_write(codec,VOLUME_MUTE,VOLUME_MUTE_ALL); // mute all
+ //cx2070x_real_write(codec,DSP_POWER,0xe0); // deep sleep mode
+ //cx2070x_dsp_init(codec,DSP_INIT_STREAM_OFF);
+
+ // TODO: power down channel
+ break;
+
+ // Off, without power
+ case SND_SOC_BIAS_OFF:
+ cx2070x_real_write(codec,VOLUME_MUTE,VOLUME_MUTE_ALL); // mute all
+ cx2070x_real_write(codec,DSP_POWER,0xe0); // deep sleep mode
+ cx2070x_dsp_init(codec,DSP_INIT_STREAM_OFF);
+ // TODO: put channel into deep-sleep
+ break;
+ }
+
+ return 0;
+}
+
+int I2cWrite( struct snd_soc_codec *codec, unsigned char ChipAddr, unsigned long cbBuf, unsigned char* pBuf)
+{
+#ifdef USING_SPI
+ struct spi_device * spi = (struct spi_device *) codec->control_data;
+ pBuf[0] |= 0x80; //SPI_WRITE
+ spi_write(spi,pBuf,cbBuf);
+ return true;
+#else //#ifdef ENABLE_SPI
+ struct i2c_client *client = (struct i2c_client *)codec->control_data;
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg[1];
+
+
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags & I2C_M_TEN;
+ msg[0].buf = pBuf;
+ msg[0].len = cbBuf;
+ if (i2c_transfer(adap,msg,1)!=1)
+ {
+ printk(KERN_ERR "cx2070x: I2cWriteThenRead failed.\n");
+
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+#endif //#!ENABLE_SPI
+}
+
+int I2cWriteThenRead( struct snd_soc_codec *codec, unsigned char ChipAddr, unsigned long cbBuf,
+ unsigned char* pBuf, unsigned long cbReadBuf, unsigned char*pReadBuf)
+{
+
+#ifdef USING_SPI
+ u8 reg[3];
+ struct spi_device * spi = (struct spi_device *) codec->control_data;
+ reg[0] = pBuf[0];
+ reg[1] = pBuf[1];
+ reg[2] = 0;
+ spi_write_then_read(spi, reg, 3, pReadBuf,cbReadBuf);
+ return true;
+#else //#ifdef USING_SPI
+ struct i2c_client *client = (struct i2c_client *)codec->control_data;
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg[2];
+
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags & I2C_M_TEN;
+ msg[0].len = cbBuf;
+ msg[0].buf = pBuf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+ msg[1].len = cbReadBuf;
+ msg[1].buf = pReadBuf;
+
+ if (i2c_transfer(adap,msg,2)!=2)
+ {
+ printk(KERN_ERR "cx2070x: I2cWriteThenRead failed.\n");
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+#endif //#!ENABLE_SPI
+}
+
+
+#if defined(CONFIG_SND_CX2070X_LOAD_FW)
+static int cx2070x_apply_firmware_patch(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ const struct firmware *fw = NULL;
+ const unsigned char *dsp_code = NULL;
+ struct device *dev = codec->dev;
+
+#if defined(CONFIG_SND_CX2070X_USE_FW_H)
+ // load firmware from c head file.
+ dsp_code = ChannelFW;
+#else
+ // load firmware from file.
+ ret = request_firmware(&fw, CX2070X_FIRMWARE_FILENAME, dev);
+ if (ret < 0) {
+ printk( KERN_ERR "%s(): Firmware %s not available %d",
+ __func__, CX2070X_FIRMWARE_FILENAME, ret);
+ return ret;
+ }
+ dsp_code = fw->data;
+#endif
+
+ ret = ApplyDSPChanges(dsp_code);
+ if (ret) {
+ printk(KERN_ERR "cx2070x: patch firmware failed, Error %d\n", ret);
+ } else {
+ printk(KERN_INFO "cx2070x: patch firmware successfully.\n");
+ }
+
+ return ret;
+}
+
+static int cx2070x_download_firmware(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ char *buf = NULL;
+ const struct firmware *fw = NULL;
+ const unsigned char *dsp_code = NULL;
+#if !defined(CONFIG_SND_CX2070X_USE_FW_H)
+ struct device *dev = codec->dev;
+#endif
+
+ // load firmware to memory.
+#if defined(CONFIG_SND_CX2070X_USE_FW_H)
+ // load firmware from c head file.
+ dsp_code = ChannelFW;
+#else
+ // load firmware from file.
+ ret = request_firmware(&fw, CX2070X_FIRMWARE_FILENAME,dev);
+ if( ret < 0)
+ {
+ printk( KERN_ERR "%s(): Firmware %s not available %d",__func__,CX2070X_FIRMWARE_FILENAME,ret);
+ goto LEAVE;
+ }
+ dsp_code = fw->data;
+#endif // #if defined(CONFIG_SND_CX2070X_USE_FW_H)
+ //
+ // Load rom data from a array.
+ //
+ buf = (char*)kzalloc(0x200,GFP_KERNEL);
+ if (buf == NULL)
+ {
+ printk(KERN_ERR "cx2070x: out of memory .\n");
+ ret = -ENOMEM;
+ goto LEAVE;
+ }
+
+ //
+ // Setup the i2c callback function.
+ //
+ SetupI2cWriteCallback( (void *) codec, (fun_I2cWrite) I2cWrite,32);
+ SetupI2cWriteThenReadCallback( (void *) codec, (fun_I2cWriteThenRead) I2cWriteThenRead);
+
+ // download
+ SetupMemoryBuffer(buf);
+
+ ret = DownloadFW(dsp_code);
+ if(ret)
+ {
+ printk(KERN_ERR "cx2070x: download firmware failed, Error %d\n",ret);
+ }
+ else
+ {
+ printk(KERN_INFO "cx2070x: download firmware successfully.\n");
+ }
+ if (buf)
+ {
+ kfree(buf);
+ }
+LEAVE:
+
+#if defined(CONFIG_SND_CX2070X_LOAD_FW) && !defined(CONFIG_SND_CX2070X_USE_FW_H)
+ if(fw)
+ {
+ release_firmware(fw);
+ }
+#endif
+ return ret;
+
+}
+#endif
+
+unsigned int cx2070x_hw_read( struct snd_soc_codec *codec, unsigned int regaddr)
+{
+ unsigned char data;
+ unsigned char chipaddr = 0;
+ unsigned char reg[2];
+#ifdef USING_I2C
+ struct i2c_client *client = (struct i2c_client *) codec->control_data;
+ chipaddr = client->addr;
+#endif
+ reg[0] = regaddr>>8;
+ reg[1] = regaddr&0xff;
+ I2cWriteThenRead(codec,chipaddr, 2, reg, 1,&data);
+ return (unsigned int)data;
+}
+
+unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
+
+//
+// Initialise the cx2070x driver
+// Register the mixer and dsp interfaces with the kernel
+//
+static int NOINLINE cx2070x_init(struct snd_soc_codec* codec)
+{
+ struct cx2070x_priv *cx2070x = get_cx2070x_priv(codec);
+ int n,vl,vh,vm,fh, fl,ret = 0;
+ cx2070x_reg_t *reg_cache;
+
+ printk(">>>>>>>%s",__func__);
+ INFO("%lu: %s() called\n",jiffies,__func__);
+
+ codec->control_data = cx2070x->control_data;
+
+
+#if defined(CONFIG_SND_CX2070X_GPIO_RESET)
+ cx2070x_reset_device();
+#endif
+
+#if CX2070X_TRISTATE_EEPROM
+ // If CX2070X has to float the pins to the NVRAM, enable this code
+ for(;;)
+ {
+ int pad,pbd;
+ pad=cx2070x_real_read(codec,PAD);
+ pbd=cx2070x_real_read(codec,PBD);
+ printk("%s(): PAD/PBD=%02x/%02x\n",__func__,pad,pbd);
+
+ _cx2070x_real_write(codec,PAD,pad&~(1<<4));
+ _cx2070x_real_write(codec,PBD,pbd&~(1<<6));
+ msleep(1000);
+ }
+#endif
+
+
+#if defined(CONFIG_SND_CX2070X_LOAD_FW)
+ ret = cx2070x_download_firmware(codec);
+ if( ret < 0)
+ {
+ printk(KERN_ERR "%s: failed to download firmware\n",__func__);
+ return ret;
+ }
+
+#endif
+ // Verify that Channel/Balboa is ready.
+ // May have to wait for ~5sec becore Channel/Balboa comes out of reset
+ for(n=5000;!bNoHW;)
+ {
+ int abcode=cx2070x_real_read(codec,ABORT_CODE);
+ // int abcode=cx2070x_real_read(codec,CHIP_VERSION);
+ printk(">>>>>>>>>>>>>>>%s abcode = %d",__func__, abcode);
+ if (abcode==0x01)
+ break; // initialization done!
+ if (--n==0)
+ {
+ printk(KERN_ERR "Timeout waiting for cx2070x to come out of reset!\n");
+ return -EIO;
+ }
+ msleep(1);
+ }
+
+ cx2070x_real_read(codec,FIRMWARE_VERSION);
+ cx2070x_real_read(codec,PATCH_VERSION);
+ cx2070x_real_read(codec,CHIP_VERSION);
+ cx2070x_real_read(codec,RELEASE_TYPE);
+
+ reg_cache = GET_REG_CACHE(codec);
+ fl=(reg_cache[FIRMWARE_VERSION]>>0)&0xFF;
+ fl=(fl>>4)*10+(fl&0xf);
+ fh=(reg_cache[FIRMWARE_VERSION]>>8)&0xFF;
+
+ // determine whether the codec is ROM version or not.
+ if( fh == 5)
+ { //firmware 5.x
+ //shows the firmware patch version.
+ cx2070x_real_read(codec,ROM_PATCH_VER_HB);
+ cx2070x_real_read(codec,ROM_PATCH_VER_MB);
+ cx2070x_real_read(codec,ROM_PATCH_VER_LB);
+ vh = reg_cache[ROM_PATCH_VER_HB];
+ vm = reg_cache[ROM_PATCH_VER_MB];
+ vl = reg_cache[ROM_PATCH_VER_LB];
+ printk("cx2070x: firmware version %u.%u, patch %u.%u.%u, chip CX2070%u (ROM)\n",fh,fl,vh,vm,vl,reg_cache[CHIP_VERSION]);
+ }
+ else if( fh == 4)
+ {
+ //firmware 4.x
+ printk("cx2070x: firmware version %u.%u, chip CX2070%u (RAM), ",fh,fl,reg_cache[CHIP_VERSION]);
+ // shows the firmware release type.
+ switch(reg_cache[RELEASE_TYPE])
+ {
+ case 12: printk("Custom Release\n"); break;
+ case 14: printk("Engineering Release\n"); break;
+ case 15: printk("Field Release\n"); break;
+ default: printk("Release %u?\n",reg_cache[RELEASE_TYPE]); break;
+ }
+ }
+ else
+ {
+ printk("cx2070x: Unsupported firmware version %u.%u!!!\n",fh,fl);
+ ret = -EINVAL;
+ goto card_err;
+ }
+
+
+ if (reg_cache[PATCH_VERSION])
+ {
+ vl=(reg_cache[PATCH_VERSION]>>0)&0xFF;
+ vh=(reg_cache[PATCH_VERSION]>>8)&0xFF;
+ printk("%s(): CX2070X patch version %u.%u\n",__func__,vh,vl);
+ }
+
+ // Initialize the CX2070X regisers and/or read them as needed.
+ for(n=0;n<noof(cx2070x_regs);n++)
+ switch(cx2070x_regs[n].type®_TYPE_MASK)
+ {
+ case REG_TYPE_RO:
+ case REG_TYPE_RW:
+ cx2070x_real_read(codec,n);
+ break;
+ case REG_TYPE_WI:
+ case REG_TYPE_WC:
+ cx2070x_real_write(codec,n,cx2070x_data[n]);
+ break;
+#if defined(REG_TYPE_DM)
+ case REG_TYPE_DM:
+ break;
+#endif
+ default:
+ snd_BUG();
+ }
+
+#if defined(CONFIG_SND_CX2070X_LOAD_FW)
+ cx2070x_apply_firmware_patch(codec);
+#endif
+
+ cx2070x_add_controls(codec);
+ cx2070x_add_widgets(codec);
+
+ //snd_soc_dapm_nc_pin(&codec->dapm, "LINE IN");
+ //snd_soc_dapm_nc_pin( &codec->dapm, "LINE OUT");
+ //snd_soc_dapm_enable_pin( &codec->dapm, "INT MIC");
+ //snd_soc_dapm_enable_pin( &codec->dapm, "INT SPK");
+ //snd_soc_dapm_disable_pin( &codec->dapm, "BT IN");
+ //snd_soc_dapm_enable_pin( &codec->dapm, "Headphone");
+ //snd_soc_dapm_disable_pin( &codec->dapm, "BT OUT");
+
+
+#if defined(CONFIG_SND_CX2070X_GPIO_JACKSENSE)
+ /* Headset jack detection */
+ ret = snd_soc_jack_new(codec, "Headset Jack",
+ SND_JACK_HEADSET, &hs_jack);
+ if (ret)
+ {
+ printk(KERN_ERR "CX2070X: failed to register Headset Jack\n");
+ goto card_err;
+ }
+
+ ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+ if (ret)
+ {
+ printk(KERN_ERR "CX2070X: failed to add jack gpios.\n");
+ goto card_err;
+ }
+
+ ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
+ hs_jack_pins);
+ if (ret)
+ {
+ printk(KERN_ERR "CX2070X: failed to add soc jack pin\n");
+ goto card_err;
+ }
+#else
+ snd_soc_dapm_sync( &codec->dapm);
+#endif //#if defined(CONFIG_SND_CX2070X_GPIO_JACKSENSE)
+
+#if defined(CONFIG_SND_CXLIFEGUARD)
+ cxdbg_dev_init(codec);
+#endif
+ if( ret == 0)
+ {
+ printk(KERN_INFO "CX2070X: codec is ready.\n");
+ }
+ return ret;
+
+card_err:
+ return ret;
+}
+
+static int cx2070x_hw_reset(void)
+{
+ int err;
+ /* Reset */
+ err = gpio_request(CODEC_RESET_GPIO_PIN, "nCX2070X_Reset");
+ printk(KERN_ERR "channel reset gpio=%d\n", CODEC_RESET_GPIO_PIN);
+ if (err)
+ printk(KERN_ERR "#### failed to request GPH3_2 for Audio Reset\n");
+
+ gpio_direction_output(CODEC_RESET_GPIO_PIN, 0);
+ msleep(150);
+ gpio_direction_output(CODEC_RESET_GPIO_PIN, 1);
+ gpio_free(CODEC_RESET_GPIO_PIN);
+ msleep(150);
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+static struct snd_soc_codec *g_cx2070x_codec;
+
+static int cx2070x_dbg_show_regs(struct seq_file *s, void *unused)
+{
+
+ int reg_no = (int)s->private;
+ int i = 0;
+
+ if (reg_no == noof(cx2070x_regs)) {
+ seq_printf(s, "Offset\tType\tValue\tName\n");
+ for (i = 0; i < reg_no; i++) {
+ seq_printf(s, "0x%02X\t", cx2070x_regs[i].addr);
+ switch (cx2070x_regs[i].type) {
+ case REG_TYPE_RO:
+ seq_printf(s, "R");
+ break;
+ case REG_TYPE_RW:
+ seq_printf(s, "RW");
+ break;
+ case REG_TYPE_WC:
+ seq_printf(s, "WC");
+ break;
+ case REG_TYPE_WI:
+ seq_printf(s, "WI");
+ break;
+ default:
+ seq_printf(s, "UNKNOWN\t");
+ }
+ seq_printf(s, "\t0x%02X\t%s\n", cx2070x_read_reg_cache(g_cx2070x_codec, i),
+ cx2070x_regs[i].name);
+ }
+ return 0;
+ }
+
+ seq_printf(s, "Offset:\t0x%02X\n", cx2070x_regs[reg_no].addr);
+
+ seq_printf(s, "Type:\t");
+ switch (cx2070x_regs[reg_no].type) {
+ case REG_TYPE_RO:
+ seq_printf(s, "R");
+ break;
+ case REG_TYPE_RW:
+ seq_printf(s, "RW");
+ break;
+ case REG_TYPE_WC:
+ seq_printf(s, "WC");
+ break;
+ case REG_TYPE_WI:
+ seq_printf(s, "WI");
+ break;
+ default:
+ seq_printf(s, "UNKNOWN");
+ }
+ seq_printf(s, "\n");
+
+ seq_printf(s, "Value:\t0x%02X\n", cx2070x_read_reg_cache(g_cx2070x_codec, reg_no));
+
+ return 0;
+}
+
+static int cx2070x_dbg_reg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cx2070x_dbg_show_regs, inode->i_private);
+}
+
+static ssize_t cx2070x_dbg_reg_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ char buf[8];
+ unsigned long val;
+ int reg_no = (int)seq->private;
+ int ret = 0;
+
+ if (count >= sizeof(buf)) {
+ ERROR("%s, The buffer is not enough.\n", __func__);
+ return -EINVAL;
+ } if (copy_from_user(buf, ubuf, count)) {
+ ERROR("%s, Faied to copy data from user space.\n", __func__);
+ return -EFAULT;
+ }
+
+ buf[count] = 0;
+
+ ret = strict_strtoul(buf, 16, &val);
+ if (ret < 0) {
+ ERROR("%s, Failed to convert a string to an unsinged long integer.\n", __func__);
+ return ret;
+ }
+
+ switch (cx2070x_regs[reg_no].type) {
+ case REG_TYPE_RO:
+ ERROR("%s, A read-only register 0x%02x cannot be written.\n",
+ __func__, cx2070x_regs[reg_no].addr);
+ return -EINVAL;
+ case REG_TYPE_WI:
+ case REG_TYPE_WC:
+ case REG_TYPE_RW:
+ ret = cx2070x_write(g_cx2070x_codec, reg_no, (u8)val);
+ if (ret) {
+ ERROR("%s, Failed to write register 0x%02x.\n", __func__,
+ cx2070x_regs[reg_no].addr);
+ return ret;
+ }
+ break;
+ default:
+ ERROR("%s, Unknown type register\n", __func__);
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct file_operations cx2070x_debug_reg_fops = {
+ .open = cx2070x_dbg_reg_open,
+ .read = seq_read,
+ .write = cx2070x_dbg_reg_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+static int cx2070x_probe(struct snd_soc_codec *codec)
+{
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *d, *regs;
+ int n = 0;
+#endif
+
+ INFO("%lu: %s() called\n",jiffies,__func__);
+ printk(KERN_INFO "cx2070x codec driver version: %02x,%02x,%02x,%02x\n",(u8)((CX2070X_DRIVER_VERSION)>>24),
+ (u8)((CX2070X_DRIVER_VERSION)>>16),
+ (u8)((CX2070X_DRIVER_VERSION)>>8),
+ (u8)((CX2070X_DRIVER_VERSION)));
+
+#ifdef CONFIG_DEBUG_FS
+ g_cx2070x_codec = codec;
+ d = debugfs_create_dir("cx2070x", NULL);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+
+ regs = debugfs_create_dir("regs", d);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ for (n = 0; n < noof(cx2070x_regs); n++)
+ debugfs_create_file(cx2070x_regs[n].name, 0644, regs, (void *)n,
+ &cx2070x_debug_reg_fops);
+
+ debugfs_create_file("ALL", 0644, regs, (void *)n,
+ &cx2070x_debug_reg_fops);
+#endif
+
+ return cx2070x_init(codec);
+}
+
+static int cx2070x_remove(struct snd_soc_codec *codec)
+{
+ INFO("%lu: %s() called\n",jiffies,__func__);
+ // power down chip
+ cx2070x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+#if defined (CONFIG_SND_CX2070X_GPIO_JACKSENSE)
+ snd_soc_jack_free_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+#endif//#if defined (CONFIG_SND_CX2070X_GPIO_JACKSENSE)
+ return 0;
+}
+
+static int cx2070x_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ INFO("%lu: %s() called\n",jiffies,__func__);
+ cx2070x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int cx2070x_resume(struct snd_soc_codec *codec)
+{
+ int n;
+ INFO("%lu: %s() called\n",jiffies,__func__);
+
+ // Sync reg_cache with the hardware
+ for(n=0;n<noof(cx2070x_regs);n++)
+ switch(cx2070x_regs[n].type®_TYPE_MASK)
+ {
+ case REG_TYPE_RO:
+ break;
+ case REG_TYPE_RW:
+ case REG_TYPE_WI:
+ case REG_TYPE_WC:
+ cx2070x_real_write(codec,n,cx2070x_read_reg_cache(codec,n));
+ break;
+#if defined(REG_TYPE_DM)
+ case REG_TYPE_DM:
+ break;
+#endif
+ default:
+ snd_BUG();
+ }
+ cx2070x_dsp_init(codec,cx2070x_read_reg_cache(codec,DSP_INIT)|DSP_INIT_NEWC);
+ cx2070x_set_bias_level(codec, SND_SOC_BIAS_ON);
+ return 0;
+}
+
+static inline unsigned int cx2070x_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ return 0;
+}
+
+struct snd_soc_codec_driver soc_codec_dev_cx2070x=
+{
+ .probe =cx2070x_probe,
+ .remove = cx2070x_remove,
+ .suspend = cx2070x_suspend,
+ .resume = cx2070x_resume,
+ .read = cx2070x_read,
+ .write = cx2070x_write,
+ .reg_cache_size = sizeof(cx2070x_data),
+ .reg_cache_step = 1,
+ .reg_word_size = sizeof(u8),
+ .reg_cache_default = cx2070x_data,
+ .set_bias_level = cx2070x_set_bias_level,
+
+};
+
+#if defined(USING_I2C)
+static int cx2070x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+ struct cx2070x_priv *cx2070x;
+ int ret = 0;
+
+ INFO("%lu: %s() called\n", jiffies, __func__);
+
+ cx2070x = (struct cx2070x_priv*)kzalloc(sizeof(struct cx2070x_priv), GFP_KERNEL);
+ if (cx2070x == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(i2c, cx2070x);
+
+ cx2070x->control_data = (void*)i2c;
+ cx2070x->control_type = SND_SOC_I2C;
+
+ cx2070x->input_sel = Cx_INPUT_SEL_BY_GPIO;
+ cx2070x->output_sel = Cx_OUTPUT_SEL_BY_GPIO;
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_cx2070x, &soc_codec_cx2070x_dai, 1);
+ printk(">>>>>>>%s ret = %d ",__func__,ret);
+
+ if (ret < 0)
+ INFO("%s() failed ret = %d\n", __func__, ret);
+ return ret;
+}
+
+static int cx2070x_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+#if defined(CONFIG_SND_CXLIFEGUARD)
+ cxdbg_dev_exit();
+#endif
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct i2c_device_id cx2070x_i2c_id[] =
+{
+ { "cx2070x", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, cx2070x_i2c_id);
+
+static struct i2c_driver cx2070x_i2c_driver = {
+ .driver = {
+ .name = "cx2070x",
+ .owner = THIS_MODULE,
+ },
+ .probe=cx2070x_i2c_probe,
+ .remove=__devexit_p(cx2070x_i2c_remove),
+ .id_table=cx2070x_i2c_id,
+};
+
+#elif defined(USING_SPI)
+static int cx2070x_spi_probe(struct spi_device *spi)
+{
+ INFO("%lu: %s() called\n", jiffies, __func__);
+
+ struct cx2070x_priv *cx2070x;
+ int ret = 0;
+
+ //printk(KERN_INFO "Channel Audio Codec %08x\n", CX2070X_DRIVER_VERSION);
+
+ cx2070x = (struct cx2070x_priv *)kzalloc(sizeof(struct cx2070x_priv), GFP_KERNEL);
+ if (cx2070x == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ spi_set_drvdata(spi, cx2070x);
+
+ cx2070x->control_data = (void*)spi;
+ cx2070x->control_type = SND_SOC_SPI;
+
+ cx2070x->input_sel = Cx_INPUT_SEL_BY_GPIO;
+ cx2070x->output_sel = Cx_OUTPUT_SEL_BY_GPIO;
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_cx2070x, &soc_codec_cx2070x_dai, 1);
+
+ if (ret < 0)
+ INFO("%s() failed ret = %d\n", __func__, ret);
+ return ret;
+}
+
+static int cx2070x_spi_remove(struct spi_device *client)
+{
+ struct snd_soc_codec *codec = (struct snd_soc_codec *)spi_get_drvdata(client);
+
+ kfree(codec->reg_cache);
+ return 0;
+}
+
+static const struct spi_device_id cx2070x_spi_id[]={
+ { CX2070X_SPI_DRIVER_NAME,NULL},
+};
+
+static struct spi_driver cx2070x_spi_driver = {
+ .driver = {
+ .name = "cx2070x-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = cx2070x_spi_probe,
+ .remove = __devexit_p(cx2070x_spi_remove),
+};
+#endif
+
+static int __init cx2070x_modinit(void)
+{
+ int ret;
+ INFO("%lu: %s() called\n",jiffies,__func__);
+#if defined(USING_I2C)
+ ret = i2c_add_driver(&cx2070x_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register CX2070X I2C driver: %d\n",
+ ret);
+ }
+#elif defined(USING_SPI)
+ ret = spi_register_driver(&cx2070x_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register CX2070X SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return ret;
+}
+module_init(cx2070x_modinit);
+
+static void __exit cx2070x_exit(void)
+{
+ INFO("%lu: %s() called\n",jiffies,__func__);
+#if defined(USING_I2C)
+ i2c_del_driver(&cx2070x_i2c_driver);
+#elif defined(USING_SPI)
+ spi_unregister_driver(&cx2070x_spi_driver);
+#endif
+}
+module_exit(cx2070x_exit);
+
+int CX_AUDDRV_VERSION = CX2070X_DRIVER_VERSION;
+EXPORT_SYMBOL_GPL(CX_AUDDRV_VERSION);
+EXPORT_SYMBOL_GPL(soc_codec_dev_cx2070x);
+MODULE_DESCRIPTION("ASoC cx2070x Codec Driver");
+MODULE_LICENSE("GPL");
+
--- /dev/null
+/*
+ * ALSA SoC CX2070X Channel codec driver
+ *
+ * Copyright: (C) 2009/2010 Conexant Systems
+ *
+ * Based on sound/soc/codecs/tlv320aic2x.c by Vladimir Barinov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _CX2070X_H
+#define _CX2070X_H
+
+
+#define CONFIG_SND_DIGICOLOR_SOC_CHANNEL_VER_4_30F 1
+
+#ifdef CONFIG_SND_SOC_CNXT_FW_UPDATE
+#define CONFIG_SND_CX2070X_LOAD_FW 1
+#endif
+//#define CONFIG_SND_CX2070X_USE_FW_H 1
+//#define CONFIG_CNXT_USING_SPI_BUS 1
+
+#ifdef CONFIG_SND_SOC_CNXT_JACKSENSE
+#define CONFIG_SND_CX2070X_GPIO_JACKSENSE 1
+#endif
+//#define CONFIG_SND_CX2070X_GPIO_RESET 1
+#define CONFIG_SND_CXLIFEGUARD 1
+//#define CONFIG_CXNT_SOFTWOARE_SIMULATION 1
+#define DBG_MONITOR_REG 1
+
+//#define GPIO_HP_JACKSENSE 178 //Tegra 250
+//.#define JACK_SENSE_GPIO_PIN 178 // Tegra
+//#define CODEC_RESET_GPIO_PIN 184 // Tegra
+
+#define JACK_SENSE_GPIO_PIN 151 //s5pc110 GPH2_5
+#define CODEC_RESET_GPIO_PIN 157 //s5pc110 reset pin.
+#define FOR_MID 0
+
+#if (defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) ) && !defined(CONFIG_CNXT_USING_SPI_BUS)
+#define USING_I2C 1
+#endif
+
+#if defined(CONFIG_SPI_MASTER) && defined(CONFIG_CNXT_USING_SPI_BUS)
+#define USING_SPI 1
+#endif
+
+
+enum Cx_INPUT_SEL{
+ Cx_INPUT_SEL_BY_GPIO = 0,
+ Cx_INPUT_SEL_MIC,
+ Cx_INPUT_SEL_LINE,
+ Cx_INPUT_SEL_DPORT2,
+};
+
+enum Cx_OUTPUT_SEL{
+ Cx_OUTPUT_SEL_BY_GPIO = 0,
+ Cx_OUTPUT_SEL_SPK,
+ Cx_OUTPUT_SEL_LINE,
+ Cx_OUTPUT_SEL_HP,
+ Cx_OUTPUT_SEL_DPORT2,
+};
+
+#define CX2070X_I2C_DRIVER_NAME "cx2070x-i2c"
+#define CX2070X_SPI_DRIVER_NAME "cx2070x-spi"
+#define CX2070X_FIRMWARE_FILENAME "cnxt/cx2070x.fw"
+#define AUDDRV_VERSION(major0,major1, minor, build ) ((major0)<<24|(major1)<<16| (minor)<<8 |(build))
+
+#endif
--- /dev/null
+/*
+* ALSA SoC CX20709 Channel codec driver
+*
+* Copyright: (C) 2009/2010 Conexant Systems
+*
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+*
+*
+*************************************************************************
+* Modified Date: 12/01/11
+* File Version: 2.26.35.11
+*************************************************************************
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/module.h> /* Specifically, a module */
+#include <linux/fs.h>
+#include <asm/uaccess.h> /* for get_user and put_user */
+//#include <sound/core.h>
+//#include <sound/pcm.h>
+//#include <sound/pcm_params.h>
+#include <sound/soc.h>
+//#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+//#define DEBUG 1
+
+#include "cxdebug.h"
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#include <linux/i2c.h>
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+#include <linux/spi/spi.h>
+#endif
+
+
+/*
+*
+ * Is the device open right now? Used to prevent
+ * concurent access into the same device
+ */
+static int Device_Open = 0;
+extern int CX_AUDDRV_VERSION;
+struct snd_soc_codec *g_codec = NULL;
+
+/*
+ * This is called whenever a process attempts to open the device file
+ */
+static int cxdbg_dev_open(struct inode *inode, struct file *file)
+{
+#ifdef DEBUG
+ printk(KERN_INFO "cxdbg_dev: device_open(%p)\n", file);
+#endif
+
+ /*
+ * We don't want to talk to two processes at the same time
+ */
+ if (Device_Open)
+ return -EBUSY;
+
+ Device_Open++;
+ /*
+ * Initialize the message
+ */
+
+ // try_module_get(THIS_MODULE);
+ return 0;
+}
+
+/*
+ * This is called whenever a process attempts to open the device file
+ */
+static int cxdbg_dev_release(struct inode *inode, struct file *file)
+{
+#ifdef DEBUG
+ printk(KERN_INFO "cxdbg_dev: device_release(%p)\n", file);
+#endif
+
+
+ Device_Open--;
+ /*
+ * Initialize the message
+ */
+
+ return 0;
+}
+static int codec_reg_write(struct CXDBG_IODATA *reg)
+{
+ int errno = 0;
+
+ BUG_ON(!g_codec);
+ BUG_ON(!g_codec->hw_write);
+
+ if(g_codec&& g_codec->hw_write)
+ {
+ if( g_codec->hw_write(g_codec,(char*)reg->data,reg->len)
+ !=reg->len)
+ {
+ errno =-EIO;
+ printk(KERN_ERR "cxdbg_dev: codec_reg_write failed\n");
+ }
+ }
+ else
+ {
+ errno = -EBUSY;
+ printk(KERN_ERR "cxdbg_dev: codec_reg_write failed, device is not ready.\n");
+ }
+ return errno;
+}
+
+static unsigned int codec_reg_read(struct CXDBG_IODATA *reg)
+{
+ int errno = 0;
+ unsigned int regaddr;
+ unsigned int data;
+
+
+ BUG_ON(!g_codec);
+ BUG_ON(!g_codec->hw_read);
+
+ if (reg-> len == 2)
+ {
+ regaddr = (((unsigned int)reg->data[0])<<8) + reg->data[1];
+ }
+ else if (reg->len == 1)
+ {
+ regaddr = (unsigned int)reg->data[0];
+ }
+ else
+ {
+ printk(KERN_ERR "cxdbg_dev: codec_reg_read failed, invalid parameter.\n");
+ return -EINVAL;
+ }
+ memset(reg,0,sizeof(*reg));
+ if(g_codec && g_codec->hw_read)
+ {
+ data = g_codec->hw_read(g_codec,regaddr);
+ reg->data[0] = data & 0xFF;
+ reg->len = 1;
+ }
+ else
+ {
+ errno = -EBUSY;
+ printk(KERN_ERR "cxdbg_dev: codec_reg_read failed, device is not ready.\n");
+ }
+ return errno;
+ return 0;
+}
+
+long cxdbg_dev_ioctl(struct file * file, unsigned int cmd, unsigned long arg)
+{
+ struct CXDBG_IODATA *reg=NULL ;
+ int __user *ip = (int __user*) arg;
+ long err = -1;
+#ifdef DEBUG
+ printk(KERN_INFO "cxdbg_dev: ioctl, cmd=0x%02x, arg=0x%02lx\n", cmd, arg);
+#endif
+
+ /*
+ * Switch according to the ioctl called
+ */
+ switch (cmd) {
+ case CXDBG_IOCTL_REG_SET:
+ reg = (struct CXDBG_IODATA*) kmalloc(sizeof(*reg),GFP_KERNEL);
+ err = copy_from_user((char*) reg, (char*)arg,sizeof(*reg));
+ if(err==0)
+ {
+ codec_reg_write(reg);
+ }
+ break;
+
+ case CXDBG_IOCTL_REG_GET:
+ reg = (struct CXDBG_IODATA*) kmalloc(sizeof(*reg),GFP_KERNEL);
+ err =copy_from_user((char*) reg, (char*)arg,sizeof(*reg));
+ if( err == 0)
+ {
+ codec_reg_read(reg);
+ err = copy_to_user((char*) arg, (char*)reg,sizeof(*reg));
+ }
+ break;
+ case CXDBG_IOCTL_PDRIVER_VERSION:
+ err = put_user(CX_AUDDRV_VERSION,ip);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if(reg)
+ {
+ kfree(reg);
+ }
+
+ return err;
+}
+
+
+#if defined(_MSC_VER)
+static const struct file_operations cxdbg_dev_fops =
+{
+ /*.owner = */THIS_MODULE,
+ /*.llseek*/NULL,
+ /*.read = */NULL,
+ /*.write*/ NULL,
+ /*.aio_read*/ NULL,
+ /*.aio_write*/NULL,
+ /*readdir*/NULL,
+ /*.poll*/NULL,
+ /*ioctl*/ NULL /*i2cdev_ioctl*/,
+ /*.unlocked_ioctl*/cxdbg_dev_ioctl,
+ /*.compat_ioctl*/NULL,
+ /*.mmap*/NULL,
+ /*.open*/cxdbg_dev_open,
+ /*.flush*/NULL,
+ /*.release*/NULL,
+ /*.fsync*/NULL,
+ /*.aio_fsync*/NULL,
+ /*.fasync*/NULL,
+ /*.lock*/NULL,
+ /*.sendpage*/NULL,
+};
+#else
+static const struct file_operations cxdbg_dev_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = cxdbg_dev_ioctl,
+ .open = cxdbg_dev_open,
+ .release = cxdbg_dev_release,
+};
+#endif
+
+
+/*
+* Initialize the module - Register the character device
+*/
+int cxdbg_dev_init(struct snd_soc_codec *socdev)
+{
+ int err;
+ printk(KERN_INFO "cxdbg_dev: entries driver\n");
+
+ g_codec = socdev;
+
+ err = register_chrdev(CXDBG_MAJOR, CXDBG_DEVICE_NAME, &cxdbg_dev_fops);
+ if (err)
+ {
+ printk(KERN_ERR "cxdbg_dev: Driver Initialisation failed\n");
+ }
+ return err;
+}
+
+void cxdbg_dev_exit(void)
+{
+ unregister_chrdev(CXDBG_MAJOR,CXDBG_DEVICE_NAME);
+}
+
+
+MODULE_AUTHOR("Simon Ho<simon.ho@conexant.com");
+MODULE_DESCRIPTION("debug driver");
+MODULE_LICENSE("GPL");
+
--- /dev/null
+/*
+* ALSA SoC CX20709 Channel codec driver
+*
+* Copyright: (C) 2009/2010 Conexant Systems
+*
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+*
+*
+*************************************************************************
+* Modified Date: 11/02/11
+* File Version: 2.26.35.11
+*************************************************************************
+*/
+#ifndef CXDEBUG_H
+#define CXDEBUG_H
+
+#define CXDBG_MAJOR 168 /* Device major number */
+
+
+/* Use 'k' as magic number */
+#define CXDBG_IOC_MAGIC 'S'
+
+#define MAX_DATA_LEN 64
+struct CXDBG_IODATA{
+ unsigned short len;
+ unsigned char data[MAX_DATA_LEN];
+};
+
+#define CXDBG_IOCTL_REG_SET _IOWR(CXDBG_IOC_MAGIC, 1, struct CXDBG_IODATA)
+#define CXDBG_IOCTL_REG_GET _IOWR(CXDBG_IOC_MAGIC, 2, struct CXDBG_IODATA)
+#define CXDBG_IOCTL_PDRIVER_VERSION _IOR( CXDBG_IOC_MAGIC, 3, int)
+
+#define CXDBG_DEVICE_NAME "cxdbg"
+
+#ifdef __KERNEL__
+int cxdbg_dev_init(struct snd_soc_codec * codec);
+void cxdbg_dev_exit(void);
+#endif
+
+#endif //#ifndef CXDEBUG_H
+
+
--- /dev/null
+/*\r
+ * ALSA SoC CX20709 Channel codec driver\r
+ *\r
+ * Copyright: (C) 2009/2010 Conexant Systems\r
+ *\r
+ * \r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2 as\r
+ * published by the Free Software Foundation.\r
+ *\r
+ * This code is to download the firmware to CX2070x device. \r
+ * \r
+ *************************************************************************\r
+ * Modified Date: 01/24/11\r
+ * File Version: 1.0.0.1\r
+ *************************************************************************\r
+ */\r
+#if defined(_MSC_VER) \r
+// microsoft windows environment.\r
+#define __BYTE_ORDER __LITTLE_ENDIAN\r
+#define __LITTLE_ENDIAN 1234\r
+#include <stdlib.h> // For _MAX_PATH definition\r
+#include <stdio.h>\r
+#include <string.h>\r
+#define msleep(_x_) \r
+int printk(const char *s, ...);\r
+#define KERN_ERR "<3>"\r
+#elif defined(__KERNEL__) \r
+// linux kernel environment.\r
+#include <linux/module.h>\r
+#include <linux/moduleparam.h>\r
+#include <linux/init.h>\r
+#include <linux/delay.h>\r
+#else\r
+//\r
+// linux user mode environment.\r
+//\r
+#include <stdlib.h> // For _MAX_PATH definition\r
+#include <stdio.h>\r
+#endif\r
+#include "cxpump.h"\r
+\r
+#if defined( __BIG_ENDIAN) && !defined(__BYTE_ORDER)\r
+#define __BYTE_ORDER __BIG_ENDIAN\r
+#elif defined( __LITTLE_ENDIAN ) && !defined(__BYTE_ORDER)\r
+#define __BYTE_ORDER __LITTLE_ENDIAN\r
+#endif \r
+\r
+\r
+#ifndef __BYTE_ORDER\r
+#error __BYTE_ORDER is undefined.\r
+#endif \r
+\r
+#define ENABLE_I2C_BURST_MODE\r
+\r
+#if defined(_MSC_VER) \r
+#pragma warning(disable:4127 4706 4101) // conditional experssion is constant\r
+typedef enum I2C_STATE{I2C_OK,I2C_ERR,I2C_RETRY} ;\r
+void ShowProgress(int curPos,bool bForceRedraw, I2C_STATE eState, const int MaxPos);\r
+void InitShowProgress(const int MaxPos);\r
+#elif defined(__KERNEL__) \r
+//linux kernel mode\r
+#define ShowProgress(curPos,bForceRedraw,eState, axPos)\r
+#define InitShowProgress(MaxPos)\r
+#else \r
+//linux user mode\r
+#define InitShowProgress(MaxPos)\r
+#define ShowProgress(curPos,bForceRedraw,eState, axPos)\r
+#endif\r
+\r
+\r
+#ifndef NULL\r
+#define NULL 0\r
+#endif //#ifndef NULL\r
+\r
+#define S_DESC "Cnxt Channel Firmware" /*Specify the string that will show on head of rom file*/\r
+#define S_ROM_FILE_NAME "cx2070x.fw" /*Specify the file name of rom file*/\r
+#define CHIP_ADDR 0x14 /*Specify the i2c chip address*/\r
+#define MEMORY_UPDATE_TIMEOUT 300\r
+#define MAX_ROM_SIZE (1024*1024)\r
+//#define DBG_ERROR "ERROR : "\r
+#define DBG_ERROR KERN_ERR\r
+#define LOG( _msg_ ) printk _msg_ \r
+//#define LOG( _msg_ ) ;\r
+\r
+typedef struct CX_CODEC_ROM_DATA\r
+{\r
+#ifdef USE_TYPE_DEFINE\r
+ unsigned long Type;\r
+#endif //#ifdef USE_TYPE_DEFINE\r
+ unsigned long Length;\r
+ unsigned long Address;\r
+ unsigned char data[1];\r
+}CX_CODEC_ROM_DATA;\r
+\r
+#define ROM_DATA_TYPE_S37 0xAA55CC01 // S37 format.\r
+#define ROM_DATA_TYPE_CNXT 0xAA55CC04 // Conexant SPI format.\r
+#define ROM_DATA_SEPARATED_LINE 0x23232323 //()()()()\r
+\r
+typedef struct CX_CODEC_ROM{\r
+ char sDesc[24]; \r
+ char cOpenBracket;\r
+ char sVersion[5];\r
+ char cCloseBracket;\r
+ char cEOF;\r
+ unsigned long FileSize;\r
+ unsigned long LoaderAddr;\r
+ unsigned long LoaderLen;\r
+ unsigned long CtlAddr;\r
+ unsigned long CtlLen;\r
+ unsigned long SpxAddr;\r
+ unsigned long SpxLen;\r
+ struct CX_CODEC_ROM_DATA Data[1];\r
+}CX_CODEC_ROM;\r
+\r
+typedef struct CX_CODEC_APPENDED_DATA\r
+{\r
+ unsigned char Address[2]; // The address of data.\r
+ unsigned char padding; // The actual firmware data.\r
+ unsigned char data; // The actual firmware data.\r
+}CX_CODEC_APPENDED_DATA;\r
+\r
+typedef struct CX_CODEC_ROM_APPENDED{\r
+ unsigned long TuningAddr;\r
+ unsigned long TuningLen;\r
+ CX_CODEC_APPENDED_DATA data[1]; // following by Jira id and time.\r
+}CX_CODEC_ROM_APPENDED;\r
+\r
+typedef struct CX_CODEC_ROM_APPENDED_INFO{\r
+ char sJIRAID[16];\r
+ char sTime[16];\r
+}CX_CODEC_ROM_APPENDED_INFO;\r
+\r
+\r
+// To convert two digital ASCII into one BYTE.\r
+unsigned char ASCII_2_BYTE( char ah, char al) ;\r
+\r
+#define BUF_SIZE 0x1000\r
+#define BIBF(_x_) if(!(_x_)) break;\r
+#define BIF(_x_) if((ErrNo=(_x_)) !=0) break;\r
+\r
+\r
+#ifndef BIBF\r
+#define BIBF( _x_ ) if(!(_x_)) break;\r
+#endif \r
+\r
+enum { \r
+ MEM_TYPE_RAM = 1 /* CTL*/, \r
+ MEM_TYPE_SPX = 2,\r
+ MEM_TYPE_EEPROM = 3,\r
+ MEM_TYPE_CPX =0x04,\r
+ MEM_TYPE_EEPROM_RESET = 0x8003, //reset after writing.\r
+}; \r
+\r
+\r
+fun_I2cWriteThenRead g_I2cWriteThenReadPtr = NULL;\r
+fun_I2cWrite g_I2cWritePtr = NULL;\r
+unsigned char * g_AllocatedBuffer = NULL;\r
+unsigned char * g_Buffer = NULL;\r
+unsigned long g_cbMaxWriteBufSize = 0;\r
+void * g_pContextI2cWrite = NULL;\r
+void * g_pContextI2cWriteThenRead = NULL;\r
+\r
+\r
+/*\r
+* The SetupI2cWriteCallback sets the I2cWrite callback function.\r
+* \r
+* PARAMETERS\r
+* \r
+* pCallbackContext [in] - A pointer to a caller-defined structure of data items\r
+* to be passed as the context parameter of the callback\r
+* routine each time it is called. \r
+*\r
+* I2cWritePtr [in] - A pointer to a i2cwirte callback routine, which is to \r
+* write I2C data. The callback routine must conform to \r
+* the following prototype:\r
+* \r
+* int (*fun_I2cWrite)( \r
+* void * pCallbackContext,\r
+* unsigned char ChipAddr,\r
+* unsigned long cbBuf, \r
+* unsigned char* pBuf\r
+* );\r
+*\r
+* The callback routine parameters are as follows:\r
+*\r
+* pCallbackContext [in] - A pointer to a caller-supplied \r
+* context area as specified in the\r
+* CallbackContext parameter of \r
+* SetupI2cWriteCallback. \r
+* ChipAddr [in] - The i2c chip address.\r
+* cbBuf [in] - The size of the input buffer, in bytes.\r
+* pBuf [in] - A pointer to the input buffer that contains \r
+* the data required to perform the operation.\r
+*\r
+*\r
+* cbMaxWriteBuf [in] - Specify the maximux transfer size for a I2c continue \r
+* writing with 'STOP'. This is limited in I2C bus Master\r
+* device. The size can not less then 3 since Channel \r
+* requires 2 address bytes plus a data byte.\r
+* \r
+*\r
+*\r
+* RETURN\r
+* \r
+* None\r
+*\r
+*/\r
+void SetupI2cWriteCallback( void * pCallbackContext,\r
+ fun_I2cWrite I2cWritePtr,\r
+ unsigned long cbMaxWriteBufSize)\r
+{\r
+ g_pContextI2cWrite = pCallbackContext;\r
+ g_I2cWritePtr = I2cWritePtr;\r
+ g_cbMaxWriteBufSize = cbMaxWriteBufSize;\r
+}\r
+\r
+/*\r
+* The SetupI2cWriteThenReadCallback sets the SetupI2cWriteThenRead callback function.\r
+* \r
+* PARAMETERS\r
+* \r
+* pCallbackContext [in] - A pointer to a caller-defined structure of data items\r
+* to be passed as the context parameter of the callback\r
+* routine each time it is called. \r
+*\r
+* I2cWriteThenReadPtr [in] - A pointer to a i2cwirte callback routine, which is to \r
+* write I2C data. The callback routine must conform to \r
+* the following prototype:\r
+*\r
+* int (*fun_I2cWriteThenRead)( \r
+* void * pCallbackContext,\r
+* unsigned char ChipAddr,\r
+* unsigned long cbBuf, \r
+* unsigned char* pBuf\r
+* );\r
+*\r
+* The callback routine parameters are as follows:\r
+*\r
+* pCallbackContext [in] - A pointer to a caller-supplied \r
+* context area as specified in the\r
+* CallbackContext parameter of \r
+* SetupI2cWriteCallback. \r
+* ChipAddr [in] - The i2c chip address.\r
+* cbBuf [in] - The size of the input buffer, in bytes.\r
+* pBuf [in] - A pointer to the input buffer that contains \r
+* the data required to perform the operation.\r
+*\r
+* RETURN\r
+* None\r
+* \r
+*/\r
+void SetupI2cWriteThenReadCallback( void * pCallbackContext,\r
+ fun_I2cWriteThenRead I2cWriteThenReadPtr)\r
+{\r
+ g_pContextI2cWriteThenRead = pCallbackContext;\r
+ g_I2cWriteThenReadPtr = I2cWriteThenReadPtr;\r
+}\r
+\r
+void SetupMemoryBuffer(void * pAllocedMemoryBuffer)\r
+{\r
+ g_AllocatedBuffer = (unsigned char*)pAllocedMemoryBuffer;\r
+ g_Buffer = g_AllocatedBuffer +2;\r
+}\r
+\r
+/*\r
+* Convert a 4-byte number from a ByteOrder into another ByteOrder.\r
+*/\r
+unsigned long ByteOrderSwapULONG(unsigned long i)\r
+{\r
+ return((i&0xff)<<24)+((i&0xff00)<<8)+((i&0xff0000)>>8)+((i>>24)&0xff);\r
+}\r
+\r
+/*\r
+* Convert a 2-byte number from a ByteOrder into another ByteOrder.\r
+*/\r
+unsigned short ByteOrderSwapWORD(unsigned short i)\r
+{\r
+ return ((i>>8)&0xff)+((i << 8)&0xff00);\r
+}\r
+\r
+/*\r
+* Convert a 4-byte number from generic byte order into Big Endia\r
+*/\r
+unsigned long ToBigEndiaULONG(unsigned long i)\r
+{\r
+#if __BYTE_ORDER == __LITTLE_ENDIAN\r
+ return ByteOrderSwapULONG(i);\r
+#else\r
+ return i;\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+* Convert a 2-byte number from generic byte order into Big Endia\r
+*/\r
+unsigned short ToBigEndiaWORD(unsigned short i)\r
+{\r
+#if __BYTE_ORDER == __LITTLE_ENDIAN\r
+ return ByteOrderSwapWORD(i);\r
+#else\r
+ return i;\r
+#endif\r
+}\r
+\r
+/*\r
+* Convert a 4-byte number from Big Endia into generic byte order.\r
+*/\r
+unsigned long FromBigEndiaULONG(unsigned long i)\r
+{\r
+#if __BYTE_ORDER == __LITTLE_ENDIAN\r
+ return ByteOrderSwapULONG(i);\r
+#else\r
+ return i;\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+* Convert a 2-byte number from Big Endia into generic byte order.\r
+*/\r
+unsigned short FromBigEndiaWORD(unsigned short i)\r
+{\r
+#if __BYTE_ORDER == __LITTLE_ENDIAN\r
+ return ByteOrderSwapWORD(i);\r
+#else\r
+ return i;\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+* To convert two digital ASCII into one BYTE.\r
+*/\r
+unsigned char ASCII_2_BYTE( char ah, char al) \r
+{\r
+ unsigned char ret = '\0';\r
+ int i =2;\r
+\r
+ for(;i>0;i--)\r
+ {\r
+ if( 'a' <= ah && 'f' >= ah)\r
+ {\r
+ ret += ah - 'a'+10;\r
+ }\r
+ else if( 'A' <= ah && 'F' >= ah)\r
+ {\r
+ ret += ah -'A'+10;\r
+ }\r
+\r
+ else if( '0' <= ah && '9' >= ah)\r
+ {\r
+ ret += ah - '0';\r
+ }\r
+ else\r
+ {\r
+ LOG((DBG_ERROR "Invalid txt data.\n"));\r
+\r
+ // ErrNo = ERRNO_INVALID_DATA;\r
+ break;\r
+ }\r
+ ah =al;\r
+ if(i==2)\r
+ ret = (unsigned short)ret << 4;\r
+ }\r
+ return ret;\r
+}\r
+\r
+/*\r
+* Read a byte from the specified register address.\r
+* \r
+* PARAMETERS\r
+* \r
+* RegAddr [in] - Specifies the register address.\r
+*\r
+* RETURN\r
+* \r
+* Returns the byte that is read from the specified register address.\r
+*\r
+*/\r
+unsigned char ReadReg(unsigned short RegAddr)\r
+{\r
+\r
+ unsigned char RegData;\r
+\r
+ if(!g_I2cWriteThenReadPtr)\r
+ {\r
+ LOG((DBG_ERROR "i2C function is not set.\n"));\r
+ return 0;\r
+ }\r
+\r
+\r
+ RegAddr = ToBigEndiaWORD(RegAddr);\r
+\r
+ g_I2cWriteThenReadPtr(g_pContextI2cWriteThenRead,CHIP_ADDR,\r
+ 2,(unsigned char*) &RegAddr,1,&RegData);\r
+\r
+ return RegData;\r
+}\r
+\r
+\r
+/*\r
+* Write a byte from the specified register address.\r
+* \r
+* PARAMETERS\r
+* \r
+* RegAddr [in] - Specifies the register address.\r
+*\r
+* RETURN\r
+* \r
+* Returns the byte that is read from the specified register address.\r
+*\r
+* REMARK\r
+* \r
+* The g_I2cWriteThenReadPtr must be set before calling this function.\r
+*/\r
+int WriteReg(unsigned short RegAddr, unsigned char RegData)\r
+{\r
+ unsigned char WrBuf[3];\r
+\r
+ if(!g_I2cWritePtr)\r
+ {\r
+ LOG((DBG_ERROR "i2C function is not set.\n"));\r
+ return -ERRNO_I2CFUN_NOT_SET;\r
+ }\r
+\r
+ *((unsigned short*) WrBuf) = ToBigEndiaWORD(RegAddr);\r
+ WrBuf[2] = RegData;\r
+\r
+ g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR,sizeof(WrBuf),WrBuf);\r
+\r
+ return ERRNO_NOERR;\r
+}\r
+\r
+/*\r
+* Writes a number of bytes from a buffer to Channel via I2C bus.\r
+* \r
+* PARAMETERS\r
+* \r
+* NumOfBytes [in] - Specifies the number of bytes to be written\r
+* to the memory address.\r
+* pData [in] - Pointer to a buffer from an array of I2C data\r
+* are to be written.\r
+* \r
+* RETURN\r
+* \r
+* If the operation completes successfully, the return value is ERRNO_NOERR.\r
+* Otherwise, return ERRON_* error code. \r
+*/\r
+int ChannelI2cBulkWrite( unsigned long NumOfBytes, unsigned char *pData)\r
+{\r
+ int ErrNo = ERRNO_NOERR;\r
+ unsigned short CurAddr;\r
+\r
+ //unsigned char *pDataEnd = pData + NumOfBytes;\r
+ unsigned char *pCurData = pData;\r
+ unsigned short *pCurAddrByte = NULL;\r
+ unsigned long BytesToProcess = 0;\r
+ unsigned short backup = 0;\r
+ const unsigned long cbAddressBytes = 2;\r
+ const unsigned long cbMaxDataLen = g_cbMaxWriteBufSize-cbAddressBytes;\r
+\r
+\r
+ if(!g_I2cWritePtr )\r
+ {\r
+ LOG((DBG_ERROR "i2C function is not set.\n"));\r
+ return -ERRNO_I2CFUN_NOT_SET;\r
+ }\r
+\r
+ //assert(NumOfBytes < 3);\r
+ CurAddr = FromBigEndiaWORD( *((unsigned short*)pData));\r
+\r
+ //skip first 2 bytes data (address).\r
+ NumOfBytes -= cbAddressBytes;\r
+ pCurData += cbAddressBytes;\r
+\r
+ for(;NumOfBytes;)\r
+ {\r
+ BytesToProcess = NumOfBytes > cbMaxDataLen? cbMaxDataLen : NumOfBytes;\r
+ NumOfBytes-= BytesToProcess;\r
+ // save the pervious 2 bytes for later use.\r
+ pCurAddrByte = (unsigned short*) (pCurData -cbAddressBytes);\r
+ backup = *pCurAddrByte;\r
+ *pCurAddrByte= ToBigEndiaWORD(CurAddr);\r
+ BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, BytesToProcess + cbAddressBytes,(unsigned char*)pCurAddrByte));\r
+ //restore the data \r
+ *pCurAddrByte = backup;\r
+\r
+ pCurData += BytesToProcess;\r
+ CurAddr += (unsigned short)BytesToProcess;\r
+ }\r
+ return ErrNo;\r
+}\r
+\r
+/*\r
+* Writes a number of bytes from a buffer to the specified memory address.\r
+*\r
+* PARAMETERS\r
+*\r
+* dwAddr [in] - Specifies the memory address.\r
+* NumOfBytes [in] - Specifies the number of bytes to be written\r
+* to the memory address.\r
+* pData [in] - Pointer to a buffer from an struct of \r
+* CX_CODEC_ROM_DATA is to be written.\r
+* MemType [in] - Specifies the requested memory type, the value must be from \r
+* the following table.\r
+*\r
+* MEM_TYPE_RAM = 1\r
+* MEM_TYPE_SPX = 2\r
+* MEM_TYPE_EEPROM = 3\r
+*\r
+* RETURN\r
+* \r
+* If the operation completes successfully, the return value is ERRNO_NOERR.\r
+* Otherwise, return ERRON_* error code. \r
+*/\r
+int CxWriteMemory(unsigned long dwAddr, unsigned long NumOfBytes, unsigned char * pData, int MemType )\r
+{\r
+ int ErrNo = ERRNO_NOERR;\r
+ unsigned char Address[4];\r
+ unsigned char WrData[8];\r
+ unsigned char offset = 0;\r
+ const unsigned long MAX_BUF_LEN = 0x100;\r
+ unsigned char cr = 0;\r
+ int bNeedToContinue = 0;\r
+ int i=0;\r
+\r
+\r
+ const unsigned long cbAddressBytes = 2;\r
+\r
+ unsigned short * pAddressByte;\r
+ unsigned char *pEndData = pData + NumOfBytes;\r
+ unsigned short RegMemMapAddr = ToBigEndiaWORD(0x300);\r
+ unsigned long BytesToProcess = 0;\r
+\r
+ while(NumOfBytes)\r
+ {\r
+\r
+ BytesToProcess = NumOfBytes <= MAX_BUF_LEN ? NumOfBytes : MAX_BUF_LEN;\r
+ NumOfBytes -= BytesToProcess;\r
+ pEndData = pData + BytesToProcess;\r
+\r
+ *((unsigned long*)&Address) = ToBigEndiaULONG(dwAddr);\r
+ // dwAddr += offset;\r
+ offset = 0;\r
+\r
+ if( !bNeedToContinue )\r
+ {\r
+#ifdef ENABLE_I2C_BURST_MODE\r
+ //\r
+ // Update the memory target address and buffer length.\r
+ //\r
+ WrData[0] = 0x02; //update target address Low 0x02FC \r
+ WrData[1] = 0xFC;\r
+ WrData[2] = Address[3];\r
+ WrData[3] = Address[2];\r
+ WrData[4] = Address[1];\r
+ WrData[5] = (unsigned char)BytesToProcess -1 ; // X bytes - 1\r
+ BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 6 , WrData));\r
+#else\r
+ //\r
+ // Update the memory target address and buffer length.\r
+ //\r
+ WrData[0] = 0x02; //update target address Low 0x02FC \r
+ WrData[1] = 0xFC;\r
+ WrData[2] = Address[3];\r
+ BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));\r
+\r
+ WrData[0] = 0x02; //update target address Middle 0x02FD\r
+ WrData[1] = 0xFD;\r
+ WrData[2] = Address[2];\r
+ BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));\r
+\r
+ WrData[0] = 0x02; //update target address High 0x02FE\r
+ WrData[1] = 0xFE;\r
+ WrData[2] = Address[1];\r
+ BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));\r
+\r
+ WrData[0] = 0x02; //update Buffer Length. 0x02FF\r
+ WrData[1] = 0xFF;\r
+ WrData[2] = (unsigned char)BytesToProcess -1 ; // X bytes - 1\r
+ BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));\r
+#endif\r
+ }\r
+\r
+ //\r
+ // Update buffer.\r
+ //\r
+#ifdef ENABLE_I2C_BURST_MODE\r
+ pAddressByte = (unsigned short*) (pData - cbAddressBytes);\r
+ memcpy(g_Buffer, pAddressByte, BytesToProcess+cbAddressBytes);\r
+ *((unsigned short*)g_Buffer) = RegMemMapAddr;\r
+ ChannelI2cBulkWrite(BytesToProcess+cbAddressBytes, (unsigned char*)g_Buffer);\r
+ pData = pEndData;\r
+#else\r
+ for(offset=0;pData != pEndData;offset++,pData++)\r
+ {\r
+ WrData[0] = 0x03; //update Buffer [0x0300 - 0x03ff]\r
+ WrData[1] = (unsigned char) offset;\r
+ WrData[2] = *pData;\r
+ BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));\r
+ }\r
+#endif \r
+\r
+ //\r
+ // Commit the changes and start to transfer buffer to memory.\r
+ //\r
+ if( MemType == MEM_TYPE_RAM)\r
+ {\r
+ cr = 0x81;\r
+ }\r
+ else if( MemType == MEM_TYPE_EEPROM)\r
+ {\r
+ cr = 0x83;\r
+ }\r
+ else if( MemType == MEM_TYPE_SPX)\r
+ {\r
+ cr = 0x85;\r
+ if( bNeedToContinue )\r
+ {\r
+ cr |= 0x08;\r
+ }\r
+ }\r
+\r
+ WrData[0] = 0x04; // UpdateCtl [0x400]\r
+ WrData[1] = 0x00;\r
+ WrData[2] = cr; // start to transfer \r
+ BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));\r
+\r
+ for(i = 0;i<MEMORY_UPDATE_TIMEOUT;i++)\r
+ {\r
+\r
+ // loop until the writing is done.\r
+ WrData[0] = ReadReg(0x0400);\r
+ if(!( WrData[0] & 0x80 ))\r
+ {\r
+ //done\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ //pending\r
+ if(MemType== MEM_TYPE_EEPROM)\r
+ { \r
+ //it needs more time for updating eeprom.\r
+ msleep(5); // need more waiting \r
+ }\r
+ else\r
+ {\r
+ udelay(1);\r
+ }\r
+ continue;\r
+ }\r
+ }\r
+\r
+ if( i == MEMORY_UPDATE_TIMEOUT)\r
+ {\r
+ //writing failed.\r
+ LOG( (DBG_ERROR "memory update timeout.\n"));\r
+ ErrNo = -ERRNO_UPDATE_MEMORY_FAILED;\r
+\r
+ break;\r
+ }\r
+ if ( i >= 1) \r
+ {\r
+ printk( KERN_ERR "write pending loop =%d\n", i);\r
+ } \r
+\r
+ bNeedToContinue = 1; \r
+ }while(0);\r
+\r
+ return ErrNo ;\r
+}\r
+\r
+#define WAIT_UNTIL_DEVICE_READY(_x_,_msg_) \\r
+for (timeout=0;timeout<dev_ready_time_out;timeout++) \\r
+{ \\r
+ Ready = ReadReg(0x1000); \\r
+ if (Ready _x_) break; \\r
+ msleep(10); \\r
+}; \\r
+if( timeout == dev_ready_time_out) \\r
+{ \\r
+ printk(KERN_ERR _msg_); \\r
+ ErrNo = -ERRNO_DEVICE_OUT_OF_CONTROL; \\r
+ break; \\r
+} \r
+\r
+unsigned int CxGetFirmwarePatchVersion(void)\r
+{\r
+ unsigned int FwPatchVersion = 0;\r
+ int ErrNo;\r
+\r
+\r
+ if( NULL == g_I2cWriteThenReadPtr||\r
+ NULL == g_I2cWritePtr)\r
+ {\r
+ ErrNo = -ERRNO_I2CFUN_NOT_SET;\r
+ LOG( (DBG_ERROR "i2C function is not set.\n"));\r
+ return 0;\r
+ }\r
+\r
+ FwPatchVersion = ReadReg(0x1584);\r
+ FwPatchVersion <<= 8;\r
+ FwPatchVersion |= ReadReg(0x1585);\r
+ FwPatchVersion <<= 8;\r
+ FwPatchVersion |= ReadReg(0x1586);\r
+\r
+ return FwPatchVersion;\r
+}\r
+\r
+unsigned int CxGetFirmwareVersion(void)\r
+{\r
+ unsigned int FwVersion = 0;\r
+ int ErrNo;\r
+\r
+\r
+ if( NULL == g_I2cWriteThenReadPtr||\r
+ NULL == g_I2cWritePtr)\r
+ {\r
+ ErrNo = -ERRNO_I2CFUN_NOT_SET;\r
+ LOG( (DBG_ERROR "i2C function is not set.\n"));\r
+ return 0;\r
+ }\r
+\r
+ FwVersion = ReadReg(0x1002);\r
+ FwVersion <<= 8;\r
+ FwVersion |= ReadReg(0x1001);\r
+ FwVersion <<= 8;\r
+ FwVersion |= ReadReg(0x1006);\r
+\r
+ return FwVersion;\r
+}\r
+\r
+// return number, 0= failed. 1 = successful. \r
+int DownloadFW(const unsigned char * const pRomBin)\r
+{\r
+ int ErrNo = ERRNO_NOERR;\r
+ struct CX_CODEC_ROM *pRom = (struct CX_CODEC_ROM *)pRomBin;\r
+ struct CX_CODEC_ROM_DATA *pRomData;\r
+ struct CX_CODEC_ROM_DATA *pRomDataEnd;\r
+ unsigned char *pData;\r
+ unsigned char *pDataEnd;\r
+ unsigned long CurAddr = 0;\r
+ unsigned long cbDataLen = 0;\r
+ unsigned char Ready;\r
+ unsigned long curProgress = 0;\r
+ unsigned long TotalLen = 0;\r
+ unsigned long i = 0;\r
+ const unsigned long dev_ready_time_out = 100;\r
+ int bIsRomVersion = 0; \r
+ const char CHAN_PATH[]="CNXT CHANNEL PATCH"; \r
+ unsigned long timeout;\r
+ unsigned long fwVer;\r
+ unsigned long fwPatchVer;\r
+\r
+ do{\r
+ if(pRom == NULL ||g_Buffer == NULL)\r
+ {\r
+ ErrNo = -ERRNO_INVALID_PARAMETER;\r
+ LOG( (DBG_ERROR "Invalid parameter.\n"));\r
+ break;\r
+ }\r
+\r
+ if( NULL == g_I2cWriteThenReadPtr||\r
+ NULL == g_I2cWritePtr)\r
+ {\r
+ ErrNo = -ERRNO_I2CFUN_NOT_SET;\r
+ LOG( (DBG_ERROR "i2C function is not set.\n"));\r
+ break;\r
+ }\r
+ \r
+ //check if codec is ROM version\r
+ if (0 == memcmp(CHAN_PATH,pRom->sDesc,sizeof(CHAN_PATH)-1)) {\r
+ printk(KERN_INFO "[CNXT] sDesc = %s", pRom->sDesc);\r
+ bIsRomVersion = 1;\r
+ }\r
+\r
+ if (bIsRomVersion) {\r
+ WAIT_UNTIL_DEVICE_READY(== 0X01,"cx2070x: Timed out waiting for codecto be ready!\n");\r
+ } else {\r
+ //Check if there is a FIRMWARE present. the Channel should get\r
+ // a clear reset signal before we download firmware to it.\r
+ if( (ReadReg(0x009) & 0x04) == 0) {\r
+ LOG((DBG_ERROR "cx2070x: did not get a clear reset..!"));\r
+ ErrNo = -ERRNO_DEVICE_NOT_RESET;\r
+ break;\r
+ }\r
+ }\r
+\r
+ TotalLen = FromBigEndiaULONG(pRom->LoaderLen) + FromBigEndiaULONG(pRom->CtlLen) + FromBigEndiaULONG(pRom->SpxLen);\r
+ // InitShowProgress(TotalLen);\r
+\r
+ //Download the loader.\r
+ pRomData = (struct CX_CODEC_ROM_DATA *) ( (char*)pRom + FromBigEndiaULONG(pRom->LoaderAddr));\r
+ pRomDataEnd = (struct CX_CODEC_ROM_DATA *) ((char*)pRomData +FromBigEndiaULONG(pRom->LoaderLen));\r
+\r
+ for( ;pRomData!=pRomDataEnd;)\r
+ {\r
+#ifdef ENABLE_I2C_BURST_MODE\r
+ pData = &pRomData->data[0];\r
+ pDataEnd= pData + FromBigEndiaULONG(pRomData->Length) - sizeof(unsigned long); \r
+ memcpy(g_Buffer, pData-2, FromBigEndiaULONG(pRomData->Length) - sizeof(unsigned short));\r
+ BIF(ChannelI2cBulkWrite( FromBigEndiaULONG(pRomData->Length) - sizeof(unsigned short), g_Buffer));\r
+ curProgress += FromBigEndiaULONG(pRomData->Length) ;\r
+ ShowProgress(curProgress,false, I2C_OK,TotalLen);\r
+\r
+ pRomData = (struct CX_CODEC_ROM_DATA *)pDataEnd;\r
+\r
+#else\r
+ CurAddr = FromBigEndiaULONG(pRomData->Address);\r
+ pData = &pRomData->data[0];\r
+ pDataEnd= pData + FromBigEndiaULONG(pRomData->Length) - sizeof(unsigned long); \r
+ for( ;pData!=pDataEnd;pData++)\r
+ {\r
+ *((unsigned short*)writeBuf) = ToBigEndiaWORD((unsigned short)CurAddr);\r
+ writeBuf[2]= *pData;\r
+ g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR,3, writeBuf);\r
+ CurAddr++;\r
+ }\r
+ pRomData = (struct CX_CODEC_ROM_DATA *)pData;\r
+#endif \r
+\r
+ }\r
+\r
+ //* check if the device is ready.\r
+ if (bIsRomVersion) {\r
+ WAIT_UNTIL_DEVICE_READY(== 0X01,"cx2070x: Timed out waiting for cx2070x to be ready after loader downloaded!\n");\r
+ } else { \r
+ WAIT_UNTIL_DEVICE_READY(!= 0xFF,"cx2070x: Timed out waiting for cx2070x to be ready after loader downloaded!\n");\r
+ }\r
+\r
+ //Download the CTL\r
+ pRomData = (struct CX_CODEC_ROM_DATA *) ( (char*)pRom + FromBigEndiaULONG(pRom->CtlAddr ));\r
+ pRomDataEnd = (struct CX_CODEC_ROM_DATA *) ((char*)pRomData +FromBigEndiaULONG(pRom->CtlLen));\r
+\r
+ for( ;pRomData!=pRomDataEnd;)\r
+ {\r
+ CurAddr = FromBigEndiaULONG(pRomData->Address);\r
+ pData = &pRomData->data[0];\r
+ cbDataLen = FromBigEndiaULONG(pRomData->Length) ;\r
+ BIF(CxWriteMemory(CurAddr,cbDataLen -sizeof(unsigned long)/*subtracts the address bytes*/ , pData, MEM_TYPE_RAM ));\r
+ // The next RoMData position = current romData position + cbDataLen + sizeof( data len bytes)\r
+ pRomData = (struct CX_CODEC_ROM_DATA *)((char*) pRomData + cbDataLen + sizeof(unsigned long)); \r
+\r
+ curProgress += cbDataLen ;\r
+ ShowProgress(curProgress,false, I2C_OK,TotalLen);\r
+ }\r
+\r
+ pRomData = (struct CX_CODEC_ROM_DATA *) ( (char*)pRom + FromBigEndiaULONG(pRom->SpxAddr ));\r
+ pRomDataEnd = (struct CX_CODEC_ROM_DATA *) ((char*)pRomData +FromBigEndiaULONG(pRom->SpxLen));\r
+\r
+ for( ;pRomData!=pRomDataEnd;)\r
+ {\r
+ CurAddr = FromBigEndiaULONG(pRomData->Address);\r
+ pData = &pRomData->data[0];\r
+ cbDataLen = FromBigEndiaULONG(pRomData->Length) ;\r
+ BIF(CxWriteMemory(CurAddr,cbDataLen -sizeof(unsigned long)/*subtracts the address bytes*/ , pData, MEM_TYPE_SPX ));\r
+ // The next RoMData position = current romData position + cbDataLen + sizeof( data len bytes)\r
+ pRomData = (struct CX_CODEC_ROM_DATA *)((char*) pRomData + cbDataLen + sizeof(unsigned long)); \r
+\r
+ curProgress += cbDataLen ;\r
+ ShowProgress(curProgress,false, I2C_OK,TotalLen);\r
+ }\r
+\r
+ if(ErrNo != 0) break;\r
+\r
+ ShowProgress(TotalLen,false, I2C_OK,TotalLen);\r
+\r
+ //\r
+ // Reset\r
+ //\r
+ if(bIsRomVersion)\r
+ {\r
+ WriteReg(0x1000,0x00);\r
+ // msleep(400); //delay 400 ms\r
+ }\r
+ else\r
+ {\r
+ WriteReg(0x400,0x40);\r
+ msleep(400); //delay 400 ms\r
+ }\r
+ \r
+ WAIT_UNTIL_DEVICE_READY(== 0x01,"cx2070x: Timed out waiting for cx2070x to be ready after firmware downloaded!\n");\r
+\r
+ //check if XPS code is working or not.\r
+\r
+ WriteReg(0x117d,0x01);\r
+ for (timeout=0;timeout<dev_ready_time_out;timeout++) \r
+ { \r
+ Ready = ReadReg(0x117d); \r
+ if (Ready == 0x00) break; \r
+ msleep(1); \r
+ }; \r
+ if( timeout == dev_ready_time_out) \r
+ { \r
+ LOG((DBG_ERROR "cx2070x: DSP lockup! download firmware failed!")); \r
+ ErrNo = -ERRNO_DEVICE_DSP_LOCKUP; \r
+ break; \r
+ } \r
+\r
+ fwVer = CxGetFirmwareVersion();\r
+ if(bIsRomVersion)\r
+ {\r
+ fwPatchVer = CxGetFirmwarePatchVersion();\r
+ printk(KERN_INFO "cx2070x: firmware download successfully! FW: %u,%u,%u, FW Patch: %u,%u,%u\n",\r
+ (unsigned char)(fwVer>>16), \r
+ (unsigned char)(fwVer>>8), \r
+ (unsigned char)fwVer,\r
+ (unsigned char)(fwPatchVer>>16), \r
+ (unsigned char)(fwPatchVer>>8), \r
+ (unsigned char)fwPatchVer);\r
+ }\r
+ else\r
+ {\r
+ printk(KERN_INFO "cx2070x: firmware download successfully! FW: %u,%u,%u\n",\r
+ (unsigned char)(fwVer>>16), \r
+ (unsigned char)(fwVer>>8), \r
+ (unsigned char)fwVer);\r
+ }\r
+\r
+ }while(0);\r
+\r
+ return ErrNo;\r
+}\r
+\r
+int ApplyDSPChanges(const unsigned char *const pRom)\r
+{\r
+ int ErrNo = ERRNO_NOERR;\r
+ struct CX_CODEC_ROM* pNewRom ;\r
+ struct CX_CODEC_ROM_APPENDED *pRomAppended;\r
+ struct CX_CODEC_ROM_APPENDED_INFO *pInfo;\r
+ struct CX_CODEC_APPENDED_DATA *pData;\r
+ struct CX_CODEC_APPENDED_DATA *pDataEnd;\r
+ unsigned short wRegAddr;\r
+ unsigned char NewC;\r
+\r
+#define DESC_LEN (16)\r
+ char szJira[DESC_LEN+1];\r
+ char szDate[DESC_LEN+1];\r
+\r
+ pNewRom = (struct CX_CODEC_ROM*) pRom;\r
+\r
+ // check if firmware contains DSP tuning data.\r
+ if( (FromBigEndiaULONG(pNewRom->SpxLen) + FromBigEndiaULONG(pNewRom->SpxAddr)) != FromBigEndiaULONG(pNewRom->FileSize) ) \r
+ {\r
+ // has DSP Tuning data.\r
+\r
+ pRomAppended = (struct CX_CODEC_ROM_APPENDED*)((char*)pNewRom + FromBigEndiaULONG(pNewRom->SpxAddr) + FromBigEndiaULONG(pNewRom->SpxLen));\r
+ pInfo = (struct CX_CODEC_ROM_APPENDED_INFO*) ((char*)pNewRom + FromBigEndiaULONG(pRomAppended ->TuningAddr) + \r
+ FromBigEndiaULONG(pRomAppended ->TuningLen));\r
+\r
+ strncpy(szJira,pInfo->sJIRAID,DESC_LEN);\r
+ strncpy(szDate,pInfo->sTime,DESC_LEN);\r
+ szJira[DESC_LEN]=0;\r
+ szDate[DESC_LEN-1]=0; //remove the last lettle $.\r
+ printk(KERN_INFO "Applying the DSP tuning changes..Jira: %s Date: %s\n"\r
+ ,szJira,szDate);\r
+\r
+ pData = pRomAppended->data; \r
+ pDataEnd = (struct CX_CODEC_APPENDED_DATA*)((char*)pData + FromBigEndiaULONG(pRomAppended->TuningLen));\r
+ for(;pData != pDataEnd; pData++)\r
+ {\r
+ wRegAddr = pData->Address[0];\r
+ wRegAddr <<=8;\r
+ wRegAddr |= pData->Address[1];\r
+ WriteReg(wRegAddr,pData->data);\r
+ //printk(KERN_INFO "0X%04x=0x%02x\n",wRegAddr,pData->data);\r
+ }\r
+ // re-set NewC.\r
+ NewC = ReadReg(0x117d); \r
+ WriteReg(0x117d,NewC|1);\r
+ }\r
+ \r
+ return ErrNo;\r
+}\r
+\r
--- /dev/null
+/****************************************************************************************\r
+*****************************************************************************************\r
+*** ***\r
+*** Copyright (c) 2011 ***\r
+*** ***\r
+*** Conexant Systems, Inc. ***\r
+*** ***\r
+*** All Rights Reserved ***\r
+*** ***\r
+*** CONFIDENTIAL ***\r
+*** ***\r
+*** NO DISSEMINATION OR USE WITHOUT PRIOR WRITTEN PERMISSION ***\r
+*** ***\r
+*****************************************************************************************\r
+**\r
+** File Name:\r
+** pump.c\r
+**\r
+** Abstract:\r
+** This code is to download the firmware to CX20709 device via I2C bus. \r
+** \r
+**\r
+** Product Name:\r
+** Conexant Channel CX20709\r
+**\r
+** Remark:\r
+** \r
+**\r
+** \r
+********************************************************************************\r
+** Revision History\r
+** Date Description Author\r
+** 01/21/11 Created. Simon Ho\r
+** 01/24/11 Speed up the firmware download by sending Simon Ho\r
+** I2C data continually without addressing \r
+********************************************************************************\r
+*****************************************************************************************/\r
+#ifdef __cplusplus\r
+extern "C"{\r
+#endif \r
+\r
+\r
+typedef int (*fun_I2cWriteThenRead)( void * pCallbackContext,\r
+ unsigned char ChipAddr, \r
+ unsigned long cbBuf,\r
+ unsigned char* pBuf,\r
+ unsigned long cbReadBuf, \r
+ unsigned char*pReadBuf);\r
+\r
+typedef int (*fun_I2cWrite)( void * pCallbackContext,\r
+ unsigned char ChipAddr,\r
+ unsigned long cbBuf, \r
+ unsigned char* pBuf);\r
+\r
+/*\r
+ * Set the I2cWrite callback function.\r
+ * \r
+ * PARAMETERS\r
+ * \r
+ * pCallbackContext [in] - A pointer to a caller-defined structure of data items\r
+ * to be passed as the context parameter of the callback\r
+ * routine each time it is called. \r
+ *\r
+ * I2cWritePtr [in] - A pointer to a i2cwirte callback routine, which is to \r
+ * write I2C data. The callback routine must conform to \r
+ * the following prototype:\r
+ * \r
+ * int (*fun_I2cWrite)( \r
+ * void * pCallbackContext,\r
+ * unsigned char ChipAddr,\r
+ * unsigned long cbBuf, \r
+ * unsigned char* pBuf\r
+ * );\r
+ *\r
+ * The callback routine parameters are as follows:\r
+ *\r
+ * pCallbackContext [in] - A pointer to a caller-supplied \r
+ * context area as specified in the\r
+ * CallbackContext parameter of \r
+ * SetupI2cWriteCallback. \r
+ * ChipAddr [in] - The i2c chip address.\r
+ * cbBuf [in] - The size of the input buffer, in bytes.\r
+ * pBuf [in] - A pointer to the input buffer that contains \r
+ * the data required to perform the operation.\r
+ *\r
+ *\r
+ * cbMaxWriteBufSize [in] - Specify the maximux transfer size for a I2c continue \r
+ * writing with 'STOP'. This is limited in I2C bus Master\r
+ * device. The size can not less then 3 since Channel \r
+ * requires 2 address bytes plus a data byte.\r
+ * \r
+ *\r
+ *\r
+ * RETURN\r
+ * None\r
+ *\r
+ */\r
+void SetupI2cWriteCallback( void * pCallbackContext,\r
+ fun_I2cWrite I2cWritePtr,\r
+ unsigned long cbMaxWriteBufSize);\r
+\r
+\r
+/*\r
+ * Set the SetupI2cWriteThenRead callback function.\r
+ * \r
+ * PARAMETERS\r
+ * \r
+ * pCallbackContext [in] - A pointer to a caller-defined structure of data items\r
+ * to be passed as the context parameter of the callback\r
+ * routine each time it is called. \r
+ *\r
+ * I2cWriteThenReadPtr [in] - A pointer to a i2cwirte callback routine, which is to \r
+ * write I2C data. The callback routine must conform to \r
+ * the following prototype:\r
+ *\r
+ * int (*fun_I2cWriteThenRead)( \r
+ * void * pCallbackContext,\r
+ * unsigned char ChipAddr,\r
+ * unsigned long cbBuf, \r
+ * unsigned char* pBuf\r
+ * );\r
+ *\r
+ * The callback routine parameters are as follows:\r
+ *\r
+ * pCallbackContext [in] - A pointer to a caller-supplied \r
+ * context area as specified in the\r
+ * CallbackContext parameter of \r
+ * SetupI2cWriteCallback. \r
+ * ChipAddr [in] - The i2c chip address.\r
+ * cbBuf [in] - The size of the input buffer, in bytes.\r
+ * pBuf [in] - A pointer to the input buffer that contains \r
+ * the data required to perform the operation.\r
+ *\r
+ * RETURN\r
+ * \r
+ * If the operation completes successfully, the return value is ERRNO_NOERR.\r
+ * Otherwise, return ERRON_* error code. \r
+ *\r
+ */\r
+void SetupI2cWriteThenReadCallback( void * pCallbackContext,\r
+ fun_I2cWriteThenRead I2cWriteThenReadPtr);\r
+\r
+\r
+void SetupMemoryBuffer(void * pAllocedMemoryBuffer);\r
+\r
+\r
+/*\r
+ * Download Firmware to Channel.\r
+ * \r
+ * PARAMETERS\r
+ * \r
+ * pRomData [in] - A pointer fo the input buffer that contains rom data.\r
+ *\r
+ * RETURN\r
+ * \r
+ * If the operation completes successfully, the return value is ERRNO_NOERR.\r
+ * Otherwise, return ERRON_* error code. \r
+ * \r
+ * REMARKS\r
+ * \r
+ * You need to set up both I2cWrite and I2cWriteThenRead callback function by calling \r
+ * SetupI2cWriteCallback and SetupI2cWriteThenReadCallback before you call this function.\r
+ */\r
+int DownloadFW(const unsigned char *const pRomData);\r
+\r
+/*\r
+ * Apply the extra DSP changes from FW file.\r
+ * \r
+ * PARAMETERS\r
+ * \r
+ * pRomData [in] - A pointer fo the input buffer that contains rom data.\r
+ *\r
+ * RETURN\r
+ * \r
+ * If the operation completes successfully, the return value is ERRNO_NOERR.\r
+ * Otherwise, return ERRON_* error code. \r
+ * \r
+ * REMARKS\r
+ * \r
+ * You need to set up both I2C/SPI Write and I2C/SPI WriteThenRead callback function \r
+ * by calling SetupI2cSpiWriteCallback and SetupI2cSpiWriteThenReadCallback before you call \r
+ * this function.\r
+ */\r
+int ApplyDSPChanges(const unsigned char *const pRom);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif \r
+\r
+\r
+/*Error codes*/\r
+#define ERRNO_NOERR 0\r
+#define ERRNO_SRC_FILE_NOT_EXIST 101\r
+#define ERRNO_WRITE_FILE_FAILED 102\r
+#define ERRNO_INVALID_DATA 103\r
+#define ERRNO_CHECKSUM_FAILED 104\r
+#define ERRNO_FAILED 105\r
+#define ERRNO_INVALID_PARAMETER 106\r
+#define ERRNO_NOMEM 107\r
+#define ERRNO_I2CFUN_NOT_SET 108\r
+#define ERRNO_UPDATE_MEMORY_FAILED 109\r
+#define ERRNO_DEVICE_NOT_RESET 110\r
+#define ERRNO_DEVICE_OUT_OF_CONTROL 111\r
+#define ERRNO_DEVICE_DSP_LOCKUP 112\r
+\r
+\r
select SND_SOC_RT5512
select SND_RK29_SOC_I2S
help
- Say Y if you want to add support for SoC audio on the ODROID.
+ Say Y if you want to add support for SoC audio on the rockchip.
+
+config SND_RK29_SOC_CX2070X
+ tristate "SoC I2S Audio support for rockchip - CX2070X"
+ depends on SND_RK29_SOC
+ select SND_SOC_CX2070X
+ select SND_RK29_SOC_I2S
+ help
+ Say Y if you want to add support for SoC audio on the rockchip.
config SND_RK29_SOC_RT5621
tristate "SoC I2S Audio support for rockchip - rt5621"
snd-soc-hdmi-i2s-objs := rk_hdmi_i2s.o
snd-soc-hdmi-spdif-objs := rk_hdmi_spdif.o
snd-soc-rt5512-objs := rk29_rt5512.o
+snd-soc-cx2070x-objs := rk29_cx2070x.o
obj-$(CONFIG_SND_RK29_SOC_WM8994) += snd-soc-wm8994.o
obj-$(CONFIG_SND_RK29_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_RK29_SOC_ES8323) += snd-soc-es8323.o
obj-$(CONFIG_SND_RK_SOC_RK3026) += snd-soc-rk3026.o
obj-$(CONFIG_SND_RK29_SOC_RT5512) += snd-soc-rt5512.o
+obj-$(CONFIG_SND_RK29_SOC_CX2070X) += snd-soc-cx2070x.o
--- /dev/null
+/*
+ * rk29_cx2070x.c
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <sound/jack.h>
+#include <linux/delay.h>
+#include "rk29_pcm.h"
+#include "rk29_i2s.h"
+#if 1
+#define DBG(x...) printk(KERN_INFO x)
+#else
+#define DBG(x...)
+#endif
+
+#include "../codecs/cx2070x.h"
+
+static struct platform_device *rk29_snd_device;
+
+
+static int rk29_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int pll_out = 0;
+ //unsigned int pll_div;
+ int ret;
+
+ DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
+
+ /* set cpu DAI configuration */
+ #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ #endif
+ #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ #endif
+ if (ret < 0)
+ return ret;
+
+
+ /* set codec DAI configuration */
+ #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ #endif
+ #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM );
+ #endif
+ if (ret < 0)
+ return ret;
+
+ switch(params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ pll_out = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ pll_out = 11289600;
+ break;
+ case 96000:
+ case 192000:
+ pll_out = 12288000*2;
+ break;
+ case 88200:
+ case 176400:
+ pll_out = 11289600*2;
+ break;
+ default:
+ DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
+ return -EINVAL;
+ break;
+ }
+#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
+ snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
+ snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, 64-1);//bclk = 2*32*lrck; 2*32fs
+ switch(params_rate(params)) {
+ case 176400:
+ case 192000:
+ snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 1);
+ DBG("Enter:%s, %d, MCLK=%d BCLK=%d LRCK=%d\n",
+ __FUNCTION__,__LINE__,pll_out,pll_out/2,params_rate(params));
+ break;
+ default:
+ snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3);
+ DBG("default:%s, %d, MCLK=%d BCLK=%d LRCK=%d\n",
+ __FUNCTION__,__LINE__,pll_out,pll_out/4,params_rate(params));
+ break;
+ }
+
+ /*Set the system clk for codec*/
+ ret=snd_soc_dai_set_sysclk(codec_dai, 0,pll_out,SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ {
+ DBG("rk29_hw_params_rt5631:failed to set the sysclk for codec side\n");
+ return ret;
+ }
+#endif
+
+#if 0
+ switch (params_rate(params))
+ {
+ case 8000:
+ pll_div = 12;
+ break;
+ case 16000:
+ pll_div = 6;
+ break;
+ case 32000:
+ pll_div = 3;
+ break;
+ case 48000:
+ pll_div = 2;
+ break;
+ case 96000:
+ pll_div = 1;
+ break;
+ case 11025:
+ pll_div = 8;
+ break;
+ case 22050:
+ pll_div = 4;
+ break;
+ case 44100:
+ pll_div = 2;
+ break;
+ case 88200:
+ pll_div = 1;
+ break;
+ default:
+ printk("Not yet supported!\n");
+ return -EINVAL;
+ }
+ ret = snd_soc_dai_set_clkdiv(codec_dai, cx2070x_CLK_DIV_ID, pll_div*4);
+ if (ret < 0)
+ return ret;
+#endif
+
+#if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)
+ //snd_soc_dai_set_pll(codec_dai,0,pll_out, 22579200);
+ snd_soc_dai_set_sysclk(codec_dai,0,pll_out, SND_SOC_CLOCK_IN);
+#endif
+ return 0;
+}
+
+//---------------------------------------------------------------------------------
+/*
+ * cx2070x DAI operations.
+ */
+static struct snd_soc_ops rk29_ops = {
+ .hw_params = rk29_hw_params,
+};
+
+static const struct snd_soc_dapm_widget cx2070x_dapm_widgets[] = {
+ // Input
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ //SND_SOC_DAPM_LINE("Headset Jack", NULL),
+ SND_SOC_DAPM_INPUT("BT IN"),
+ // Output
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("ALineOut", NULL),
+ SND_SOC_DAPM_OUTPUT("BT OUT"),
+
+};
+
+static const struct snd_soc_dapm_route cx2070x_audio_map[] = {
+ // Input
+ {"MIC IN", NULL,"Mic Jack"},
+ {"PCM IN", NULL, "BT IN"},
+ // Output
+ {"Ext Spk", NULL, "SPK OUT"},
+ {"Headphone Jack", NULL, "HP OUT"},
+ {"ALineOut", NULL, "LINE OUT"},
+ {"BT OUT", NULL, "PCM OUT"},
+};
+
+static int cx2070x_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ //struct cx2070x_codec_chip *chip = snd_soc_codec_get_drvdata(codec);
+ //int err = 0;
+ printk(">>>>>>>>>>%s",__FUNCTION__);
+ snd_soc_dapm_new_controls(dapm, cx2070x_dapm_widgets,
+ ARRAY_SIZE(cx2070x_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, cx2070x_audio_map,
+ ARRAY_SIZE(cx2070x_audio_map));
+#if FOR_MID
+ snd_soc_dapm_disable_pin(dapm, "Mic Jack");
+ snd_soc_dapm_disable_pin(dapm, "BT IN");
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk");
+ snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
+ snd_soc_dapm_disable_pin(dapm, "ALineOut");
+ snd_soc_dapm_disable_pin(dapm, "BT OUT");
+#endif
+
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static struct snd_soc_dai_link rk29_dai[] = {
+ { /* Primary DAI i/f */
+ .name = "CX2070X AIF1",
+ .stream_name = "CX2070X PCM",
+ .cpu_dai_name = "rk29_i2s.1",
+ .codec_dai_name = "cx2070x-hifi",
+ .platform_name = "rockchip-audio",
+ .codec_name = "cx2070x.0-0014",
+ .init = cx2070x_init,
+ .ops = &rk29_ops,
+ },
+};
+
+static struct snd_soc_card snd_soc_card_rk29 = {
+ .name = "RK29_CX2070X",
+ .dai_link = rk29_dai,
+
+ /* If you want to use sec_fifo device,
+ * changes the num_link = 2 or ARRAY_SIZE(snd_soc_card_rk29). */
+ .num_links = ARRAY_SIZE(rk29_dai),
+};
+
+static int __init audio_card_init(void)
+{
+ int ret;
+ rk29_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!rk29_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(rk29_snd_device, &snd_soc_card_rk29);
+
+ ret = platform_device_add(rk29_snd_device);
+ printk(">>>>>>>>>>%s ret = %d",__FUNCTION__, ret);
+ if (ret)
+ platform_device_put(rk29_snd_device);
+
+ return ret;
+}
+module_init(audio_card_init);
+
+static void __exit audio_card_exit(void)
+{
+ platform_device_unregister(rk29_snd_device);
+}
+module_exit(audio_card_exit);
+
+MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
+MODULE_AUTHOR("showy.zhang <showy.zhang@rock-chips.com>");
+MODULE_LICENSE("GPL");