From fc5293086d264445ef97cd078277c13e1fe6a9e1 Mon Sep 17 00:00:00 2001 From: zhangjun Date: Tue, 17 Sep 2013 15:14:22 +0800 Subject: [PATCH] CX20701 USB Codec: add CX20701 support in RK3188_tb_sdk --- arch/arm/mach-rk30/board-rk3168-tb.c | 7 + sound/soc/codecs/Kconfig | 3 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cx2070x-i2c.h | 411 ++++++ sound/soc/codecs/cx2070x.c | 2041 ++++++++++++++++++++++++++ sound/soc/codecs/cx2070x.h | 69 + sound/soc/codecs/cxdebug.c | 266 ++++ sound/soc/codecs/cxdebug.h | 46 + sound/soc/codecs/cxpump.c | 978 ++++++++++++ sound/soc/codecs/cxpump.h | 206 +++ sound/soc/rk29/Kconfig | 10 +- sound/soc/rk29/Makefile | 2 + sound/soc/rk29/rk29_cx2070x.c | 272 ++++ 13 files changed, 4312 insertions(+), 1 deletion(-) create mode 100644 sound/soc/codecs/cx2070x-i2c.h create mode 100644 sound/soc/codecs/cx2070x.c create mode 100644 sound/soc/codecs/cx2070x.h create mode 100644 sound/soc/codecs/cxdebug.c create mode 100644 sound/soc/codecs/cxdebug.h create mode 100644 sound/soc/codecs/cxpump.c create mode 100644 sound/soc/codecs/cxpump.h create mode 100644 sound/soc/rk29/rk29_cx2070x.c diff --git a/arch/arm/mach-rk30/board-rk3168-tb.c b/arch/arm/mach-rk30/board-rk3168-tb.c index c32a63e322c4..09709ba14ca0 100755 --- a/arch/arm/mach-rk30/board-rk3168-tb.c +++ b/arch/arm/mach-rk30/board-rk3168-tb.c @@ -1630,6 +1630,13 @@ static struct i2c_board_info __initdata i2c0_info[] = { }, #endif +#if defined (CONFIG_SND_SOC_CX2070X) + { + .type = "cx2070x", + .addr = 0x14, + .flags = 0, + }, +#endif #if defined (CONFIG_SND_SOC_RT5640) { diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b978303b0ebc..4ef07128b60c 100755 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -115,6 +115,7 @@ config SND_SOC_ALL_CODECS 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 @@ -478,3 +479,5 @@ config SND_SOC_RK3026 config SND_SOC_RT5512 tristate +config SND_SOC_CX2070X + tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 849b837570ad..c52948c0f4a4 100755 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -104,6 +104,7 @@ snd-soc-rk2928-objs := rk2928_codec.o 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 @@ -217,6 +218,7 @@ obj-$(CONFIG_SND_SOC_RK616) += snd-soc-rk616.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 diff --git a/sound/soc/codecs/cx2070x-i2c.h b/sound/soc/codecs/cx2070x-i2c.h new file mode 100644 index 000000000000..096fc8f85752 --- /dev/null +++ b/sound/soc/codecs/cx2070x-i2c.h @@ -0,0 +1,411 @@ +/* + * 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 + * + * 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 diff --git a/sound/soc/codecs/cx2070x.c b/sound/soc/codecs/cx2070x.c new file mode 100644 index 000000000000..6c4e8401c46b --- /dev/null +++ b/sound/soc/codecs/cx2070x.c @@ -0,0 +1,2041 @@ +/* +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cx2070x.h" + +#define CX2070X_DRIVER_VERSION AUDDRV_VERSION( 3, 1 ,0x10 ,0x13) + +#ifdef USING_I2C +#include +#endif + +#ifdef USING_SPI +#include +#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 +#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;ndapm, "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 +#include +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;ncontrol_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"); + diff --git a/sound/soc/codecs/cx2070x.h b/sound/soc/codecs/cx2070x.h new file mode 100644 index 000000000000..5b04e8f80dde --- /dev/null +++ b/sound/soc/codecs/cx2070x.h @@ -0,0 +1,69 @@ +/* + * 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 diff --git a/sound/soc/codecs/cxdebug.c b/sound/soc/codecs/cxdebug.c new file mode 100644 index 000000000000..43c4ab41a7b2 --- /dev/null +++ b/sound/soc/codecs/cxdebug.c @@ -0,0 +1,266 @@ +/* +* 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 +#include +#include /* Specifically, a module */ +#include +#include /* for get_user and put_user */ +//#include +//#include +//#include +#include +//#include +#include +#include + +//#define DEBUG 1 + +#include "cxdebug.h" + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#include +#endif + +#if defined(CONFIG_SPI_MASTER) +#include +#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 // For _MAX_PATH definition +#include +#include +#define msleep(_x_) +int printk(const char *s, ...); +#define KERN_ERR "<3>" +#elif defined(__KERNEL__) +// linux kernel environment. +#include +#include +#include +#include +#else +// +// linux user mode environment. +// +#include // For _MAX_PATH definition +#include +#endif +#include "cxpump.h" + +#if defined( __BIG_ENDIAN) && !defined(__BYTE_ORDER) +#define __BYTE_ORDER __BIG_ENDIAN +#elif defined( __LITTLE_ENDIAN ) && !defined(__BYTE_ORDER) +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + + +#ifndef __BYTE_ORDER +#error __BYTE_ORDER is undefined. +#endif + +#define ENABLE_I2C_BURST_MODE + +#if defined(_MSC_VER) +#pragma warning(disable:4127 4706 4101) // conditional experssion is constant +typedef enum I2C_STATE{I2C_OK,I2C_ERR,I2C_RETRY} ; +void ShowProgress(int curPos,bool bForceRedraw, I2C_STATE eState, const int MaxPos); +void InitShowProgress(const int MaxPos); +#elif defined(__KERNEL__) +//linux kernel mode +#define ShowProgress(curPos,bForceRedraw,eState, axPos) +#define InitShowProgress(MaxPos) +#else +//linux user mode +#define InitShowProgress(MaxPos) +#define ShowProgress(curPos,bForceRedraw,eState, axPos) +#endif + + +#ifndef NULL +#define NULL 0 +#endif //#ifndef NULL + +#define S_DESC "Cnxt Channel Firmware" /*Specify the string that will show on head of rom file*/ +#define S_ROM_FILE_NAME "cx2070x.fw" /*Specify the file name of rom file*/ +#define CHIP_ADDR 0x14 /*Specify the i2c chip address*/ +#define MEMORY_UPDATE_TIMEOUT 300 +#define MAX_ROM_SIZE (1024*1024) +//#define DBG_ERROR "ERROR : " +#define DBG_ERROR KERN_ERR +#define LOG( _msg_ ) printk _msg_ +//#define LOG( _msg_ ) ; + +typedef struct CX_CODEC_ROM_DATA +{ +#ifdef USE_TYPE_DEFINE + unsigned long Type; +#endif //#ifdef USE_TYPE_DEFINE + unsigned long Length; + unsigned long Address; + unsigned char data[1]; +}CX_CODEC_ROM_DATA; + +#define ROM_DATA_TYPE_S37 0xAA55CC01 // S37 format. +#define ROM_DATA_TYPE_CNXT 0xAA55CC04 // Conexant SPI format. +#define ROM_DATA_SEPARATED_LINE 0x23232323 //()()()() + +typedef struct CX_CODEC_ROM{ + char sDesc[24]; + char cOpenBracket; + char sVersion[5]; + char cCloseBracket; + char cEOF; + unsigned long FileSize; + unsigned long LoaderAddr; + unsigned long LoaderLen; + unsigned long CtlAddr; + unsigned long CtlLen; + unsigned long SpxAddr; + unsigned long SpxLen; + struct CX_CODEC_ROM_DATA Data[1]; +}CX_CODEC_ROM; + +typedef struct CX_CODEC_APPENDED_DATA +{ + unsigned char Address[2]; // The address of data. + unsigned char padding; // The actual firmware data. + unsigned char data; // The actual firmware data. +}CX_CODEC_APPENDED_DATA; + +typedef struct CX_CODEC_ROM_APPENDED{ + unsigned long TuningAddr; + unsigned long TuningLen; + CX_CODEC_APPENDED_DATA data[1]; // following by Jira id and time. +}CX_CODEC_ROM_APPENDED; + +typedef struct CX_CODEC_ROM_APPENDED_INFO{ + char sJIRAID[16]; + char sTime[16]; +}CX_CODEC_ROM_APPENDED_INFO; + + +// To convert two digital ASCII into one BYTE. +unsigned char ASCII_2_BYTE( char ah, char al) ; + +#define BUF_SIZE 0x1000 +#define BIBF(_x_) if(!(_x_)) break; +#define BIF(_x_) if((ErrNo=(_x_)) !=0) break; + + +#ifndef BIBF +#define BIBF( _x_ ) if(!(_x_)) break; +#endif + +enum { + MEM_TYPE_RAM = 1 /* CTL*/, + MEM_TYPE_SPX = 2, + MEM_TYPE_EEPROM = 3, + MEM_TYPE_CPX =0x04, + MEM_TYPE_EEPROM_RESET = 0x8003, //reset after writing. +}; + + +fun_I2cWriteThenRead g_I2cWriteThenReadPtr = NULL; +fun_I2cWrite g_I2cWritePtr = NULL; +unsigned char * g_AllocatedBuffer = NULL; +unsigned char * g_Buffer = NULL; +unsigned long g_cbMaxWriteBufSize = 0; +void * g_pContextI2cWrite = NULL; +void * g_pContextI2cWriteThenRead = NULL; + + +/* +* The SetupI2cWriteCallback sets the I2cWrite callback function. +* +* PARAMETERS +* +* pCallbackContext [in] - A pointer to a caller-defined structure of data items +* to be passed as the context parameter of the callback +* routine each time it is called. +* +* I2cWritePtr [in] - A pointer to a i2cwirte callback routine, which is to +* write I2C data. The callback routine must conform to +* the following prototype: +* +* int (*fun_I2cWrite)( +* void * pCallbackContext, +* unsigned char ChipAddr, +* unsigned long cbBuf, +* unsigned char* pBuf +* ); +* +* The callback routine parameters are as follows: +* +* pCallbackContext [in] - A pointer to a caller-supplied +* context area as specified in the +* CallbackContext parameter of +* SetupI2cWriteCallback. +* ChipAddr [in] - The i2c chip address. +* cbBuf [in] - The size of the input buffer, in bytes. +* pBuf [in] - A pointer to the input buffer that contains +* the data required to perform the operation. +* +* +* cbMaxWriteBuf [in] - Specify the maximux transfer size for a I2c continue +* writing with 'STOP'. This is limited in I2C bus Master +* device. The size can not less then 3 since Channel +* requires 2 address bytes plus a data byte. +* +* +* +* RETURN +* +* None +* +*/ +void SetupI2cWriteCallback( void * pCallbackContext, + fun_I2cWrite I2cWritePtr, + unsigned long cbMaxWriteBufSize) +{ + g_pContextI2cWrite = pCallbackContext; + g_I2cWritePtr = I2cWritePtr; + g_cbMaxWriteBufSize = cbMaxWriteBufSize; +} + +/* +* The SetupI2cWriteThenReadCallback sets the SetupI2cWriteThenRead callback function. +* +* PARAMETERS +* +* pCallbackContext [in] - A pointer to a caller-defined structure of data items +* to be passed as the context parameter of the callback +* routine each time it is called. +* +* I2cWriteThenReadPtr [in] - A pointer to a i2cwirte callback routine, which is to +* write I2C data. The callback routine must conform to +* the following prototype: +* +* int (*fun_I2cWriteThenRead)( +* void * pCallbackContext, +* unsigned char ChipAddr, +* unsigned long cbBuf, +* unsigned char* pBuf +* ); +* +* The callback routine parameters are as follows: +* +* pCallbackContext [in] - A pointer to a caller-supplied +* context area as specified in the +* CallbackContext parameter of +* SetupI2cWriteCallback. +* ChipAddr [in] - The i2c chip address. +* cbBuf [in] - The size of the input buffer, in bytes. +* pBuf [in] - A pointer to the input buffer that contains +* the data required to perform the operation. +* +* RETURN +* None +* +*/ +void SetupI2cWriteThenReadCallback( void * pCallbackContext, + fun_I2cWriteThenRead I2cWriteThenReadPtr) +{ + g_pContextI2cWriteThenRead = pCallbackContext; + g_I2cWriteThenReadPtr = I2cWriteThenReadPtr; +} + +void SetupMemoryBuffer(void * pAllocedMemoryBuffer) +{ + g_AllocatedBuffer = (unsigned char*)pAllocedMemoryBuffer; + g_Buffer = g_AllocatedBuffer +2; +} + +/* +* Convert a 4-byte number from a ByteOrder into another ByteOrder. +*/ +unsigned long ByteOrderSwapULONG(unsigned long i) +{ + return((i&0xff)<<24)+((i&0xff00)<<8)+((i&0xff0000)>>8)+((i>>24)&0xff); +} + +/* +* Convert a 2-byte number from a ByteOrder into another ByteOrder. +*/ +unsigned short ByteOrderSwapWORD(unsigned short i) +{ + return ((i>>8)&0xff)+((i << 8)&0xff00); +} + +/* +* Convert a 4-byte number from generic byte order into Big Endia +*/ +unsigned long ToBigEndiaULONG(unsigned long i) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + return ByteOrderSwapULONG(i); +#else + return i; +#endif +} + + +/* +* Convert a 2-byte number from generic byte order into Big Endia +*/ +unsigned short ToBigEndiaWORD(unsigned short i) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + return ByteOrderSwapWORD(i); +#else + return i; +#endif +} + +/* +* Convert a 4-byte number from Big Endia into generic byte order. +*/ +unsigned long FromBigEndiaULONG(unsigned long i) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + return ByteOrderSwapULONG(i); +#else + return i; +#endif +} + + +/* +* Convert a 2-byte number from Big Endia into generic byte order. +*/ +unsigned short FromBigEndiaWORD(unsigned short i) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + return ByteOrderSwapWORD(i); +#else + return i; +#endif +} + + +/* +* To convert two digital ASCII into one BYTE. +*/ +unsigned char ASCII_2_BYTE( char ah, char al) +{ + unsigned char ret = '\0'; + int i =2; + + for(;i>0;i--) + { + if( 'a' <= ah && 'f' >= ah) + { + ret += ah - 'a'+10; + } + else if( 'A' <= ah && 'F' >= ah) + { + ret += ah -'A'+10; + } + + else if( '0' <= ah && '9' >= ah) + { + ret += ah - '0'; + } + else + { + LOG((DBG_ERROR "Invalid txt data.\n")); + + // ErrNo = ERRNO_INVALID_DATA; + break; + } + ah =al; + if(i==2) + ret = (unsigned short)ret << 4; + } + return ret; +} + +/* +* Read a byte from the specified register address. +* +* PARAMETERS +* +* RegAddr [in] - Specifies the register address. +* +* RETURN +* +* Returns the byte that is read from the specified register address. +* +*/ +unsigned char ReadReg(unsigned short RegAddr) +{ + + unsigned char RegData; + + if(!g_I2cWriteThenReadPtr) + { + LOG((DBG_ERROR "i2C function is not set.\n")); + return 0; + } + + + RegAddr = ToBigEndiaWORD(RegAddr); + + g_I2cWriteThenReadPtr(g_pContextI2cWriteThenRead,CHIP_ADDR, + 2,(unsigned char*) &RegAddr,1,&RegData); + + return RegData; +} + + +/* +* Write a byte from the specified register address. +* +* PARAMETERS +* +* RegAddr [in] - Specifies the register address. +* +* RETURN +* +* Returns the byte that is read from the specified register address. +* +* REMARK +* +* The g_I2cWriteThenReadPtr must be set before calling this function. +*/ +int WriteReg(unsigned short RegAddr, unsigned char RegData) +{ + unsigned char WrBuf[3]; + + if(!g_I2cWritePtr) + { + LOG((DBG_ERROR "i2C function is not set.\n")); + return -ERRNO_I2CFUN_NOT_SET; + } + + *((unsigned short*) WrBuf) = ToBigEndiaWORD(RegAddr); + WrBuf[2] = RegData; + + g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR,sizeof(WrBuf),WrBuf); + + return ERRNO_NOERR; +} + +/* +* Writes a number of bytes from a buffer to Channel via I2C bus. +* +* PARAMETERS +* +* NumOfBytes [in] - Specifies the number of bytes to be written +* to the memory address. +* pData [in] - Pointer to a buffer from an array of I2C data +* are to be written. +* +* RETURN +* +* If the operation completes successfully, the return value is ERRNO_NOERR. +* Otherwise, return ERRON_* error code. +*/ +int ChannelI2cBulkWrite( unsigned long NumOfBytes, unsigned char *pData) +{ + int ErrNo = ERRNO_NOERR; + unsigned short CurAddr; + + //unsigned char *pDataEnd = pData + NumOfBytes; + unsigned char *pCurData = pData; + unsigned short *pCurAddrByte = NULL; + unsigned long BytesToProcess = 0; + unsigned short backup = 0; + const unsigned long cbAddressBytes = 2; + const unsigned long cbMaxDataLen = g_cbMaxWriteBufSize-cbAddressBytes; + + + if(!g_I2cWritePtr ) + { + LOG((DBG_ERROR "i2C function is not set.\n")); + return -ERRNO_I2CFUN_NOT_SET; + } + + //assert(NumOfBytes < 3); + CurAddr = FromBigEndiaWORD( *((unsigned short*)pData)); + + //skip first 2 bytes data (address). + NumOfBytes -= cbAddressBytes; + pCurData += cbAddressBytes; + + for(;NumOfBytes;) + { + BytesToProcess = NumOfBytes > cbMaxDataLen? cbMaxDataLen : NumOfBytes; + NumOfBytes-= BytesToProcess; + // save the pervious 2 bytes for later use. + pCurAddrByte = (unsigned short*) (pCurData -cbAddressBytes); + backup = *pCurAddrByte; + *pCurAddrByte= ToBigEndiaWORD(CurAddr); + BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, BytesToProcess + cbAddressBytes,(unsigned char*)pCurAddrByte)); + //restore the data + *pCurAddrByte = backup; + + pCurData += BytesToProcess; + CurAddr += (unsigned short)BytesToProcess; + } + return ErrNo; +} + +/* +* Writes a number of bytes from a buffer to the specified memory address. +* +* PARAMETERS +* +* dwAddr [in] - Specifies the memory address. +* NumOfBytes [in] - Specifies the number of bytes to be written +* to the memory address. +* pData [in] - Pointer to a buffer from an struct of +* CX_CODEC_ROM_DATA is to be written. +* MemType [in] - Specifies the requested memory type, the value must be from +* the following table. +* +* MEM_TYPE_RAM = 1 +* MEM_TYPE_SPX = 2 +* MEM_TYPE_EEPROM = 3 +* +* RETURN +* +* If the operation completes successfully, the return value is ERRNO_NOERR. +* Otherwise, return ERRON_* error code. +*/ +int CxWriteMemory(unsigned long dwAddr, unsigned long NumOfBytes, unsigned char * pData, int MemType ) +{ + int ErrNo = ERRNO_NOERR; + unsigned char Address[4]; + unsigned char WrData[8]; + unsigned char offset = 0; + const unsigned long MAX_BUF_LEN = 0x100; + unsigned char cr = 0; + int bNeedToContinue = 0; + int i=0; + + + const unsigned long cbAddressBytes = 2; + + unsigned short * pAddressByte; + unsigned char *pEndData = pData + NumOfBytes; + unsigned short RegMemMapAddr = ToBigEndiaWORD(0x300); + unsigned long BytesToProcess = 0; + + while(NumOfBytes) + { + + BytesToProcess = NumOfBytes <= MAX_BUF_LEN ? NumOfBytes : MAX_BUF_LEN; + NumOfBytes -= BytesToProcess; + pEndData = pData + BytesToProcess; + + *((unsigned long*)&Address) = ToBigEndiaULONG(dwAddr); + // dwAddr += offset; + offset = 0; + + if( !bNeedToContinue ) + { +#ifdef ENABLE_I2C_BURST_MODE + // + // Update the memory target address and buffer length. + // + WrData[0] = 0x02; //update target address Low 0x02FC + WrData[1] = 0xFC; + WrData[2] = Address[3]; + WrData[3] = Address[2]; + WrData[4] = Address[1]; + WrData[5] = (unsigned char)BytesToProcess -1 ; // X bytes - 1 + BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 6 , WrData)); +#else + // + // Update the memory target address and buffer length. + // + WrData[0] = 0x02; //update target address Low 0x02FC + WrData[1] = 0xFC; + WrData[2] = Address[3]; + BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData)); + + WrData[0] = 0x02; //update target address Middle 0x02FD + WrData[1] = 0xFD; + WrData[2] = Address[2]; + BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData)); + + WrData[0] = 0x02; //update target address High 0x02FE + WrData[1] = 0xFE; + WrData[2] = Address[1]; + BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData)); + + WrData[0] = 0x02; //update Buffer Length. 0x02FF + WrData[1] = 0xFF; + WrData[2] = (unsigned char)BytesToProcess -1 ; // X bytes - 1 + BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData)); +#endif + } + + // + // Update buffer. + // +#ifdef ENABLE_I2C_BURST_MODE + pAddressByte = (unsigned short*) (pData - cbAddressBytes); + memcpy(g_Buffer, pAddressByte, BytesToProcess+cbAddressBytes); + *((unsigned short*)g_Buffer) = RegMemMapAddr; + ChannelI2cBulkWrite(BytesToProcess+cbAddressBytes, (unsigned char*)g_Buffer); + pData = pEndData; +#else + for(offset=0;pData != pEndData;offset++,pData++) + { + WrData[0] = 0x03; //update Buffer [0x0300 - 0x03ff] + WrData[1] = (unsigned char) offset; + WrData[2] = *pData; + BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData)); + } +#endif + + // + // Commit the changes and start to transfer buffer to memory. + // + if( MemType == MEM_TYPE_RAM) + { + cr = 0x81; + } + else if( MemType == MEM_TYPE_EEPROM) + { + cr = 0x83; + } + else if( MemType == MEM_TYPE_SPX) + { + cr = 0x85; + if( bNeedToContinue ) + { + cr |= 0x08; + } + } + + WrData[0] = 0x04; // UpdateCtl [0x400] + WrData[1] = 0x00; + WrData[2] = cr; // start to transfer + BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData)); + + for(i = 0;i= 1) + { + printk( KERN_ERR "write pending loop =%d\n", i); + } + + bNeedToContinue = 1; + }while(0); + + return ErrNo ; +} + +#define WAIT_UNTIL_DEVICE_READY(_x_,_msg_) \ +for (timeout=0;timeoutsDesc,sizeof(CHAN_PATH)-1)) { + printk(KERN_INFO "[CNXT] sDesc = %s", pRom->sDesc); + bIsRomVersion = 1; + } + + if (bIsRomVersion) { + WAIT_UNTIL_DEVICE_READY(== 0X01,"cx2070x: Timed out waiting for codecto be ready!\n"); + } else { + //Check if there is a FIRMWARE present. the Channel should get + // a clear reset signal before we download firmware to it. + if( (ReadReg(0x009) & 0x04) == 0) { + LOG((DBG_ERROR "cx2070x: did not get a clear reset..!")); + ErrNo = -ERRNO_DEVICE_NOT_RESET; + break; + } + } + + TotalLen = FromBigEndiaULONG(pRom->LoaderLen) + FromBigEndiaULONG(pRom->CtlLen) + FromBigEndiaULONG(pRom->SpxLen); + // InitShowProgress(TotalLen); + + //Download the loader. + pRomData = (struct CX_CODEC_ROM_DATA *) ( (char*)pRom + FromBigEndiaULONG(pRom->LoaderAddr)); + pRomDataEnd = (struct CX_CODEC_ROM_DATA *) ((char*)pRomData +FromBigEndiaULONG(pRom->LoaderLen)); + + for( ;pRomData!=pRomDataEnd;) + { +#ifdef ENABLE_I2C_BURST_MODE + pData = &pRomData->data[0]; + pDataEnd= pData + FromBigEndiaULONG(pRomData->Length) - sizeof(unsigned long); + memcpy(g_Buffer, pData-2, FromBigEndiaULONG(pRomData->Length) - sizeof(unsigned short)); + BIF(ChannelI2cBulkWrite( FromBigEndiaULONG(pRomData->Length) - sizeof(unsigned short), g_Buffer)); + curProgress += FromBigEndiaULONG(pRomData->Length) ; + ShowProgress(curProgress,false, I2C_OK,TotalLen); + + pRomData = (struct CX_CODEC_ROM_DATA *)pDataEnd; + +#else + CurAddr = FromBigEndiaULONG(pRomData->Address); + pData = &pRomData->data[0]; + pDataEnd= pData + FromBigEndiaULONG(pRomData->Length) - sizeof(unsigned long); + for( ;pData!=pDataEnd;pData++) + { + *((unsigned short*)writeBuf) = ToBigEndiaWORD((unsigned short)CurAddr); + writeBuf[2]= *pData; + g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR,3, writeBuf); + CurAddr++; + } + pRomData = (struct CX_CODEC_ROM_DATA *)pData; +#endif + + } + + //* check if the device is ready. + if (bIsRomVersion) { + WAIT_UNTIL_DEVICE_READY(== 0X01,"cx2070x: Timed out waiting for cx2070x to be ready after loader downloaded!\n"); + } else { + WAIT_UNTIL_DEVICE_READY(!= 0xFF,"cx2070x: Timed out waiting for cx2070x to be ready after loader downloaded!\n"); + } + + //Download the CTL + pRomData = (struct CX_CODEC_ROM_DATA *) ( (char*)pRom + FromBigEndiaULONG(pRom->CtlAddr )); + pRomDataEnd = (struct CX_CODEC_ROM_DATA *) ((char*)pRomData +FromBigEndiaULONG(pRom->CtlLen)); + + for( ;pRomData!=pRomDataEnd;) + { + CurAddr = FromBigEndiaULONG(pRomData->Address); + pData = &pRomData->data[0]; + cbDataLen = FromBigEndiaULONG(pRomData->Length) ; + BIF(CxWriteMemory(CurAddr,cbDataLen -sizeof(unsigned long)/*subtracts the address bytes*/ , pData, MEM_TYPE_RAM )); + // The next RoMData position = current romData position + cbDataLen + sizeof( data len bytes) + pRomData = (struct CX_CODEC_ROM_DATA *)((char*) pRomData + cbDataLen + sizeof(unsigned long)); + + curProgress += cbDataLen ; + ShowProgress(curProgress,false, I2C_OK,TotalLen); + } + + pRomData = (struct CX_CODEC_ROM_DATA *) ( (char*)pRom + FromBigEndiaULONG(pRom->SpxAddr )); + pRomDataEnd = (struct CX_CODEC_ROM_DATA *) ((char*)pRomData +FromBigEndiaULONG(pRom->SpxLen)); + + for( ;pRomData!=pRomDataEnd;) + { + CurAddr = FromBigEndiaULONG(pRomData->Address); + pData = &pRomData->data[0]; + cbDataLen = FromBigEndiaULONG(pRomData->Length) ; + BIF(CxWriteMemory(CurAddr,cbDataLen -sizeof(unsigned long)/*subtracts the address bytes*/ , pData, MEM_TYPE_SPX )); + // The next RoMData position = current romData position + cbDataLen + sizeof( data len bytes) + pRomData = (struct CX_CODEC_ROM_DATA *)((char*) pRomData + cbDataLen + sizeof(unsigned long)); + + curProgress += cbDataLen ; + ShowProgress(curProgress,false, I2C_OK,TotalLen); + } + + if(ErrNo != 0) break; + + ShowProgress(TotalLen,false, I2C_OK,TotalLen); + + // + // Reset + // + if(bIsRomVersion) + { + WriteReg(0x1000,0x00); + // msleep(400); //delay 400 ms + } + else + { + WriteReg(0x400,0x40); + msleep(400); //delay 400 ms + } + + WAIT_UNTIL_DEVICE_READY(== 0x01,"cx2070x: Timed out waiting for cx2070x to be ready after firmware downloaded!\n"); + + //check if XPS code is working or not. + + WriteReg(0x117d,0x01); + for (timeout=0;timeout>16), + (unsigned char)(fwVer>>8), + (unsigned char)fwVer, + (unsigned char)(fwPatchVer>>16), + (unsigned char)(fwPatchVer>>8), + (unsigned char)fwPatchVer); + } + else + { + printk(KERN_INFO "cx2070x: firmware download successfully! FW: %u,%u,%u\n", + (unsigned char)(fwVer>>16), + (unsigned char)(fwVer>>8), + (unsigned char)fwVer); + } + + }while(0); + + return ErrNo; +} + +int ApplyDSPChanges(const unsigned char *const pRom) +{ + int ErrNo = ERRNO_NOERR; + struct CX_CODEC_ROM* pNewRom ; + struct CX_CODEC_ROM_APPENDED *pRomAppended; + struct CX_CODEC_ROM_APPENDED_INFO *pInfo; + struct CX_CODEC_APPENDED_DATA *pData; + struct CX_CODEC_APPENDED_DATA *pDataEnd; + unsigned short wRegAddr; + unsigned char NewC; + +#define DESC_LEN (16) + char szJira[DESC_LEN+1]; + char szDate[DESC_LEN+1]; + + pNewRom = (struct CX_CODEC_ROM*) pRom; + + // check if firmware contains DSP tuning data. + if( (FromBigEndiaULONG(pNewRom->SpxLen) + FromBigEndiaULONG(pNewRom->SpxAddr)) != FromBigEndiaULONG(pNewRom->FileSize) ) + { + // has DSP Tuning data. + + pRomAppended = (struct CX_CODEC_ROM_APPENDED*)((char*)pNewRom + FromBigEndiaULONG(pNewRom->SpxAddr) + FromBigEndiaULONG(pNewRom->SpxLen)); + pInfo = (struct CX_CODEC_ROM_APPENDED_INFO*) ((char*)pNewRom + FromBigEndiaULONG(pRomAppended ->TuningAddr) + + FromBigEndiaULONG(pRomAppended ->TuningLen)); + + strncpy(szJira,pInfo->sJIRAID,DESC_LEN); + strncpy(szDate,pInfo->sTime,DESC_LEN); + szJira[DESC_LEN]=0; + szDate[DESC_LEN-1]=0; //remove the last lettle $. + printk(KERN_INFO "Applying the DSP tuning changes..Jira: %s Date: %s\n" + ,szJira,szDate); + + pData = pRomAppended->data; + pDataEnd = (struct CX_CODEC_APPENDED_DATA*)((char*)pData + FromBigEndiaULONG(pRomAppended->TuningLen)); + for(;pData != pDataEnd; pData++) + { + wRegAddr = pData->Address[0]; + wRegAddr <<=8; + wRegAddr |= pData->Address[1]; + WriteReg(wRegAddr,pData->data); + //printk(KERN_INFO "0X%04x=0x%02x\n",wRegAddr,pData->data); + } + // re-set NewC. + NewC = ReadReg(0x117d); + WriteReg(0x117d,NewC|1); + } + + return ErrNo; +} + diff --git a/sound/soc/codecs/cxpump.h b/sound/soc/codecs/cxpump.h new file mode 100644 index 000000000000..825b070a8800 --- /dev/null +++ b/sound/soc/codecs/cxpump.h @@ -0,0 +1,206 @@ +/**************************************************************************************** +***************************************************************************************** +*** *** +*** Copyright (c) 2011 *** +*** *** +*** Conexant Systems, Inc. *** +*** *** +*** All Rights Reserved *** +*** *** +*** CONFIDENTIAL *** +*** *** +*** NO DISSEMINATION OR USE WITHOUT PRIOR WRITTEN PERMISSION *** +*** *** +***************************************************************************************** +** +** File Name: +** pump.c +** +** Abstract: +** This code is to download the firmware to CX20709 device via I2C bus. +** +** +** Product Name: +** Conexant Channel CX20709 +** +** Remark: +** +** +** +******************************************************************************** +** Revision History +** Date Description Author +** 01/21/11 Created. Simon Ho +** 01/24/11 Speed up the firmware download by sending Simon Ho +** I2C data continually without addressing +******************************************************************************** +*****************************************************************************************/ +#ifdef __cplusplus +extern "C"{ +#endif + + +typedef int (*fun_I2cWriteThenRead)( void * pCallbackContext, + unsigned char ChipAddr, + unsigned long cbBuf, + unsigned char* pBuf, + unsigned long cbReadBuf, + unsigned char*pReadBuf); + +typedef int (*fun_I2cWrite)( void * pCallbackContext, + unsigned char ChipAddr, + unsigned long cbBuf, + unsigned char* pBuf); + +/* + * Set the I2cWrite callback function. + * + * PARAMETERS + * + * pCallbackContext [in] - A pointer to a caller-defined structure of data items + * to be passed as the context parameter of the callback + * routine each time it is called. + * + * I2cWritePtr [in] - A pointer to a i2cwirte callback routine, which is to + * write I2C data. The callback routine must conform to + * the following prototype: + * + * int (*fun_I2cWrite)( + * void * pCallbackContext, + * unsigned char ChipAddr, + * unsigned long cbBuf, + * unsigned char* pBuf + * ); + * + * The callback routine parameters are as follows: + * + * pCallbackContext [in] - A pointer to a caller-supplied + * context area as specified in the + * CallbackContext parameter of + * SetupI2cWriteCallback. + * ChipAddr [in] - The i2c chip address. + * cbBuf [in] - The size of the input buffer, in bytes. + * pBuf [in] - A pointer to the input buffer that contains + * the data required to perform the operation. + * + * + * cbMaxWriteBufSize [in] - Specify the maximux transfer size for a I2c continue + * writing with 'STOP'. This is limited in I2C bus Master + * device. The size can not less then 3 since Channel + * requires 2 address bytes plus a data byte. + * + * + * + * RETURN + * None + * + */ +void SetupI2cWriteCallback( void * pCallbackContext, + fun_I2cWrite I2cWritePtr, + unsigned long cbMaxWriteBufSize); + + +/* + * Set the SetupI2cWriteThenRead callback function. + * + * PARAMETERS + * + * pCallbackContext [in] - A pointer to a caller-defined structure of data items + * to be passed as the context parameter of the callback + * routine each time it is called. + * + * I2cWriteThenReadPtr [in] - A pointer to a i2cwirte callback routine, which is to + * write I2C data. The callback routine must conform to + * the following prototype: + * + * int (*fun_I2cWriteThenRead)( + * void * pCallbackContext, + * unsigned char ChipAddr, + * unsigned long cbBuf, + * unsigned char* pBuf + * ); + * + * The callback routine parameters are as follows: + * + * pCallbackContext [in] - A pointer to a caller-supplied + * context area as specified in the + * CallbackContext parameter of + * SetupI2cWriteCallback. + * ChipAddr [in] - The i2c chip address. + * cbBuf [in] - The size of the input buffer, in bytes. + * pBuf [in] - A pointer to the input buffer that contains + * the data required to perform the operation. + * + * RETURN + * + * If the operation completes successfully, the return value is ERRNO_NOERR. + * Otherwise, return ERRON_* error code. + * + */ +void SetupI2cWriteThenReadCallback( void * pCallbackContext, + fun_I2cWriteThenRead I2cWriteThenReadPtr); + + +void SetupMemoryBuffer(void * pAllocedMemoryBuffer); + + +/* + * Download Firmware to Channel. + * + * PARAMETERS + * + * pRomData [in] - A pointer fo the input buffer that contains rom data. + * + * RETURN + * + * If the operation completes successfully, the return value is ERRNO_NOERR. + * Otherwise, return ERRON_* error code. + * + * REMARKS + * + * You need to set up both I2cWrite and I2cWriteThenRead callback function by calling + * SetupI2cWriteCallback and SetupI2cWriteThenReadCallback before you call this function. + */ +int DownloadFW(const unsigned char *const pRomData); + +/* + * Apply the extra DSP changes from FW file. + * + * PARAMETERS + * + * pRomData [in] - A pointer fo the input buffer that contains rom data. + * + * RETURN + * + * If the operation completes successfully, the return value is ERRNO_NOERR. + * Otherwise, return ERRON_* error code. + * + * REMARKS + * + * You need to set up both I2C/SPI Write and I2C/SPI WriteThenRead callback function + * by calling SetupI2cSpiWriteCallback and SetupI2cSpiWriteThenReadCallback before you call + * this function. + */ +int ApplyDSPChanges(const unsigned char *const pRom); + +#ifdef __cplusplus +} +#endif + + +/*Error codes*/ +#define ERRNO_NOERR 0 +#define ERRNO_SRC_FILE_NOT_EXIST 101 +#define ERRNO_WRITE_FILE_FAILED 102 +#define ERRNO_INVALID_DATA 103 +#define ERRNO_CHECKSUM_FAILED 104 +#define ERRNO_FAILED 105 +#define ERRNO_INVALID_PARAMETER 106 +#define ERRNO_NOMEM 107 +#define ERRNO_I2CFUN_NOT_SET 108 +#define ERRNO_UPDATE_MEMORY_FAILED 109 +#define ERRNO_DEVICE_NOT_RESET 110 +#define ERRNO_DEVICE_OUT_OF_CONTROL 111 +#define ERRNO_DEVICE_DSP_LOCKUP 112 + + diff --git a/sound/soc/rk29/Kconfig b/sound/soc/rk29/Kconfig index 64ef61cc20b7..2966744847bf 100755 --- a/sound/soc/rk29/Kconfig +++ b/sound/soc/rk29/Kconfig @@ -136,7 +136,15 @@ config SND_RK29_SOC_RT5512 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" diff --git a/sound/soc/rk29/Makefile b/sound/soc/rk29/Makefile index 892ca7143ef5..88fec2ee7f83 100755 --- a/sound/soc/rk29/Makefile +++ b/sound/soc/rk29/Makefile @@ -37,6 +37,7 @@ snd-soc-rk3026-objs := rk_rk3026.o 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 @@ -63,3 +64,4 @@ obj-$(CONFIG_SND_RK_SOC_RK2928) += snd-soc-rk2928.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 diff --git a/sound/soc/rk29/rk29_cx2070x.c b/sound/soc/rk29/rk29_cx2070x.c new file mode 100644 index 000000000000..44013aceba89 --- /dev/null +++ b/sound/soc/rk29/rk29_cx2070x.c @@ -0,0 +1,272 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 "); +MODULE_LICENSE("GPL"); -- 2.34.1