diff options
Diffstat (limited to 'sound/soc/wmt/wmt-pcm-controller.c')
-rwxr-xr-x | sound/soc/wmt/wmt-pcm-controller.c | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/sound/soc/wmt/wmt-pcm-controller.c b/sound/soc/wmt/wmt-pcm-controller.c new file mode 100755 index 00000000..dccb82c6 --- /dev/null +++ b/sound/soc/wmt/wmt-pcm-controller.c @@ -0,0 +1,419 @@ +/*++ + * linux/sound/soc/wmt/wmt-pdm-if.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 <mach/hardware.h> +#include <asm/dma.h> +#include "wmt-soc.h" +#include "wmt-pcm-controller.h" + +#define PCM_IS_MASTER_MODE +#define NULL_DMA ((dmach_t)(-1)) + +static int wmt_pcm_module_enable = 0; +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); + +static struct audio_stream_a wmt_pcm_controller_data[] = { + { + .id = "WMT PCM out", + .stream_id = SNDRV_PCM_STREAM_PLAYBACK, + .dmach = NULL_DMA, + .dma_dev = PCM_TX_DMA_REQ, + /*.dma_cfg = dma_device_cfg_table[I2S_TX_DMA_REQ],*/ + }, + { + .id = "WMT PCM in", + .stream_id = SNDRV_PCM_STREAM_CAPTURE, + .dmach = NULL_DMA, + .dma_dev = PCM_RX_DMA_REQ, + /*.dma_cfg = dma_device_cfg_table[I2S_RX_DMA_REQ],*/ + }, +}; + +struct wmt_pcm_controller +{ + int irq_no; + int pcm_clk_src; + int pcm_enable; +}; + +static struct wmt_pcm_controller wmt_pcm_controller; + +static irqreturn_t +wmt_pcm_controller_irq_handler(int irq, void *dev_id) +{ + if (PCMSR_VAL & PCMSR_TXUND) { + printk("-->PCMSR_TXUND\n"); + } + else if (PCMSR_VAL & PCMSR_RXOVR) { + printk("-->PCMSR_RXOVR\n"); + } + + PCMSR_VAL = 0x7F; //write clear all intr + return IRQ_HANDLED; +} + +static void wmt_pcm_controller_enable(void) +{ + PCMCR_VAL |= (PCMCR_PCM_ENABLE | PCMCR_DMA_EN); +} + +static void wmt_pcm_controller_disable(void) +{ + PCMCR_VAL &= ~(PCMCR_PCM_ENABLE | PCMCR_DMA_EN); +} + +static int wmt_pcm_controller_init(void) +{ + wmt_pcm_controller.irq_no = IRQ_PCM; + + // Before control pcm-module, enable pcm clock first + CLOCKEN(27); + + // set pcm_clk_source = 62464khz + //auto_pll_divisor(DEV_PCM0, CLK_ENABLE, 0, 0); + //auto_pll_divisor(DEV_PCM0, SET_PLLDIV, 1, 83333); + + wmt_pcm_controller.pcm_clk_src = auto_pll_divisor(DEV_PCM0, GET_FREQ, 0, 0); + wmt_pcm_controller.pcm_clk_src /= 1000; + wmt_pcm_controller.pcm_enable = 0; + printk("wmt_pcm_controller_init: pcm_clk_src=%d \n\r", wmt_pcm_controller.pcm_clk_src); + + // Note: you should config PIN_SHARING_SEL_4BYTE_VAL if using pcm function!!! Loon mark at 2013/4/10 + if (wmt_pcm_module_enable) { + printk("begin to configure pcm pin\n"); + /* disable GPIO and Pull Down mode */ + /* Bit1:I2SDACDAT1=PCMSYNC, Bit2:I2SDACDAT2=PCMCLK, Bit3:I2SDACDAT3=PCMIN, Bit4:I2SADCMCLK=PCMOUT */ + GPIO_CTRL_GP10_I2S_BYTE_VAL &= ~(BIT1 | BIT2 | BIT3 | BIT4); + GPIO_CTRL_GP11_I2S_BYTE_VAL &= ~(BIT1); + /*disable pull enable*/ + PULL_EN_GP10_I2S_BYTE_VAL &= ~(BIT1 | BIT2 | BIT3 | BIT4); + PULL_EN_GP11_I2S_BYTE_VAL &= ~(BIT1); + + /* set to pcm mode */ + // select PCMMCLK[bit0], PCMSYNC[bit17:16], PCMCLK[19:18], PCMIN[20], PCMOUT[22:21] + //GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT21); + GPIO_PIN_SHARING_SEL_4BYTE_VAL |= (BIT15 | BIT16 | BIT17 | BIT18 | BIT19 |BIT20); + } + + // set pcm control register + PCMCR_VAL |= (PCMCR_TXFF_RST | PCMCR_RXFF_RST); + //PCMCR_VAL |= 0x00880000; // TX/RX Fifo Threshold A + PCMCR_VAL &= ~(PCMCR_BCLK_SEL); + PCMCR_VAL &= ~PCMCR_SLAVE; // master mode + PCMCR_VAL |= PCMCR_SYNC_MODE;//short frame sync + + // set pcm format register + PCMDFCR_VAL = 0; + PCMDFCR_VAL |= (PCMDFCR_WR_AL | PCMDFCR_TX_AL | PCMDFCR_RX_AL | PCMDFCR_RD_AL); + PCMDFCR_VAL |= (PCMDFCR_TX_SZ_14 | PCMDFCR_RX_SZ_14); + //PCMDFCR_VAL |= (PCMDFCR_TX_SZ_08 | PCMDFCR_RX_SZ_08); + + // + // request irq + // + /*if (request_irq(wmt_pcm_controller.irq_no, &wmt_pcm_controller_irq_handler, IRQF_DISABLED, "wmt_pcm_controller", NULL)){ + printk(KERN_ERR "PCM_IRQ Request Failed!\n"); + } + PCMCR_VAL |= (PCMCR_IRQ_EN | PCMCR_TXUND_EN | PCMCR_RXOVR_EN); + */ + printk("fun:%s,line:%d\n",__func__,__LINE__); + return 0 ; +} + + +static int wmt_pcm_controller_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 = &wmt_pcm_controller_data[0]; + //dump_stack(); + s[stream_id].stream = substream; + runtime->private_data = s; + + return 0; +} + +static void wmt_pcm_controller_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + //dump_stack(); +} + +static int wmt_pcm_controller_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int err = 0; + int stream_id = substream->pstr->stream; + //dump_stack(); + 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) { + PCMCR_VAL |= PCMCR_TXFF_RST; // reset txfifo + PCMDFCR_VAL |= PCMDFCR_TXFM_EN; + } + else if (stream_id == SNDRV_PCM_STREAM_CAPTURE) { + PCMCR_VAL |= PCMCR_RXFF_RST; // reset rxfifo + PCMDFCR_VAL |= PCMDFCR_RXFM_EN; + } + //wmt_pcm_controller_enable(); + wmt_pcm_controller.pcm_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) { + PCMDFCR_VAL &= ~PCMDFCR_TXFM_EN; + } + else if (stream_id == SNDRV_PCM_STREAM_CAPTURE) { + PCMDFCR_VAL &= ~PCMDFCR_RXFM_EN; + } + //wmt_pcm_controller_disable(); + wmt_pcm_controller.pcm_enable--; + break; + default: + err = -EINVAL; + break; + } + + if (wmt_pcm_controller.pcm_enable) + wmt_pcm_controller_enable(); + else + wmt_pcm_controller_disable(); + + return err; +} + +static int wmt_pcm_controller_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + //dump_stack(); + return 0; +} + +static int wmt_pcm_controller_dai_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; + + byte = (runtime->sample_bits)/8; + channel = runtime->channels; + + printk(KERN_INFO "wmt_pcm_controller_dai_prepare byte = %d, channels = %d\n", byte, runtime->channels); + + /* format setting */ + if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) { + /* little or big endian check */ + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + default: + break; + } + + /* channel number check */ + switch (runtime->channels) { + case 1: + break; + case 2: + break; + default: + break; + } + } + else if (stream_id == SNDRV_PCM_STREAM_CAPTURE) { + /* little or big endian check */ + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + default: + break; + } + + /* channel number check */ + switch (runtime->channels) { + case 1: + break; + case 2: + break; + default: + break; + } + } + + switch (runtime->rate) { + case 16000: + PCMDIVR_VAL &= ~PCMCLK_DIV_MASK; + PCMDIVR_VAL |= (wmt_pcm_controller.pcm_clk_src / PCMCLK_256K); + break; + case 8000: + PCMDIVR_VAL &= ~PCMCLK_DIV_MASK; + PCMDIVR_VAL |= (wmt_pcm_controller.pcm_clk_src / PCMCLK_128K); + break; + default : + printk(KERN_ERR "not supported fs: %d \n\r", runtime->rate); + break; + } + + return 0; +} + +/* + * This must be called before _set_clkdiv and _set_sysclk since McBSP register + * cache is initialized here + */ +static int wmt_pcm_controller_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + return 0; +} + +#ifdef CONFIG_PM +static int wmt_pcm_controller_suspend(struct snd_soc_dai *cpu_dai) +{ + return 0; +} + +static int wmt_pcm_controller_resume(struct snd_soc_dai *cpu_dai) +{ + int ret = wmt_pcm_controller_init(); + if (ret) { + pr_err("Failed to init pcm module: %d\n", ret); + return ret; + } + return 0; +} +#else +#define wmt_pcm_controller_suspend NULL +#define wmt_pcm_controller_resume NULL +#endif + +static struct snd_soc_dai_ops wmt_pcm_controller_dai_ops = { + .startup = wmt_pcm_controller_dai_startup, + .prepare = wmt_pcm_controller_dai_prepare, + .shutdown = wmt_pcm_controller_dai_shutdown, + .trigger = wmt_pcm_controller_dai_trigger, + .hw_params = wmt_pcm_controller_dai_hw_params, + .set_fmt = wmt_pcm_controller_dai_set_dai_fmt, +}; + +struct snd_soc_dai_driver wmt_pcm_controller_dai = { + .suspend = wmt_pcm_controller_suspend, + .resume = wmt_pcm_controller_resume, + .playback = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &wmt_pcm_controller_dai_ops, +}; + +static int wmt_pcm_controller_probe(struct platform_device *pdev) +{ + int ret = 0; + char buf[64]; + int varlen = 64; + + ret = wmt_getsyspara("wmt.audio.pcm", buf, &varlen); + if (ret == 0) { + sscanf(buf, "%d", &wmt_pcm_module_enable); + } + + ret = wmt_pcm_controller_init(); + if (ret) { + pr_err("Failed to init pcm module: %d\n", ret); + return ret; + } + + /* register with the ASoC layers */ + ret = snd_soc_register_dai(&pdev->dev, &wmt_pcm_controller_dai); + if (ret) { + pr_err("Failed to register DAI: %d\n", ret); + return ret; + } + + wmt_pcm_controller_data[0].dma_cfg = dma_device_cfg_table[PCM_TX_DMA_REQ]; + wmt_pcm_controller_data[1].dma_cfg = dma_device_cfg_table[PCM_RX_DMA_REQ]; + + spin_lock_init(&wmt_pcm_controller_data[0].dma_lock); + spin_lock_init(&wmt_pcm_controller_data[1].dma_lock); + + return 0; +} + +static int __devexit wmt_pcm_controller_remove(struct platform_device *pdev) +{ + snd_soc_unregister_dai(&pdev->dev); + return 0; +} + +static struct platform_driver wmt_pcm_controller_driver = { + .probe = wmt_pcm_controller_probe, + .remove = __devexit_p(wmt_pcm_controller_remove), + .driver = { + .name = "wmt-pcm-controller", + .owner = THIS_MODULE, + }, +}; + + +static int __init wmt_pcm_controller_module_init(void) +{ + return platform_driver_register(&wmt_pcm_controller_driver); +} + +static void __exit wmt_pcm_controller_module_exit(void) +{ + platform_driver_unregister(&wmt_pcm_controller_driver); +} + +module_init(wmt_pcm_controller_module_init); +module_exit(wmt_pcm_controller_module_exit); + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("WMT [ALSA SoC] driver"); +MODULE_LICENSE("GPL"); + |