From 9d40ac5867b9aefe0722bc1f110b965ff294d30d Mon Sep 17 00:00:00 2001 From: Kevin Date: Sat, 15 Nov 2014 10:00:36 +0800 Subject: add via modify part source code for wm8880 4.4 kitkat --- ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c | 1105 +++++++++++++++++++++++++++++++++ 1 file changed, 1105 insertions(+) create mode 100755 ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c (limited to 'ANDROID_3.4.5/sound/soc/wmt/wmt-i2s.c') 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 . + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#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"); + -- cgit