summaryrefslogtreecommitdiff
path: root/sound/soc/wmt/wmt-pcm-controller.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/wmt/wmt-pcm-controller.c')
-rwxr-xr-xsound/soc/wmt/wmt-pcm-controller.c419
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");
+