summaryrefslogtreecommitdiff
path: root/ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c
diff options
context:
space:
mode:
authorKevin2014-11-15 10:00:36 +0800
committerKevin2014-11-15 10:00:36 +0800
commit9d40ac5867b9aefe0722bc1f110b965ff294d30d (patch)
treede942df665fac4bac0d9cb7ae86910fe937b0c1a /ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c
parent392e8802486cb573b916e746010e141a75f507e6 (diff)
downloadFOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.tar.gz
FOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.tar.bz2
FOSSEE-netbook-kernel-source-9d40ac5867b9aefe0722bc1f110b965ff294d30d.zip
add via modify part source code for wm8880 4.4 kitkat
Diffstat (limited to 'ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c')
-rwxr-xr-xANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c1105
1 files changed, 1105 insertions, 0 deletions
diff --git a/ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c b/ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c
new file mode 100755
index 00000000..643d25e2
--- /dev/null
+++ b/ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c
@@ -0,0 +1,1105 @@
+/*++
+ * linux/sound/soc/wmt/wmt-i2s.c
+ * WonderMedia I2S audio driver for ALSA
+ *
+ * Copyright c 2010 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include <linux/suspend.h>
+
+#include <mach/hardware.h>
+#include <asm/dma.h>
+#include "wmt-soc.h"
+#include "wmt-pcm.h"
+
+#define NULL_DMA ((dmach_t)(-1))
+#define AUD_SPDIF_ENABLE 1
+
+static int wmt_i2s_output = 0; //hdmi enable or not 2013-9-3 //maybe can remove it 2013-10-24
+static int wmt_codec_wm8994 = 0; //env set 2013-9-3
+
+static int gpio_pa = -1;
+static int gpio_active = 0;
+extern int wmt_gpio_setpull(unsigned int gpio, enum wmt_gpio_pulltype pull);
+/*
+ * Debug
+ */
+#define AUDIO_NAME "WMT_I2S"
+//#define WMT_I2S_DEBUG 1
+//#define WMT_I2S_DEBUG_DETAIL 1
+
+#ifdef WMT_I2S_DEBUG
+#define DPRINTK(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#else
+#define DPRINTK(format, arg...) do {} while (0)
+#endif
+
+#ifdef WMT_I2S_DEBUG_DETAIL
+#define DBG_DETAIL(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": [%s]" format "\n" , __FUNCTION__, ## arg)
+#else
+#define DBG_DETAIL(format, arg...) do {} while (0)
+#endif
+
+#define err(format, arg...) \
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
+
+struct wmt_i2s_asoc_data {
+ int stream_id;
+ unsigned int bus_id;
+ struct i2s_s i2s;
+ /*
+ * Flags indicating is the bus already activated and configured by
+ * another substream
+ */
+ unsigned char HDMI_and_DAC0; //new env set to determine
+ unsigned char HDMI_aud_enable; //same with global wmt_i2s_output
+
+ int active;
+ int configured;
+ unsigned char CH_SEL_NUM;
+ unsigned char HDMI_SPDIF_STATE;
+ /*struct timer_list delay_timer;*/
+ struct audio_stream_a s[2];
+};
+static struct timer_list pa_timer;
+
+static int audio_interface_mode = 0;//add 2014-6-24 i2s left right
+
+static int wmt_pdm_module_enable = 0;
+extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
+
+#define to_i2s_data(priv) container_of((priv), struct wmt_i2s_asoc_data, bus_id)
+#define SNDRV_PCM_STREAM_ALL 2
+
+static void i2s_init(int mode);
+static void i2s_exit(void);
+
+#ifdef CONFIG_FB_WMT
+extern int vpp_set_audio(int format, int sample_rate, int channel);
+#endif
+
+
+static struct wmt_i2s_asoc_data i2s_data[NUM_LINKS] = {
+ {
+ .bus_id = 0, //?? 2 SNDRV_PCM_STREAM_ALL
+ .i2s = {
+ /* interrupt counters */
+ {0, 0, 0, 0, 0, 0, 0, 0},
+ /* irq number*/
+ 0,
+ /* reference counter */
+ 0,
+ /* channels */
+ 0,
+ /* format */
+ 0,
+ /* fragment size */
+ 0,
+ /* sample rate */
+ 0,
+ i2s_init,
+ i2s_exit,
+ },
+ .s = {
+ {
+ .id = "WMT I2S out",
+ .stream_id = SNDRV_PCM_STREAM_PLAYBACK,
+ .dmach = NULL_DMA,
+ .dma_dev = AHB1_AUD_DMA_REQ_1,
+ /*.dma_cfg = dma_device_cfg_table[I2S_TX_DMA_REQ],*/
+ },
+ {
+ .id = "WMT I2S in",
+ .stream_id = SNDRV_PCM_STREAM_CAPTURE,
+ .dmach = NULL_DMA,
+ .dma_dev = AHB1_AUD_DMA_REQ_0,
+ /*.dma_cfg = dma_device_cfg_table[I2S_RX_DMA_REQ],*/
+ },
+ },
+ .HDMI_aud_enable = 0,
+ .HDMI_and_DAC0 = 0,
+ .CH_SEL_NUM = 2,
+ .HDMI_SPDIF_STATE = 6,//hdmi spdif play at sametime
+ },
+};
+
+
+/* for HDMI Audio status */
+int hd_audio_flag = 0;
+
+
+
+#ifdef CONFIG_WMT_I2S_INT
+/* wmt_i2s_interrupt()
+ *
+ * It's only interrupt counter now, might be useful to
+ * debug or benchmark.
+ */
+static irqreturn_t
+wmt_i2s_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ DBG_DETAIL();
+
+ return IRQ_HANDLED;
+}
+#endif
+
+#if 0
+static void delay_timer_handler(unsigned long data)
+{
+ DBG_DETAIL();
+}
+#endif
+
+static void pa_timer_handler(unsigned long data)
+{
+ printk("%s\n", __func__);
+ if (gpio_pa >= 0)
+ {
+
+ gpio_direction_output(gpio_pa, gpio_active);
+ }
+}
+
+void wmt_i2s_ch_config(void)
+{
+ /* CH_SEL_NUM = 0, mesns output L to SPDIF&DAC's L and R
+ 1, mesns output R to SPDIF&DAC's L and R
+ 2, means normal stereo output to SPDIF&DAC */
+ switch (i2s_data->CH_SEL_NUM) {
+ case 0:
+ ASMPFCHCFG0_VAL = 0x10100000;
+ ASMPF2HDACHCFG_VAL = 0x76543200;
+ break;
+ case 1:
+ ASMPFCHCFG0_VAL = 0x10101111;
+ ASMPF2HDACHCFG_VAL = 0x76543211;
+ break;
+ case 2:
+ ASMPFCHCFG0_VAL = 0x10101010;
+ ASMPF2HDACHCFG_VAL = 0x76543210;
+ break;
+ }
+
+ if (i2s_data->i2s.channels == 0x01) {
+ ASMPFCHCFG0_VAL = 0x10100000;
+ ASMPF2HDACHCFG_VAL = 0x76543200;
+ }
+
+ if (i2s_data->HDMI_aud_enable) {
+ /* disable DAC#0, if HDMI Audio is enabled */
+ ASMPFCHCFG0_VAL &= 0xFFFF00FF;
+ ASMPFCHCFG0_VAL |= 0x00009800;
+ }
+
+ /* HDMI_SPDIF_STATE = 3, means disable HDMI&SPDIF
+ 4, means enable HDMI only
+ 5, means enable SPDIF only
+ 6, means enable HDMI&SPDIF as normal */
+ switch (i2s_data->HDMI_SPDIF_STATE) {
+ case 3:
+ ASMPFCHCFG0_VAL &= 0xFFFFFF00;
+ ASMPFCHCFG0_VAL |= 0x00000098;
+ ASMPF2HDACHCFG_VAL &= 0xFFFFFF00;
+ ASMPF2HDACHCFG_VAL |= 0x00000076;
+ break;
+ case 4:
+ ASMPFCHCFG0_VAL &= 0xFFFFFF00;
+ ASMPFCHCFG0_VAL |= 0x00000098;
+ break;
+ case 5:
+ ASMPF2HDACHCFG_VAL &= 0xFFFFFF00;
+ ASMPF2HDACHCFG_VAL |= 0x00000076;
+ break;
+ }
+}
+
+
+void wmt_i2s_dac0_ctrl(int HDMI_audio_enable)
+{
+ /* check if output to HDMI audio and DAC0 at same time */
+ if (i2s_data->HDMI_and_DAC0)
+ return;
+
+ if (HDMI_audio_enable) {
+ i2s_data->HDMI_aud_enable = 1;
+ info("HDMI Audio is enabled, disable dac0 of I2S");
+ }
+ else {
+ i2s_data->HDMI_aud_enable = 0;
+ info("HDMI Audio is disabled, enable dac0 of I2S");
+ }
+
+ wmt_i2s_ch_config();
+
+ info("CHCFG0=0x%x, HDACHCFG=0x%x", ASMPFCHCFG0_VAL, ASMPF2HDACHCFG_VAL);
+}
+EXPORT_SYMBOL(wmt_i2s_dac0_ctrl);
+void wmt_i2s_ch_sel(int ch_sel_num)
+{
+ if (ch_sel_num < 3)
+ i2s_data->CH_SEL_NUM = ch_sel_num;
+ else
+ i2s_data->HDMI_SPDIF_STATE = ch_sel_num;
+
+ wmt_i2s_ch_config();
+
+
+ info("SEL: CHCFG0=0x%x, HDACHCFG=0x%x", ASMPFCHCFG0_VAL, ASMPF2HDACHCFG_VAL);
+}
+EXPORT_SYMBOL(wmt_i2s_ch_sel);
+
+static void i2s_disable(void)
+{
+ DBG_DETAIL();
+
+ DACCFG_VAL &= ~DACITF_ENABLE;
+ ASMPFCFG_VAL &=~ASMPF_ENABLE;
+ HDACKGEN_VAL &= ~HDACKGEN_ENABLE;
+
+#ifdef AUD_SPDIF_ENABLE
+ DGOCFG_VAL &= ~DGOITF_ENABLE;
+#endif
+
+ AADCF0CFG_VAL &= ~(AADCITF_ENABLE | AADCF_ENABLE);
+}
+
+static void i2s_enable(void)
+{
+ DBG_DETAIL();
+
+ AADCF0CFG_VAL |= (AADCITF_ENABLE | AADCF_ENABLE);
+ ASMPFCFG_VAL |= (ASMPF_ENABLE);
+
+#ifdef AUD_SPDIF_ENABLE
+ DGOCFG_VAL |= DGOITF_ENABLE;
+#endif
+
+ DACCFG_VAL |= DACITF_ENABLE;
+
+ if (hd_audio_flag)
+ HDACKGEN_VAL |= HDACKGEN_ENABLE;
+}
+
+static void aud_audprf_setting(unsigned char smp_rate_index)
+{
+ unsigned long cfg_tbl[] = {0x00002001, 0x00002001, 0x00002001,
+ 0x00002001, 0x00002001, 0x00002001, 0x00002001, 0x00002001,
+ 0x00002001, 0x00002001, 0x00002001, 0x00002001,
+ 0x00000001, 0x00000001, 0x00000001
+ };
+ unsigned int dgo_tbl[] = {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00};
+ unsigned int sysclk_tbl[] = {4096, 5632, 6144, 8192, 11264, 12288, 16384,
+ 22579, 24576, 32768, 45056, 49152, 16384, 22528, 24576};
+#ifdef AUD_SPDIF_ENABLE
+ unsigned int dgocs_tbl[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x02, 0x00, 0x08, 0x0A, 0x00, 0x0C, 0x0E};
+#endif
+ //unsigned int clock = 0;
+ unsigned long aud_reg_val;
+
+ DBG_DETAIL();
+
+ ADCCFG_VAL = cfg_tbl[smp_rate_index];
+
+ aud_reg_val = dgo_tbl[smp_rate_index];
+ DGOCFG_VAL = aud_reg_val;
+ HDACKGEN_VAL = aud_reg_val;
+
+#ifdef AUD_SPDIF_ENABLE
+ /* set ADGO channel status */
+ aud_reg_val = dgocs_tbl[smp_rate_index] << 24;
+ DGOCS0A_VAL = aud_reg_val;
+ DGOCS1A_VAL = aud_reg_val;
+ //info("%s : DGOCS01A_VAL=0x%x", __func__, (unsigned int)aud_reg_val);
+#endif
+
+ DACCFG_VAL = cfg_tbl[smp_rate_index];
+
+ auto_pll_divisor(DEV_I2S, CLK_ENABLE , 0, 0);
+ auto_pll_divisor(DEV_I2S, SET_PLLDIV, 1, sysclk_tbl[smp_rate_index]);
+ /*clock = auto_pll_divisor(DEV_I2S, GET_FREQ , 0, 0);
+ info("%s : clock=%d", __func__, clock);*/
+}
+
+static unsigned char aud_smp_rate_convert(unsigned int smp_rate)
+{
+ unsigned char i = 0;
+ unsigned int smp_rate_tbl[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000,
+ 44100, 48000, 64000, 88200, 96000, 128000, 176000, 192000};
+
+ DBG_DETAIL();
+
+ /* boundary checking */
+ if (smp_rate < 8000) {
+ i = 0x00;
+ }
+ else if (smp_rate > 192000) {
+ i = 0x0E;
+ }
+ else {
+ for (i = 0; i < 0x1F; i++) {
+ if (smp_rate == smp_rate_tbl[i]) {
+ break;
+ }
+ else if (smp_rate < smp_rate_tbl[i + 1]) {
+ if (smp_rate < (smp_rate_tbl[i] + ((smp_rate_tbl[i + 1] - smp_rate_tbl[i]) / 2))) {
+ break;
+ }
+ else {
+ i++;
+ break;
+ }
+ }
+ }
+ }
+
+ return i;
+
+}
+
+static void i2s_sample_rate(unsigned int rate)
+{
+ unsigned char rate_index;
+
+ DBG_DETAIL("rate=%d", rate);
+
+ if (rate == i2s_data->i2s.rate)
+ return;
+
+ i2s_data->i2s.rate = rate;
+
+ rate_index = aud_smp_rate_convert(rate);
+
+ aud_audprf_setting(rate_index);
+
+#ifdef CONFIG_FB_WMT
+ /* pass information of audio to HDMI Audio */
+ hd_audio_flag = vpp_set_audio(16, rate, 2);
+#endif
+}
+
+static int i2sdacdat_gpio = -1;//means don't touch GP10 bit0-3 i2sDacDat0-3
+
+void wmt_set_i2s_share_pin(void)
+{
+ int pwren_num,active_level,gpio_int_num;
+ char buf[256];
+ int varlen = 256;
+ static int gpio26_mux = -1;
+ unsigned int gpval = 0x0;
+
+ if(gpio26_mux == -1){
+ if(wmt_getsyspara("wmt.bt.mtk6622",buf,&varlen) == 0)
+ {
+ sscanf(buf,"%d:%d:%d",&pwren_num,&active_level,&gpio_int_num);
+ printk("use customized value:p%d,a%d,i%d\n",pwren_num,active_level,gpio_int_num);
+ if(pwren_num == 62){
+ gpio26_mux = 0x01;
+ }else{
+ gpio26_mux = 0x00;
+ }
+
+ }else{
+ gpio26_mux = 0x00;
+ }
+ }
+ printk("%s gpio26_mux:%d\n",__func__,gpio26_mux);
+ if(!gpio26_mux){
+ /* disable GPIO and enable Pull Down mode */
+ gpval &= ~0xff;
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL &= ~0xFF; // 0xFF bit1
+ }else{
+ gpval &= ~0xF7;
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL &= ~0xF7;
+ }
+
+ // configure I2SDACDAT1/2/3 as GPIO function but mmax-wm8994 that configured as I2SADCLRC/I2SADCBLCK
+ if (!wmt_codec_wm8994)
+ {
+ gpval |= (BIT1 | BIT2 | BIT3);
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL |= (BIT1 | BIT2 | BIT3);
+
+ }
+
+ //add for mainly i2sdacdat1 work as gpio to control led 2014-8-15 so here just keep it specific func
+ //tp driver will use it as gpio
+ switch (i2sdacdat_gpio)
+ {
+ case 1:
+ gpval |= BIT1;
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL |= BIT1; // i2sdacdat1 work as gpio
+ break;
+ case 2:
+ gpval |= BIT2;
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL |= BIT2;
+ break;
+ case 3:
+ gpval |= BIT3;
+ //GPIO_CTRL_GP10_I2S_BYTE_VAL |= BIT3;
+ break;
+
+ }
+ GPIO_CTRL_GP10_I2S_BYTE_VAL = gpval;
+
+ GPIO_CTRL_GP11_I2S_BYTE_VAL &= ~(BIT0 | BIT1 | BIT2);
+
+ if(!gpio26_mux){
+ PULL_EN_GP10_I2S_BYTE_VAL |= 0xFF;
+ }else{
+ PULL_EN_GP10_I2S_BYTE_VAL |= 0xF7;
+ }
+ PULL_EN_GP11_I2S_BYTE_VAL |= (BIT0 | BIT1 | BIT2);
+
+ if(!gpio26_mux){
+ /* set to 2ch input, 2ch output */
+ PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT15 | BIT17 | BIT19 | BIT20);
+ PIN_SHARING_SEL_4BYTE_VAL |= (BIT1 | BIT16 | BIT18);
+ }else{
+ PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT15 | BIT17 | BIT20);
+ PIN_SHARING_SEL_4BYTE_VAL |= (BIT1 | BIT16);
+ }
+}
+EXPORT_SYMBOL(wmt_set_i2s_share_pin);
+
+static void i2s_init(int mode)
+{
+#ifdef DEBUG
+ int ret;
+#endif
+ int temp ;
+ //unsigned int clock = 0;
+
+ DPRINTK("i2s_ref = %d ", i2s_data->i2s.ref);
+
+ if (++i2s_data->i2s.ref > 1)
+ return;
+
+ DBG_DETAIL();
+
+ if (!mode) {
+ /* set to 24.576MHz */
+ auto_pll_divisor(DEV_I2S, CLK_ENABLE , 0, 0);
+ auto_pll_divisor(DEV_I2S, SET_PLLDIV, 1, 24576);
+ /*clock = auto_pll_divisor(DEV_I2S, GET_FREQ , 0, 0);
+ info("%s : clock=%d \n" , __func__, clock);*/
+
+ /* Enable BIT4:ARFP clock, BIT3:ARF clock */
+ PMCEU_VAL |= (BIT4 | BIT3);
+
+ /* Enable BIT2:AUD clock */
+ PMCE3_VAL |= BIT2;
+
+ wmt_set_i2s_share_pin();
+ }
+
+ /* connect DZDRQ8 to ADC0 FIFO, DZDRQ9 to DAC FIFO and DZDRQA to ADC1 FIFO */
+ DZDRQ8_CFG_VAL = 0x0;
+ DZDRQ9_CFG_VAL = 0x1;
+ DZDRQA_CFG_VAL = 0x2;
+
+ DACCFG_VAL = 0x0;
+ ADCCFG_VAL = 0x0;
+
+ /* little endian, signed format, enable sample FIFO, 16bit sample, 2 channel */
+ ASMPFCFG_VAL = 0x52;
+
+ /* assign ch0 to DAC#0_L, DAC#1_L, DAC#2_L, SPDIF_L,
+ ch1 to DAC#0_R, DAC#1_R, DAC#2_R, SPDIF_R */
+ ASMPFCHCFG0_VAL = 0x10101010;
+
+ /* assign ch0 to DAC#3_L, ch1 to DAC#3_R */
+ ASMPFCHCFG1_VAL = 0x10;
+
+ /* select ADCDAT0, 16 bits mode, enable AADCFIFO and AADCITF */
+ AADCF0CFG_VAL = (AADCITF_ENABLE | AADCF16_ENABLE | AADCF_ENABLE);
+
+ /* the sequence must be ARF_ADCCFG first then ARF_DGOCFG, finally ARF_DACCFG while slave mode,
+ otherwise will generate noise when record function is active */
+
+ /* ADC slave mode, 48K sample rate, I2S mode */
+ ADCCFG_VAL = 0x2001;
+
+ i2s_data->i2s.rate = 48000;
+ i2s_data->i2s.channels = 2;
+ i2s_data->i2s.format = SNDRV_PCM_FORMAT_S16_LE;
+
+#ifdef AUD_SPDIF_ENABLE
+ /* B1: 0 -> PCM, 1 -> Bitstream
+ B24 ~ B27: sample rate */
+ /* set ADGO channel status for 48K sample rate */
+ DGOCS0A_VAL = 0x02 << 24;
+ DGOCS1A_VAL = 0x02 << 24;
+
+ /* enable ADGOITF and ADGOCKGEN for 48K sample rate */
+ DGOCFG_VAL = 0x82;
+#endif
+
+ /* enable ADACITF and ADACCKGEN for 44.1K sample rate, I2S mode */
+ DACCFG_VAL = 0x402001;
+ if (audio_interface_mode == 1)
+ {
+ printk("<<<<<%s left justified!\n", __func__);
+ //left justified bit20: 0->i2s, 1->l/r clock polarity
+ DACCFG_VAL |= 0x1 << 20;
+ //bit7:0 padding added to dac serial data output
+ //0 = LJ
+ //1 = I2S
+ //others = RJ (depends on Bit 11:8)
+
+ DACCFG_VAL &= ~(0x1<<0);
+
+ }
+
+ /* enable HD Audio clock for 48K sample rate or not*/
+ HDACKGEN_VAL = 0x12;
+
+#ifdef CONFIG_FB_WMT
+ /* pass information of audio to HDMI Audio */
+ hd_audio_flag = vpp_set_audio(16, i2s_data->i2s.rate, i2s_data->i2s.channels);
+ ASMPF2HDACHCFG_VAL = 0x76543210;
+#endif
+
+ if (!hd_audio_flag)
+ HDACKGEN_VAL = 0;
+
+ /* audio peri reset */
+ temp = AUDPRFRST_VAL;
+
+#ifdef AUD_SPDIF_ENABLE
+ temp |= (ASMPF_RESET | DACITF_RESET | ADCITF_RESET | DGOITF_RESET);
+#else
+ temp |= (ASMPF_RESET | DACITF_RESET | ADCITF_RESET);
+#endif
+
+ AUDPRFRST_VAL = temp;
+
+ /*
+ request irq
+ */
+#ifdef CONFIG_WMT_I2S_INT
+ ret = request_irq(i2s_data->i2s.irq,
+ wmt_i2s_interrupt,
+ SA_INTERRUPT,
+ "wmt_alsa_vt1602",
+ NULL);
+ if (ret)
+ printk("%s : unable to request IRQ \n" , __func__);
+#endif
+}
+
+static void i2s_exit(void)
+{
+ DBG_DETAIL();
+
+ if (--i2s_data->i2s.ref)
+ return;
+
+ DPRINTK("Do i2s_exit ");
+
+#ifdef CONFIG_WMT_I2S_INT
+ free_irq(i2s_data->i2s.irq, NULL);
+#endif
+
+ /* Reset counter.*/
+ memset(&i2s_data->i2s.ints, 0, sizeof(struct i2s_ints_s));
+ return;
+}
+
+static int wmt_i2s_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream_id = substream->pstr->stream;
+ struct audio_stream_a *s = &i2s_data->s[0];
+
+ DBG_DETAIL();
+
+ s[stream_id].stream = substream;
+
+ runtime->private_data = s;
+
+ return 0;
+}
+
+static void wmt_i2s_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ DBG_DETAIL();
+}
+
+static int wmt_i2s_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int err = 0;
+ int stream_id = substream->pstr->stream;
+
+ DBG_DETAIL();
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ ASMPFCFG_VAL |= ASMPF_ENABLE;
+ }
+ else if (stream_id == SNDRV_PCM_STREAM_CAPTURE) {
+ AADCF0CFG_VAL |= AADCF_ENABLE;
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ ASMPFCFG_VAL &= ~ASMPF_ENABLE;
+ }
+ else if (stream_id == SNDRV_PCM_STREAM_CAPTURE) {
+ AADCF0CFG_VAL &= ~AADCF_ENABLE;
+ }
+
+ /*
+ mod_timer(&i2s_data->delay_timer, jiffies + HZ / 100);
+ */
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static int wmt_i2s_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ DBG_DETAIL();
+ return 0;
+}
+
+static int wmt_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int channel, byte;
+ int stream_id = substream->pstr->stream;
+
+#ifdef CONFIG_SND_OSSEMUL
+ //info("oss.rate=%d, oss.channels=%d", runtime->oss.rate, runtime->oss.channels);
+#else
+ DBG_DETAIL();
+#endif
+
+ byte = (runtime->sample_bits)/8;
+ channel = runtime->channels;
+
+ DPRINTK("snd_wmt_alsa_prepare byte = %d, channels = %d", byte, runtime->channels);
+
+
+ if ((runtime->rate != i2s_data->i2s.rate) || (runtime->format != i2s_data->i2s.format) ||
+ (runtime->channels != i2s_data->i2s.channels) || (stream_id != i2s_data->stream_id)) {
+ info("*** stream_id=%d, rate=%d, format=0x%x channels=%d ***",
+ stream_id, runtime->rate, runtime->format, runtime->channels);
+ i2s_data->i2s.format = runtime->format;
+ i2s_data->i2s.channels = runtime->channels;
+ i2s_data->stream_id = stream_id;
+ }
+ else {
+ return 0;
+ }
+
+ i2s_disable();
+
+ /* format setting */
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* little or big endian check */
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_BE:
+ case SNDRV_PCM_FORMAT_U16_BE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ case SNDRV_PCM_FORMAT_U24_BE:
+ case SNDRV_PCM_FORMAT_S32_BE:
+ case SNDRV_PCM_FORMAT_U32_BE:
+ ASMPFCFG_VAL |= ASMPF_EXCH_ENDIAN;
+ break;
+ default:
+ ASMPFCFG_VAL &= ~ASMPF_EXCH_ENDIAN;
+ break;
+ }
+
+ /* unsigned or signed check */
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_U16_BE:
+ case SNDRV_PCM_FORMAT_U24_LE:
+ case SNDRV_PCM_FORMAT_U24_BE:
+ case SNDRV_PCM_FORMAT_U32_LE:
+ case SNDRV_PCM_FORMAT_U32_BE:
+ ASMPFCFG_VAL |= ASMPF_EXCH_FMT;
+ break;
+ default:
+ ASMPFCFG_VAL &= ~ASMPF_EXCH_FMT;
+ break;
+ }
+
+ /* sample quantization check */
+ ASMPFCFG_VAL &= ~(BIT4 | BIT5);
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S8:
+ case SNDRV_PCM_FORMAT_U8:
+ ASMPFCFG_VAL |= ASMPF_8BIT_SMP;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S16_BE:
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_U16_BE:
+ ASMPFCFG_VAL |= ASMPF_16BIT_SMP;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S32_BE:
+ case SNDRV_PCM_FORMAT_U32_LE:
+ case SNDRV_PCM_FORMAT_U32_BE:
+ ASMPFCFG_VAL |= ASMPF_32BIT_SMP;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ case SNDRV_PCM_FORMAT_U24_LE:
+ case SNDRV_PCM_FORMAT_U24_BE:
+ info("*** Not Supported: fmt=24Bit ***");
+ default:
+ break;
+ }
+
+ /* channel number check */
+ ASMPFCFG_VAL &= ~(BIT0 | BIT1 | BIT2 | BIT3);
+ ASMPFCFG_VAL |= runtime->channels;
+
+ wmt_i2s_ch_config();
+ info("Prepare: CHCFG0=0x%x, HDACHCFG=0x%x", ASMPFCHCFG0_VAL, ASMPF2HDACHCFG_VAL);
+ }
+
+ /* sample rate setting */
+#ifdef CONFIG_SND_OSSEMUL
+ if (runtime->oss.rate) {
+ i2s_sample_rate(runtime->oss.rate);
+ }
+ else {
+ i2s_sample_rate(runtime->rate);
+ }
+#else
+ i2s_sample_rate(runtime->rate);
+#endif
+
+ i2s_enable();
+
+ /*
+ printk("avail_max=%d, rate=%d, channels=%d, period_size=%d, periods=%d, buffer_size=%d, tick_time=%d, \
+ min_align=%d, byte_align=%d, frame_bits=%d, sample_bits=%d, sleep_min=%d, xfer_align=%d, boundary=%d\n",
+ runtime->avail_max, runtime->rate, runtime->channels, runtime->period_size, runtime->periods,
+ runtime->buffer_size, runtime->tick_time, runtime->min_align, runtime->byte_align,
+ runtime->frame_bits, runtime->sample_bits,
+ runtime->sleep_min, runtime->xfer_align, runtime->boundary);
+ */
+ return 0;
+}
+
+/*
+ * This must be called before _set_clkdiv and _set_sysclk since McBSP register
+ * cache is initialized here
+ */
+static int wmt_i2s_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ DBG_DETAIL();
+ return 0; //add rambo 2013-3-13
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ /* Unsupported data format */
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set bit clock (CLKX/CLKR) and FS polarities */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /*
+ * Normal BCLK + FS.
+ * FS active low. TX data driven on falling edge of bit clock
+ * and RX data sampled on rising edge of bit clock.
+ */
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wmt_i2s_suspend(struct snd_soc_dai *cpu_dai)
+{
+ printk("%s!\n", __func__);
+
+ DBG_DETAIL();
+
+ i2s_data->i2s.ref = 0;
+#if 1
+ if (gpio_pa >= 0)
+ {
+ gpio_direction_output(gpio_pa, !gpio_active);
+ }
+#endif
+ return 0;
+}
+
+static int wmt_i2s_resume(struct snd_soc_dai *cpu_dai)
+{
+ printk("%s!\n", __func__);
+
+ DBG_DETAIL();
+
+ i2s_init(1);
+
+ wmt_i2s_ch_config();
+#if 1
+ if (gpio_pa >= 0)
+ {
+ gpio_direction_output(gpio_pa, gpio_active);
+ mdelay(50);
+ }
+#endif
+ info("Resume: CHCFG0=0x%x, HDACHCFG=0x%x", ASMPFCHCFG0_VAL, ASMPF2HDACHCFG_VAL);
+ return 0;
+}
+#else
+#define wmt_i2s_suspend NULL
+#define wmt_i2s_resume NULL
+#endif
+
+static struct snd_soc_dai_ops wmt_i2s_dai_ops = {
+ .startup = wmt_i2s_dai_startup,
+ .prepare = wmt_i2s_prepare,
+ .shutdown = wmt_i2s_dai_shutdown,
+ .trigger = wmt_i2s_dai_trigger,
+ .hw_params = wmt_i2s_dai_hw_params,
+ .set_fmt = wmt_i2s_dai_set_dai_fmt,
+};
+
+struct snd_soc_dai_driver wmt_i2s_dai = {
+ .suspend = wmt_i2s_suspend,
+ .resume = wmt_i2s_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_FLOAT,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &wmt_i2s_dai_ops,
+};
+
+static int wmt_i2s_probe(struct platform_device *pdev)
+{
+ int ret;
+ char buf[64];
+ int varlen = 64;
+
+ DBG_DETAIL();
+
+ ret = wmt_getsyspara("wmt.audio.pcm", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d", &wmt_pdm_module_enable);
+ }
+
+ ret = wmt_getsyspara("wmt.audio.i2s", buf, &varlen);
+ if (ret == 0) {
+ if (!strncmp(buf, "wm8994", strlen("wm8994")))
+ wmt_codec_wm8994 = 1;
+ }
+ memset(buf, 0, sizeof(buf));
+ ret = wmt_getsyspara("wmt.audio.codechdmi", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d", &i2s_data[0].HDMI_and_DAC0);
+ printk("<<<%s codec and hdmi:%d\n", __FUNCTION__, i2s_data[0].HDMI_and_DAC0);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ //0:i2sdacdat0(usually use i2s function!!) 1:i2sdacdat1 2:i2sdacdat2 3:i2sdacdat3
+ ret = wmt_getsyspara("wmt.audio.dacdat.gpio", buf, &varlen);
+ if (ret == 0) {
+ sscanf(buf, "%d", &i2sdacdat_gpio);
+ printk("<<<%s i2sdacdat_gpio:%d\n", __FUNCTION__, i2sdacdat_gpio);
+ }
+
+
+ memset(buf, 0, sizeof(buf));
+ ret = wmt_getsyspara("wmt.audio.interface.mode", buf, &varlen);
+ if (ret == 0) { // 0:i2s 1:left justified 2:right justified
+ sscanf(buf, "%d", &audio_interface_mode);
+ printk("<<<%s audio_interface_mode:%d\n", __FUNCTION__, audio_interface_mode);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ ret = wmt_getsyspara("wmt.audio.pa", buf, &varlen);
+ if (ret == 0) { // gpio nr:active---> 1:0
+ sscanf(buf, "%d:%d", &gpio_pa, &gpio_active);
+
+ }
+ printk("%s audio pa:%d:%d\n", __FUNCTION__, gpio_pa, gpio_active);
+ if (gpio_pa >= 0)
+ {
+ ret = gpio_request(gpio_pa, "audio pa");
+ if (ret)
+ {
+ printk("%s gpio %d request error!\n", __func__, gpio_pa);
+ return ret;
+ }
+ #if 0
+ if (gpio_active)
+ wmt_gpio_setpull(gpio_pa, WMT_GPIO_PULL_DOWN);
+ else
+ wmt_gpio_setpull(gpio_pa, WMT_GPIO_PULL_UP);
+ msleep(10);
+ gpio_direction_output(gpio_pa, !gpio_active); //???pop???
+ printk("%s shutdown pa!\n", __func__);
+ msleep(100);
+ #endif
+
+
+ init_timer(&pa_timer);
+ pa_timer.function = pa_timer_handler;
+ }
+ i2s_data->s[0].dma_cfg = dma_device_cfg_table[AHB1_AUD_DMA_REQ_1];
+ i2s_data->s[1].dma_cfg = dma_device_cfg_table[AHB1_AUD_DMA_REQ_0];
+ /*init i2s controller*/
+ i2s_data->i2s.init(0);
+
+ spin_lock_init(&i2s_data->s[0].dma_lock);
+ spin_lock_init(&i2s_data->s[1].dma_lock);
+ /*init_timer(&i2s_data->delay_timer);
+ i2s_data->delay_timer.function = delay_timer_handler;*/
+
+ /* register with the ASoC layers */
+ ret = snd_soc_register_dai(&pdev->dev, &wmt_i2s_dai);
+ if (ret) {
+ pr_err("Failed to register DAI: %d\n", ret);
+ return ret;
+ }
+
+ if (gpio_pa >= 0)
+ {
+ mod_timer(&pa_timer, jiffies+msecs_to_jiffies(10000));
+ }
+ return 0;
+}
+
+static int __devexit wmt_i2s_remove(struct platform_device *pdev)
+{
+ DBG_DETAIL();
+
+ snd_soc_unregister_dai(&pdev->dev);
+
+ return 0;
+}
+
+static void wmt_i2s_plat_shutdown(struct platform_device *pdev)
+{
+ printk("%s!\n", __func__);
+ if (gpio_pa >= 0)
+ {
+ gpio_direction_output(gpio_pa, !gpio_active);
+ }
+}
+
+static struct platform_driver wmt_i2s_driver = {
+ .probe = wmt_i2s_probe,
+ .remove = __devexit_p(wmt_i2s_remove),
+ //.suspend = wmt_i2s_plat_suspend,
+ //.resume = wmt_i2s_plat_resume,
+ .shutdown = wmt_i2s_plat_shutdown,
+ .driver = {
+ .name = "wmt-i2s",
+ .owner = THIS_MODULE,
+ },
+};
+
+
+static int __init wmt_i2s_init(void)
+{
+ DBG_DETAIL();
+
+ return platform_driver_register(&wmt_i2s_driver);
+}
+
+static void __exit wmt_i2s_exit(void)
+{
+ DBG_DETAIL();
+
+ platform_driver_unregister(&wmt_i2s_driver);
+}
+
+module_init(wmt_i2s_init);
+module_exit(wmt_i2s_exit);
+
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_DESCRIPTION("WMT [ALSA SoC] driver");
+MODULE_LICENSE("GPL");
+