diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 0d11128..1648b5d 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -124,6 +124,10 @@ struct snd_pcm_ops { #define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */ #define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */ #define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */ +#define SNDRV_PCM_RATE_352800 (1<<13) /* 352800Hz */ +#define SNDRV_PCM_RATE_384000 (1<<14) /* 384000Hz */ +#define SNDRV_PCM_RATE_705600 (1<<15) /* 705600Hz */ +#define SNDRV_PCM_RATE_768000 (1<<16) /* 768000Hz */ #define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */ #define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */ @@ -136,6 +140,10 @@ struct snd_pcm_ops { SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000) #define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\ SNDRV_PCM_RATE_192000) +#define SNDRV_PCM_RATE_8000_384000 (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_352800|\ + SNDRV_PCM_RATE_384000) +#define SNDRV_PCM_RATE_8000_768000 (SNDRV_PCM_RATE_8000_384000|SNDRV_PCM_RATE_705600|\ + SNDRV_PCM_RATE_768000) #define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt) #define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8) #define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index d776291..167cbd2 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1763,12 +1763,13 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, return snd_interval_refine(hw_param_interval(params, rule->var), &t); } -#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 +#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_768000 != 1 << 16 #error "Change this table" #endif static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, - 48000, 64000, 88200, 96000, 176400, 192000 }; + 48000, 64000, 88200, 96000, 176400, 192000, + 352800, 384000, 705600, 768000 }; const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { .count = ARRAY_SIZE(rates), diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index adafead..2334cab 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -60,7 +60,7 @@ source "sound/soc/sunxi/Kconfig" source "sound/soc/sunxi/hdmiaudio/Kconfig" source "sound/soc/sunxi/spdif/Kconfig" # i2s needs various adjustments for sun7i -if ARCH_SUN4I || ARCH_SUN5I +if ARCH_SUN4I || ARCH_SUN5I || ARCH_SUN7I source "sound/soc/sunxi/i2s/Kconfig" endif endif diff --git a/sound/soc/sunxi/hdmiaudio/sndhdmi.c b/sound/soc/sunxi/hdmiaudio/sndhdmi.c index 1c306d2..f291c4b 100644 --- a/sound/soc/sunxi/hdmiaudio/sndhdmi.c +++ b/sound/soc/sunxi/hdmiaudio/sndhdmi.c @@ -22,12 +22,9 @@ #include <sound/initval.h> #include <plat/sys_config.h> #include <linux/io.h> -#include <linux/drv_hdmi.h> -static __audio_hdmi_func g_hdmi_func; -static hdmi_audio_t hdmi_para; +#include "sndhdmi.h" -/* The struct is just for registering the hdmiaudio codec node */ struct sndhdmi_priv { int sysclk; int dai_fmt; @@ -36,11 +33,15 @@ struct sndhdmi_priv { struct snd_pcm_substream *slave_substream; }; -void audio_set_hdmi_func(__audio_hdmi_func *hdmi_func) +static hdmi_audio_t hdmi_para; +static __audio_hdmi_func g_hdmi_func; + +void audio_set_hdmi_func(__audio_hdmi_func * hdmi_func) { g_hdmi_func.hdmi_audio_enable = hdmi_func->hdmi_audio_enable; g_hdmi_func.hdmi_set_audio_para = hdmi_func->hdmi_set_audio_para; } + EXPORT_SYMBOL(audio_set_hdmi_func); #define SNDHDMI_RATES (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT) @@ -53,25 +54,21 @@ static int sndhdmi_mute(struct snd_soc_dai *dai, int mute) } static int sndhdmi_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { return 0; } static void sndhdmi_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { } static int sndhdmi_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { - if ((!substream) || (!params)) { - printk("error:%s,line:%d\n", __func__, __LINE__); - return -EAGAIN; - } hdmi_para.sample_rate = params_rate(params); hdmi_para.channel_num = params_channels(params); switch (params_format(params)) { @@ -94,14 +91,13 @@ static int sndhdmi_hw_params(struct snd_pcm_substream *substream, return 0; } -static int sndhdmi_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, - unsigned int freq, int dir) +static int sndhdmi_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) { return 0; } -static int sndhdmi_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, - int div) +static int sndhdmi_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { hdmi_para.fs_between = div; @@ -110,12 +106,13 @@ static int sndhdmi_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, } -static int sndhdmi_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +static int sndhdmi_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) { return 0; } -/* codec dai operation */ +//codec dai operation struct snd_soc_dai_ops sndhdmi_dai_ops = { .startup = sndhdmi_startup, .shutdown = sndhdmi_shutdown, @@ -126,7 +123,7 @@ struct snd_soc_dai_ops sndhdmi_dai_ops = { .set_fmt = sndhdmi_set_dai_fmt, }; -/* codec dai */ +//codec dai struct snd_soc_dai_driver sndhdmi_dai = { .name = "sndhdmi", /* playback capabilities */ @@ -147,17 +144,11 @@ static int sndhdmi_soc_probe(struct snd_soc_codec *codec) { struct sndhdmi_priv *sndhdmi; - if (!codec) { - printk("error:%s,line:%d\n", __func__, __LINE__); - return -EAGAIN; - } - sndhdmi = kzalloc(sizeof(struct sndhdmi_priv), GFP_KERNEL); - if (sndhdmi == NULL) { - printk("error at:%s,%d\n", __func__, __LINE__); + if(sndhdmi == NULL){ + printk("error at:%s,%d\n",__func__,__LINE__); return -ENOMEM; } - snd_soc_codec_set_drvdata(codec, sndhdmi); return 0; @@ -165,13 +156,7 @@ static int sndhdmi_soc_probe(struct snd_soc_codec *codec) static int sndhdmi_soc_remove(struct snd_soc_codec *codec) { - struct sndhdmi_priv *sndhdmi; - - if (!codec) { - printk("error:%s,line:%d\n", __func__, __LINE__); - return -EAGAIN; - } - sndhdmi = snd_soc_codec_get_drvdata(codec); + struct sndhdmi_priv *sndhdmi = snd_soc_codec_get_drvdata(codec); kfree(sndhdmi); @@ -179,26 +164,17 @@ static int sndhdmi_soc_remove(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_sndhdmi = { - .probe = sndhdmi_soc_probe, - .remove = sndhdmi_soc_remove, + .probe = sndhdmi_soc_probe, + .remove = sndhdmi_soc_remove, }; static int __devinit sndhdmi_codec_probe(struct platform_device *pdev) { - if (!pdev) { - printk("error:%s,line:%d\n", __func__, __LINE__); - return -EAGAIN; - } - return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sndhdmi, - &sndhdmi_dai, 1); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sndhdmi, &sndhdmi_dai, 1); } -static int __exit sndhdmi_codec_remove(struct platform_device *pdev) +static int __devexit sndhdmi_codec_remove(struct platform_device *pdev) { - if (!pdev) { - printk("error:%s,line:%d\n", __func__, __LINE__); - return -EAGAIN; - } snd_soc_unregister_codec(&pdev->dev); return 0; } @@ -209,15 +185,14 @@ static struct platform_driver sndhdmi_codec_driver = { .owner = THIS_MODULE, }, .probe = sndhdmi_codec_probe, - .remove = __exit_p(sndhdmi_codec_remove), + .remove = __devexit_p(sndhdmi_codec_remove), }; static int __init sndhdmi_codec_init(void) { int err = 0; - err = platform_driver_register(&sndhdmi_codec_driver); - if (err < 0) + if ((err = platform_driver_register(&sndhdmi_codec_driver)) < 0) return err; return 0; @@ -233,3 +208,4 @@ module_exit(sndhdmi_codec_exit); MODULE_DESCRIPTION("SNDHDMI ALSA soc codec driver"); MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>"); MODULE_LICENSE("GPL"); + diff --git a/sound/soc/sunxi/hdmiaudio/sndhdmi.h b/sound/soc/sunxi/hdmiaudio/sndhdmi.h new file mode 100644 index 0000000..346e587 --- /dev/null +++ b/sound/soc/sunxi/hdmiaudio/sndhdmi.h @@ -0,0 +1,36 @@ +/* + * sound\soc\sunxi\hdmiaudio\sndhdmi.h + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * chenpailin <chenpailin@allwinnertech.com> + * + * some simple description for this code + * + * 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. + * + */ +#ifndef SNDHDMI_H +#define SNDHDMI_H +#include <linux/drv_hdmi.h> + +struct sndhdmi_platform_data { + void (*power) (int); + int model; + /* + ALSA SOC usually puts the device in standby mode when it's not used + for sometime. If you unset is_powered_on_standby the driver will + turn off the ADC/DAC when this callback is invoked and turn it back + on when needed. Unfortunately this will result in a very light bump + (it can be audible only with good earphones). If this bothers you + set is_powered_on_standby, you will have slightly higher power + consumption. Please note that sending the L3 command for ADC is + enough to make the bump, so it doesn't make difference if you + completely take off power from the codec. + */ + int is_powered_on_standby; +}; + +#endif diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.c b/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.c index 12da1a6..5080f10 100644 --- a/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.c +++ b/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.c @@ -34,43 +34,300 @@ #include <plat/sys_config.h> #include <plat/dma_compat.h> +#include "sunxi-hdmipcm.h" +#include "sunxi-hdmiaudio.h" + +//save the register value +static int regsave[8]; + static struct sunxi_dma_params sunxi_hdmiaudio_pcm_stereo_out = { .client.name = "HDMIAUDIO PCM Stereo out", #if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I .channel = DMACH_HDMIAUDIO, #endif - .dma_addr = 0, + .dma_addr = 0, +}; + +static struct sunxi_dma_params sunxi_hdmiaudio_pcm_stereo_in = { + .client.name = "HDMIAUDIO PCM Stereo in", +#if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I + .channel = DMACH_HDMIAUDIO, +#endif + .dma_addr = SUNXI_HDMIAUDIOBASE + SUNXI_HDMIAUDIORXFIFO, }; +struct sunxi_hdmiaudio_info sunxi_hdmiaudio; + +//clock handle +static struct clk *hdmiaudio_apbclk; +static struct clk *hdmiaudio_pll2clk; +static struct clk *hdmiaudio_pllx8; +static struct clk *hdmiaudio_moduleclk; + void sunxi_snd_txctrl_hdmiaudio(struct snd_pcm_substream *substream, int on) { + u32 reg_val; + + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_TXCHSEL); + reg_val &= ~0x7; + reg_val |= SUNXI_TXCHSEL_CHNUM(substream->runtime->channels); + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_TXCHSEL); + + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_TXCHMAP); + reg_val = 0; + if(substream->runtime->channels == 1) { + reg_val = 0x76543200; + } else { + reg_val = 0x76543210; + } + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_TXCHMAP); + + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val &= ~SUNXI_HDMIAUDIOCTL_SDO3EN; + reg_val &= ~SUNXI_HDMIAUDIOCTL_SDO2EN; + reg_val &= ~SUNXI_HDMIAUDIOCTL_SDO1EN; + reg_val &= ~SUNXI_HDMIAUDIOCTL_SDO0EN; + switch(substream->runtime->channels) { + case 1: + case 2: + reg_val |= SUNXI_HDMIAUDIOCTL_SDO0EN; break; + case 3: + case 4: + reg_val |= SUNXI_HDMIAUDIOCTL_SDO0EN | SUNXI_HDMIAUDIOCTL_SDO1EN; break; + case 5: + case 6: + reg_val |= SUNXI_HDMIAUDIOCTL_SDO0EN | SUNXI_HDMIAUDIOCTL_SDO1EN | SUNXI_HDMIAUDIOCTL_SDO2EN; break; + case 7: + case 8: + reg_val |= SUNXI_HDMIAUDIOCTL_SDO0EN | SUNXI_HDMIAUDIOCTL_SDO1EN | SUNXI_HDMIAUDIOCTL_SDO2EN | SUNXI_HDMIAUDIOCTL_SDO3EN; break; + default: + reg_val |= SUNXI_HDMIAUDIOCTL_SDO0EN; break; + } + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + + //flush TX FIFO + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL); + reg_val |= SUNXI_HDMIAUDIOFCTL_FTX; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL); + + //clear TX counter + writel(0, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOTXCNT); + + if (on) { + /* hdmiaudio TX ENABLE */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val |= SUNXI_HDMIAUDIOCTL_TXEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + + /* enable DMA DRQ mode for play */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT); + reg_val |= SUNXI_HDMIAUDIOINT_TXDRQEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT); + + //Global Enable Digital Audio Interface + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val |= SUNXI_HDMIAUDIOCTL_GEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + }else{ + /* HDMIAUDIO TX DISABLE */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val &= ~SUNXI_HDMIAUDIOCTL_TXEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + + /* DISBALE dma DRQ mode */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT); + reg_val &= ~SUNXI_HDMIAUDIOINT_TXDRQEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT); + + //Global disable Digital Audio Interface + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val &= ~SUNXI_HDMIAUDIOCTL_GEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + } } -static int sunxi_hdmiaudio_set_fmt(struct snd_soc_dai *cpu_dai, - unsigned int fmt) +void sunxi_snd_rxctrl_hdmiaudio(struct snd_pcm_substream *substream, int on) { - return 0; + u32 reg_val; + + //flush RX FIFO + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL); + reg_val |= SUNXI_HDMIAUDIOFCTL_FRX; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL); + + //clear RX counter + writel(0, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIORXCNT); + + if (on) { + /* HDMIAUDIO RX ENABLE */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val |= SUNXI_HDMIAUDIOCTL_RXEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + + /* enable DMA DRQ mode for record */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT); + reg_val |= SUNXI_HDMIAUDIOINT_RXDRQEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT); + + //Global Enable Digital Audio Interface + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val |= SUNXI_HDMIAUDIOCTL_GEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + } else { + /* HDMIAUDIO RX DISABLE */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val &= ~SUNXI_HDMIAUDIOCTL_RXEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + + /* DISBALE dma DRQ mode */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT); + reg_val &= ~SUNXI_HDMIAUDIOINT_RXDRQEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT); + + //Global disable Digital Audio Interface + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val &= ~SUNXI_HDMIAUDIOCTL_GEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + } } -static int sunxi_hdmiaudio_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int sunxi_hdmiaudio_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - struct snd_soc_pcm_runtime *rtd; - struct sunxi_dma_params *dma_data; + u32 reg_val; + u32 reg_val1; - if (!substream) { - printk("error:%s,line:%d\n", __func__, __LINE__); - return -EAGAIN; + if (sunxi_is_sun7i()) + return 0; /* No rx / tx control, etc. on sun7i() */ + + //SDO ON + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val |= (SUNXI_HDMIAUDIOCTL_SDO0EN | SUNXI_HDMIAUDIOCTL_SDO1EN | SUNXI_HDMIAUDIOCTL_SDO2EN | SUNXI_HDMIAUDIOCTL_SDO3EN); + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + + /* master or slave selection */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ + reg_val |= SUNXI_HDMIAUDIOCTL_MS; + break; + case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */ + reg_val &= ~SUNXI_HDMIAUDIOCTL_MS; + break; + default: + return -EINVAL; + } + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + + /* pcm or hdmiaudio mode selection */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val1 = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0); + reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_FMT_RVD; + switch(fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: /* I2S mode */ + reg_val &= ~SUNXI_HDMIAUDIOCTL_PCM; + reg_val1 |= SUNXI_HDMIAUDIOFAT0_FMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: /* Right Justified mode */ + reg_val &= ~SUNXI_HDMIAUDIOCTL_PCM; + reg_val1 |= SUNXI_HDMIAUDIOFAT0_FMT_RGT; + break; + case SND_SOC_DAIFMT_LEFT_J: /* Left Justified mode */ + reg_val &= ~SUNXI_HDMIAUDIOCTL_PCM; + reg_val1 |= SUNXI_HDMIAUDIOFAT0_FMT_LFT; + break; + case SND_SOC_DAIFMT_DSP_A: /* L data msb after FRM LRC */ + reg_val |= SUNXI_HDMIAUDIOCTL_PCM; + reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_LRCP; + break; + case SND_SOC_DAIFMT_DSP_B: /* L data msb during FRM LRC */ + reg_val |= SUNXI_HDMIAUDIOCTL_PCM; + reg_val1 |= SUNXI_HDMIAUDIOFAT0_LRCP; + break; + default: + return -EINVAL; } + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + writel(reg_val1, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0); + + /* DAI signal inversions */ + reg_val1 = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0); + switch(fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ + reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_LRCP; + reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_BCP; + break; + case SND_SOC_DAIFMT_NB_IF: /* normal bclk + inv frm */ + reg_val1 |= SUNXI_HDMIAUDIOFAT0_LRCP; + reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_BCP; + break; + case SND_SOC_DAIFMT_IB_NF: /* invert bclk + nor frm */ + reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_LRCP; + reg_val1 |= SUNXI_HDMIAUDIOFAT0_BCP; + break; + case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */ + reg_val1 |= SUNXI_HDMIAUDIOFAT0_LRCP; + reg_val1 |= SUNXI_HDMIAUDIOFAT0_BCP; + break; + } + writel(reg_val1, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0); + + /* word select size */ + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0); + reg_val &= ~SUNXI_HDMIAUDIOFAT0_WSS_32BCLK; + if(sunxi_hdmiaudio.ws_size == 16) + reg_val |= SUNXI_HDMIAUDIOFAT0_WSS_16BCLK; + else if(sunxi_hdmiaudio.ws_size == 20) + reg_val |= SUNXI_HDMIAUDIOFAT0_WSS_20BCLK; + else if(sunxi_hdmiaudio.ws_size == 24) + reg_val |= SUNXI_HDMIAUDIOFAT0_WSS_24BCLK; + else + reg_val |= SUNXI_HDMIAUDIOFAT0_WSS_32BCLK; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0); + + /* PCM REGISTER setup */ + reg_val = sunxi_hdmiaudio.pcm_txtype&0x3; + reg_val |= sunxi_hdmiaudio.pcm_rxtype<<2; + + if(!sunxi_hdmiaudio.pcm_sync_type) + reg_val |= SUNXI_HDMIAUDIOFAT1_SSYNC; //short sync + if(sunxi_hdmiaudio.pcm_sw == 16) + reg_val |= SUNXI_HDMIAUDIOFAT1_SW; + + reg_val |=((sunxi_hdmiaudio.pcm_start_slot - 1)&0x3)<<6; //start slot index + + reg_val |= sunxi_hdmiaudio.pcm_lsb_first<<9; //MSB or LSB first + + if(sunxi_hdmiaudio.pcm_sync_period == 256) + reg_val |= 0x4<<12; + else if (sunxi_hdmiaudio.pcm_sync_period == 128) + reg_val |= 0x3<<12; + else if (sunxi_hdmiaudio.pcm_sync_period == 64) + reg_val |= 0x2<<12; + else if (sunxi_hdmiaudio.pcm_sync_period == 32) + reg_val |= 0x1<<12; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT1); + + /* set FIFO control register */ + reg_val = 0 & 0x3; + reg_val |= (0 & 0x1)<<2; + reg_val |= SUNXI_HDMIAUDIOFCTL_RXTL(0xf); //RX FIFO trigger level + reg_val |= SUNXI_HDMIAUDIOFCTL_TXTL(0x40); //TX FIFO empty trigger level + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL); + return 0; +} - rtd = substream->private_data; +static int sunxi_hdmiaudio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sunxi_dma_params *dma_data; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + /* play or record */ + if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dma_data = &sunxi_hdmiaudio_pcm_stereo_out; else - printk("error:hdmiaudio can't support capture:%s,line:%d\n", - __func__, __LINE__); + dma_data = &sunxi_hdmiaudio_pcm_stereo_in; snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data); @@ -78,7 +335,7 @@ static int sunxi_hdmiaudio_hw_params(struct snd_pcm_substream *substream, } static int sunxi_hdmiaudio_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) + int cmd, struct snd_soc_dai *dai) { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -89,34 +346,102 @@ static int sunxi_hdmiaudio_trigger(struct snd_pcm_substream *substream, return 0; /* No rx / tx control, etc. on sun7i() */ switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - sunxi_snd_txctrl_hdmiaudio(substream, 1); - sunxi_dma_started(dma_data); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - sunxi_snd_txctrl_hdmiaudio(substream, 0); - break; - default: - ret = -EINVAL; - break; + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + sunxi_snd_rxctrl_hdmiaudio(substream, 1); + } else { + sunxi_snd_txctrl_hdmiaudio(substream, 1); + } + sunxi_dma_started(dma_data); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + sunxi_snd_rxctrl_hdmiaudio(substream, 0); + } else { + sunxi_snd_txctrl_hdmiaudio(substream, 0); + } + break; + default: + ret = -EINVAL; + break; } return ret; } -/* freq: 1: 22.5792MHz 0: 24.576MHz */ +//freq: 1: 22.5792MHz 0: 24.576MHz static int sunxi_hdmiaudio_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, - unsigned int freq, int dir) + unsigned int freq, int dir) { + if (sunxi_is_sun7i()) + return 0; /* No rx / tx control, etc. on sun7i() */ + + if (!freq) { + clk_set_rate(hdmiaudio_pll2clk, 24576000); + } else { + clk_set_rate(hdmiaudio_pll2clk, 22579200); + } + return 0; } -static int sunxi_hdmiaudio_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, - int div) +static int sunxi_hdmiaudio_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div) { + u32 reg; + + if (sunxi_is_sun7i()) + return 0; /* No rx / tx control, etc. on sun7i() */ + + switch (div_id) { + case SUNXI_DIV_MCLK: + if(div <= 8) + div = (div >>1); + else if(div == 12) + div = 0x5; + else if(div == 16) + div = 0x6; + else if(div == 24) + div = 0x7; + else if(div == 32) + div = 0x8; + else if(div == 48) + div = 0x9; + else if(div == 64) + div = 0xa; + reg = (readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD) & ~SUNXI_HDMIAUDIOCLKD_MCLK_MASK) | (div << SUNXI_HDMIAUDIOCLKD_MCLK_OFFS); + writel(reg, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD); + break; + case SUNXI_DIV_BCLK: + if(div <= 8) + div = (div>>1) - 1; + else if(div == 12) + div = 0x4; + else if(div == 16) + div = 0x5; + else if(div == 32) + div = 0x6; + else if(div == 64) + div = 0x7; + reg = (readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD) & ~SUNXI_HDMIAUDIOCLKD_BCLK_MASK) | (div <<SUNXI_HDMIAUDIOCLKD_BCLK_OFFS); + writel(reg, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD); + break; + default: + return -EINVAL; + } + + //diable MCLK output when high samplerate + reg = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD); + if(!(reg & 0xF)) { + reg &= ~SUNXI_HDMIAUDIOCLKD_MCLKOEN; + writel(reg, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD); + } else { + reg |= SUNXI_HDMIAUDIOCLKD_MCLKOEN; + writel(reg, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD); + } + return 0; } @@ -135,78 +460,205 @@ static int sunxi_hdmiaudio_dai_remove(struct snd_soc_dai *dai) return 0; } +static void hdmiaudioregsave(void) +{ + regsave[0] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + regsave[1] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0); + regsave[2] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT1); + regsave[3] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL) | (0x3<<24); + regsave[4] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT); + regsave[5] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD); + regsave[6] = readl(sunxi_hdmiaudio.regs + SUNXI_TXCHSEL); + regsave[7] = readl(sunxi_hdmiaudio.regs + SUNXI_TXCHMAP); +} + +static void hdmiaudioregrestore(void) +{ + writel(regsave[0], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + writel(regsave[1], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0); + writel(regsave[2], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT1); + writel(regsave[3], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL); + writel(regsave[4], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT); + writel(regsave[5], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD); + writel(regsave[6], sunxi_hdmiaudio.regs + SUNXI_TXCHSEL); + writel(regsave[7], sunxi_hdmiaudio.regs + SUNXI_TXCHMAP); +} + static int sunxi_hdmiaudio_suspend(struct snd_soc_dai *cpu_dai) { - printk("[HDMIAUDIO]Entered %s\n", __func__); + u32 reg_val; + + if (sunxi_is_sun7i()) + return 0; /* No rx / tx control, etc. on sun7i() */ - /* - * printk("[HDMIAUDIO]PLL2 0x01c20008 = %#x, line = %d\n", - * *(volatile int *)0xF1C20008, __LINE__); - */ - printk("[HDMIAUDIO]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", - *(volatile int *)0xF1C20068, __LINE__); - printk("[HDMIAUDIO]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", - *(volatile int *)0xF1C200B8, __LINE__); + printk("[HDMIAUDIO]Entered %s\n", __func__); + + //Global Enable Digital Audio Interface + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val &= ~SUNXI_HDMIAUDIOCTL_GEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + + hdmiaudioregsave(); + //release the module clock + clk_disable(hdmiaudio_moduleclk); + + clk_disable(hdmiaudio_apbclk); + + //printk("[HDMIAUDIO]PLL2 0x01c20008 = %#x, line = %d\n", *(volatile int*)0xF1C20008, __LINE__); + printk("[HDMIAUDIO]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); + printk("[HDMIAUDIO]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__); return 0; } static int sunxi_hdmiaudio_resume(struct snd_soc_dai *cpu_dai) { + u32 reg_val; + + if (sunxi_is_sun7i()) + return 0; /* No rx / tx control, etc. on sun7i() */ + printk("[HDMIAUDIO]Entered %s\n", __func__); - /* - * printk("[HDMIAUDIO]PLL2 0x01c20008 = %#x, line = %d\n", - * *(volatile int *)0xF1C20008, __LINE__); - */ - printk("[HDMIAUDIO]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", - *(volatile int *)0xF1C20068, __LINE__); - printk("[HDMIAUDIO]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", - *(volatile int *)0xF1C200B8, __LINE__); + //release the module clock + clk_enable(hdmiaudio_apbclk); + + //release the module clock + clk_enable(hdmiaudio_moduleclk); + + hdmiaudioregrestore(); + + //Global Enable Digital Audio Interface + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val |= SUNXI_HDMIAUDIOCTL_GEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + + //printk("[HDMIAUDIO]PLL2 0x01c20008 = %#x, line = %d\n", *(volatile int*)0xF1C20008, __LINE__); + printk("[HDMIAUDIO]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); + printk("[HDMIAUDIO]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__); return 0; } -#define SUNXI_HDMI_RATES (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT) +#define SUNXI_I2S_RATES (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT) static struct snd_soc_dai_ops sunxi_hdmiaudio_dai_ops = { - .trigger = sunxi_hdmiaudio_trigger, - .hw_params = sunxi_hdmiaudio_hw_params, - .set_fmt = sunxi_hdmiaudio_set_fmt, - .set_clkdiv = sunxi_hdmiaudio_set_clkdiv, - .set_sysclk = sunxi_hdmiaudio_set_sysclk, + .trigger = sunxi_hdmiaudio_trigger, + .hw_params = sunxi_hdmiaudio_hw_params, + .set_fmt = sunxi_hdmiaudio_set_fmt, + .set_clkdiv = sunxi_hdmiaudio_set_clkdiv, + .set_sysclk = sunxi_hdmiaudio_set_sysclk, }; static struct snd_soc_dai_driver sunxi_hdmiaudio_dai = { - - .probe = sunxi_hdmiaudio_dai_probe, - .suspend = sunxi_hdmiaudio_suspend, - .resume = sunxi_hdmiaudio_resume, - .remove = sunxi_hdmiaudio_dai_remove, - .playback = { - .channels_min = 1, - .channels_max = 2, - .rates = SUNXI_HDMI_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .probe = sunxi_hdmiaudio_dai_probe, + .suspend = sunxi_hdmiaudio_suspend, + .resume = sunxi_hdmiaudio_resume, + .remove = sunxi_hdmiaudio_dai_remove, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SUNXI_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE }, - .symmetric_rates = 1, - .ops = &sunxi_hdmiaudio_dai_ops, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SUNXI_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE + }, + .symmetric_rates = 1, + .ops = &sunxi_hdmiaudio_dai_ops, }; static int __devinit sunxi_hdmiaudio_dev_probe(struct platform_device *pdev) { - return snd_soc_register_dai(&pdev->dev, &sunxi_hdmiaudio_dai); + int reg_val = 0; + int ret = 0; + + if (sunxi_is_sun7i()) { + /* No rx / tx control, etc. on sun7i() */ + return snd_soc_register_dai(&pdev->dev, &sunxi_hdmiaudio_dai); + } + + sunxi_hdmiaudio.regs = ioremap(SUNXI_HDMIAUDIOBASE, 0x100); + if (sunxi_hdmiaudio.regs == NULL) + return -ENXIO; + + sunxi_hdmiaudio.ccmregs = ioremap(SUNXI_CCMBASE, 0x100); + if (sunxi_hdmiaudio.ccmregs == NULL) + return -ENXIO; + + sunxi_hdmiaudio.ioregs = ioremap(0x01C20800, 0x100); + if (sunxi_hdmiaudio.ioregs == NULL) + return -ENXIO; + + //hdmiaudio apbclk + hdmiaudio_apbclk = clk_get(NULL, "apb_i2s"); + if(-1 == clk_enable(hdmiaudio_apbclk)){ + printk("hdmiaudio_apbclk failed! line = %d\n", __LINE__); + } + + hdmiaudio_pllx8 = clk_get(NULL, "audio_pllx8"); + + //hdmiaudio pll2clk + hdmiaudio_pll2clk = clk_get(NULL, "audio_pll"); + + //hdmiaudio module clk + hdmiaudio_moduleclk = clk_get(NULL, "i2s"); + + if(clk_set_parent(hdmiaudio_moduleclk, hdmiaudio_pll2clk)){ + printk("try to set parent of hdmiaudio_moduleclk to hdmiaudio_pll2ck failed! line = %d\n",__LINE__); + } + + + if(clk_set_rate(hdmiaudio_moduleclk, 24576000/8)){ + printk("set hdmiaudio_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__); + } + + + if(-1 == clk_enable(hdmiaudio_moduleclk)){ + printk("open hdmiaudio_moduleclk failed! line = %d\n", __LINE__); + } + + reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + reg_val |= SUNXI_HDMIAUDIOCTL_GEN; + writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL); + + ret = snd_soc_register_dai(&pdev->dev, &sunxi_hdmiaudio_dai); + + iounmap(sunxi_hdmiaudio.ioregs); + + return 0; } static int __devexit sunxi_hdmiaudio_dev_remove(struct platform_device *pdev) { + if (sunxi_is_sun7i()) { + /* No rx / tx control, etc. on sun7i() */ + snd_soc_unregister_dai(&pdev->dev); + return 0; + } + + //release the module clock + clk_disable(hdmiaudio_moduleclk); + + //release pllx8clk + clk_put(hdmiaudio_pllx8); + + //release pll2clk + clk_put(hdmiaudio_pll2clk); + + //release apbclk + clk_put(hdmiaudio_apbclk); + snd_soc_unregister_dai(&pdev->dev); platform_set_drvdata(pdev, NULL); return 0; } static struct platform_driver sunxi_hdmiaudio_driver = { - .probe = sunxi_hdmiaudio_dev_probe, - .remove = __devexit_p(sunxi_hdmiaudio_dev_remove), - .driver = { + .probe = sunxi_hdmiaudio_dev_probe, + .remove = __devexit_p(sunxi_hdmiaudio_dev_remove), + .driver = { .name = "sunxi-hdmiaudio", .owner = THIS_MODULE, }, @@ -216,8 +668,7 @@ static int __init sunxi_hdmiaudio_init(void) { int err = 0; - err = platform_driver_register(&sunxi_hdmiaudio_driver); - if (err < 0) + if ((err = platform_driver_register(&sunxi_hdmiaudio_driver)) < 0) return err; return 0; diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.h b/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.h new file mode 100644 index 0000000..9f4a0cc --- /dev/null +++ b/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.h @@ -0,0 +1,303 @@ +/* + * sound\soc\sunxi\hdmiaudio\sunxi-hdmiaudio.h + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * chenpailin <chenpailin@allwinnertech.com> + * + * some simple description for this code + * + * 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. + * + */ +#ifndef SUNXI_HDMIAUIDO_H_ +#define SUNXI_HDMIAUIDO_H_ +#include <linux/drv_hdmi.h> + +/*------------------------------------------------------------*/ +/* REGISTER definition */ + +/* HDMIAUDIO REGISTER */ +#define SUNXI_HDMIAUDIOBASE (0x01C22400) + +#define SUNXI_HDMIAUDIOCTL (0x00) + #define SUNXI_HDMIAUDIOCTL_SDO3EN (1<<11) + #define SUNXI_HDMIAUDIOCTL_SDO2EN (1<<10) + #define SUNXI_HDMIAUDIOCTL_SDO1EN (1<<9) + #define SUNXI_HDMIAUDIOCTL_SDO0EN (1<<8) + #define SUNXI_HDMIAUDIOCTL_ASS (1<<6) + #define SUNXI_HDMIAUDIOCTL_MS (1<<5) + #define SUNXI_HDMIAUDIOCTL_PCM (1<<4) + #define SUNXI_HDMIAUDIOCTL_LOOP (1<<3) + #define SUNXI_HDMIAUDIOCTL_TXEN (1<<2) + #define SUNXI_HDMIAUDIOCTL_RXEN (1<<1) + #define SUNXI_HDMIAUDIOCTL_GEN (1<<0) + +#define SUNXI_HDMIAUDIOFAT0 (0x04) + #define SUNXI_HDMIAUDIOFAT0_LRCP (1<<7) + #define SUNXI_HDMIAUDIOFAT0_BCP (1<<6) + #define SUNXI_HDMIAUDIOFAT0_SR_RVD (3<<4) + #define SUNXI_HDMIAUDIOFAT0_SR_16BIT (0<<4) + #define SUNXI_HDMIAUDIOFAT0_SR_20BIT (1<<4) + #define SUNXI_HDMIAUDIOFAT0_SR_24BIT (2<<4) + #define SUNXI_HDMIAUDIOFAT0_WSS_16BCLK (0<<2) + #define SUNXI_HDMIAUDIOFAT0_WSS_20BCLK (1<<2) + #define SUNXI_HDMIAUDIOFAT0_WSS_24BCLK (2<<2) + #define SUNXI_HDMIAUDIOFAT0_WSS_32BCLK (3<<2) + #define SUNXI_HDMIAUDIOFAT0_FMT_I2S (0<<0) + #define SUNXI_HDMIAUDIOFAT0_FMT_LFT (1<<0) + #define SUNXI_HDMIAUDIOFAT0_FMT_RGT (2<<0) + #define SUNXI_HDMIAUDIOFAT0_FMT_RVD (3<<0) + +#define SUNXI_HDMIAUDIOFAT1 (0x08) + #define SUNXI_HDMIAUDIOFAT1_SYNCLEN_16BCLK (0<<12) + #define SUNXI_HDMIAUDIOFAT1_SYNCLEN_32BCLK (1<<12) + #define SUNXI_HDMIAUDIOFAT1_SYNCLEN_64BCLK (2<<12) + #define SUNXI_HDMIAUDIOFAT1_SYNCLEN_128BCLK (3<<12) + #define SUNXI_HDMIAUDIOFAT1_SYNCLEN_256BCLK (4<<12) + #define SUNXI_HDMIAUDIOFAT1_SYNCOUTEN (1<<11) + #define SUNXI_HDMIAUDIOFAT1_OUTMUTE (1<<10) + #define SUNXI_HDMIAUDIOFAT1_MLS (1<<9) + #define SUNXI_HDMIAUDIOFAT1_SEXT (1<<8) + #define SUNXI_HDMIAUDIOFAT1_SI_1ST (0<<6) + #define SUNXI_HDMIAUDIOFAT1_SI_2ND (1<<6) + #define SUNXI_HDMIAUDIOFAT1_SI_3RD (2<<6) + #define SUNXI_HDMIAUDIOFAT1_SI_4TH (3<<6) + #define SUNXI_HDMIAUDIOFAT1_SW (1<<5) + #define SUNXI_HDMIAUDIOFAT1_SSYNC (1<<4) + #define SUNXI_HDMIAUDIOFAT1_RXPDM_16PCM (0<<2) + #define SUNXI_HDMIAUDIOFAT1_RXPDM_8PCM (1<<2) + #define SUNXI_HDMIAUDIOFAT1_RXPDM_8ULAW (2<<2) + #define SUNXI_HDMIAUDIOFAT1_RXPDM_8ALAW (3<<2) + #define SUNXI_HDMIAUDIOFAT1_TXPDM_16PCM (0<<0) + #define SUNXI_HDMIAUDIOFAT1_TXPDM_8PCM (1<<0) + #define SUNXI_HDMIAUDIOFAT1_TXPDM_8ULAW (2<<0) + #define SUNXI_HDMIAUDIOFAT1_TXPDM_8ALAW (3<<0) + +#define SUNXI_HDMIAUDIOTXFIFO (0x0C) + +#define SUNXI_HDMIAUDIORXFIFO (0x10) + +#define SUNXI_HDMIAUDIOFCTL (0x14) + #define SUNXI_HDMIAUDIOFCTL_FIFOSRC (1<<31) + #define SUNXI_HDMIAUDIOFCTL_FTX (1<<25) + #define SUNXI_HDMIAUDIOFCTL_FRX (1<<24) + #define SUNXI_HDMIAUDIOFCTL_TXTL(v) ((v)<<12) + #define SUNXI_HDMIAUDIOFCTL_RXTL(v) ((v)<<4) + #define SUNXI_HDMIAUDIOFCTL_TXIM_MOD0 (0<<2) + #define SUNXI_HDMIAUDIOFCTL_TXIM_MOD1 (1<<2) + #define SUNXI_HDMIAUDIOFCTL_RXOM_MOD0 (0<<0) + #define SUNXI_HDMIAUDIOFCTL_RXOM_MOD1 (1<<0) + #define SUNXI_HDMIAUDIOFCTL_RXOM_MOD2 (2<<0) + #define SUNXI_HDMIAUDIOFCTL_RXOM_MOD3 (3<<0) + +#define SUNXI_HDMIAUDIOFSTA (0x18) + #define SUNXI_HDMIAUDIOFSTA_TXE (1<<28) + #define SUNXI_HDMIAUDIOFSTA_TXECNT(v) ((v)<<16) + #define SUNXI_HDMIAUDIOFSTA_RXA (1<<8) + #define SUNXI_HDMIAUDIOFSTA_RXACNT(v) ((v)<<0) + +#define SUNXI_HDMIAUDIOINT (0x1C) + #define SUNXI_HDMIAUDIOINT_TXDRQEN (1<<7) + #define SUNXI_HDMIAUDIOINT_TXUIEN (1<<6) + #define SUNXI_HDMIAUDIOINT_TXOIEN (1<<5) + #define SUNXI_HDMIAUDIOINT_TXEIEN (1<<4) + #define SUNXI_HDMIAUDIOINT_RXDRQEN (1<<2) + #define SUNXI_HDMIAUDIOINT_RXOIEN (1<<1) + #define SUNXI_HDMIAUDIOINT_RXAIEN (1<<0) + +#define SUNXI_HDMIAUDIOISTA (0x20) + #define SUNXI_HDMIAUDIOISTA_TXUISTA (1<<6) + #define SUNXI_HDMIAUDIOISTA_TXOISTA (1<<5) + #define SUNXI_HDMIAUDIOISTA_TXEISTA (1<<4) + #define SUNXI_HDMIAUDIOISTA_RXOISTA (1<<1) + #define SUNXI_HDMIAUDIOISTA_RXAISTA (1<<0) + +#define SUNXI_HDMIAUDIOCLKD (0x24) + #define SUNXI_HDMIAUDIOCLKD_MCLKOEN (1<<7) + #define SUNXI_HDMIAUDIOCLKD_BCLKDIV_2 (0<<4) + #define SUNXI_HDMIAUDIOCLKD_BCLKDIV_4 (1<<4) + #define SUNXI_HDMIAUDIOCLKD_BCLKDIV_6 (2<<4) + #define SUNXI_HDMIAUDIOCLKD_BCLKDIV_8 (3<<4) + #define SUNXI_HDMIAUDIOCLKD_BCLKDIV_12 (4<<4) + #define SUNXI_HDMIAUDIOCLKD_BCLKDIV_16 (5<<4) + #define SUNXI_HDMIAUDIOCLKD_BCLKDIV_32 (6<<4) + #define SUNXI_HDMIAUDIOCLKD_BCLKDIV_64 (7<<4) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_1 (0<<0) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_2 (1<<0) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_4 (2<<0) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_6 (3<<0) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_8 (4<<0) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_12 (5<<0) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_16 (6<<0) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_24 (7<<0) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_32 (8<<0) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_48 (9<<0) + #define SUNXI_HDMIAUDIOCLKD_MCLKDIV_64 (10<<0) + +#define SUNXI_HDMIAUDIOTXCNT (0x28) + +#define SUNXI_HDMIAUDIORXCNT (0x2C) + +#define SUNXI_TXCHSEL (0x30) + #define SUNXI_TXCHSEL_CHNUM(v) (((v)-1)<<0) + +#define SUNXI_TXCHMAP (0x34) + #define SUNXI_TXCHMAP_CH7(v) (((v)-1)<<28) + #define SUNXI_TXCHMAP_CH6(v) (((v)-1)<<24) + #define SUNXI_TXCHMAP_CH5(v) (((v)-1)<<20) + #define SUNXI_TXCHMAP_CH4(v) (((v)-1)<<16) + #define SUNXI_TXCHMAP_CH3(v) (((v)-1)<<12) + #define SUNXI_TXCHMAP_CH2(v) (((v)-1)<<8) + #define SUNXI_TXCHMAP_CH1(v) (((v)-1)<<4) + #define SUNXI_TXCHMAP_CH0(v) (((v)-1)<<0) + +#define SUNXI_RXCHSEL (0x38) + #define SUNXI_RXCHSEL_CHNUM(v) (((v)-1)<<0) + +#define SUNXI_RXCHMAP (0x3C) + #define SUNXI_RXCHMAP_CH3(v) (((v)-1)<<12) + #define SUNXI_RXCHMAP_CH2(v) (((v)-1)<<8) + #define SUNXI_RXCHMAP_CH1(v) (((v)-1)<<4) + #define SUNXI_RXCHMAP_CH0(v) (((v)-1)<<0) + + +/* DMA REGISTER */ +#define SUNXI_DMABASE (0x01C02000) + +#define SUNXI_DMAIRQEN (0x0) + #define SUNXI_DMAIRQEN_NDMA_FULLEN(v) (1<<((v)*2+1)) + #define SUNXI_DMAIRQEN_NDMA_HALFEN(v) (1<<((v)*2)) + +#define SUNXI_DMAIRQPENDING (0x4) + #define SUNXI_DMAIRQPENGDING_NDMA_FULLPEND(v) (1<<((v)*2+1)) + #define SUNXI_DMAIRQPENGDING_NDMA_HALFPEND(v) (1<<((v)*2)) + +#define SUNXI_NDMACFG(v) ((v)*0x20+0x100) + #define SUNXI_NDMACFG_DMALOAD (1<<31) + #define SUNXI_NDMACFG_BUSY (1<<30) + #define SUNXI_NDMACFG_CONTINUOUS (1<<29) + #define SUNXI_NDMACFG_WAIT(v) (((v)-1)<<26) //wait clock = 2^n example: 8 clocks = 2^3 + #define SUNXI_NDMACFG_DSTDATAWIDTH_8BIT (0<<24) + #define SUNXI_NDMACFG_DSTDATAWIDTH_16BIT (1<<24) + #define SUNXI_NDMACFG_DSTDATAWIDTH_32BIT (2<<24) + #define SUNXI_NDMACFG_DSTDATAWIDTH_RVD (3<<24) + #define SUNXI_NDMACFG_DSTBURST4 (1<<23) + #define SUNXI_NDMACFG_DSTADDRTYPE_INC (0<<21) + #define SUNXI_NDMACFG_DSTADDRTYPE_CON (1<<21) + #define SUNXI_NDMACFG_DSTTYPE_IRTX (0x0<<16) + #define SUNXI_NDMACFG_DSTTYPE_SPDIFTX (0x1<<16) + #define SUNXI_NDMACFG_DSTTYPE_IISTX (0x2<<16) + #define SUNXI_NDMACFG_DSTTYPE_AC97TX (0x3<<16) + #define SUNXI_NDMACFG_DSTTYPE_SPI0TX (0x4<<16) + #define SUNXI_NDMACFG_DSTTYPE_SPI1TX (0x5<<16) + #define SUNXI_NDMACFG_DSTTYPE_SPI2TX (0x6<<16) + #define SUNXI_NDMACFG_DSTTYPE_UART0TX (0x8<<16) + #define SUNXI_NDMACFG_DSTTYPE_UART1TX (0x9<<16) + #define SUNXI_NDMACFG_DSTTYPE_UART2TX (0xA<<16) + #define SUNXI_NDMACFG_DSTTYPE_UART3TX (0xB<<16) + #define SUNXI_NDMACFG_DSTTYPE_AUDIODA (0xC<<16) + #define SUNXI_NDMACFG_DSTTYPE_NFC (0xF<<16) + #define SUNXI_NDMACFG_DSTTYPE_SRAM (0x10<<16) + #define SUNXI_NDMACFG_DSTTYPE_DRAM (0x11<<16) + #define SUNXI_NDMACFG_DSTTYPE_UART4TX (0x12<<16) + #define SUNXI_NDMACFG_DSTTYPE_UART5TX (0x13<<16) + #define SUNXI_NDMACFG_DSTTYPE_UART6TX (0x14<<16) + #define SUNXI_NDMACFG_DSTTYPE_UART7TX (0x15<<16) + #define SUNXI_NDMACFG_SRCDATAWIDTH_8BIT (0<<8) + #define SUNXI_NDMACFG_SRCDATAWIDTH_16BIT (1<<8) + #define SUNXI_NDMACFG_SRCDATAWIDTH_32BIT (2<<8) + #define SUNXI_NDMACFG_SRCDATAWIDTH_RVD (3<<8) + #define SUNXI_NDMACFG_SRCBURST4 (1<<7) + #define SUNXI_NDMACFG_SRCADDRTYPE_INC (0<<5) + #define SUNXI_NDMACFG_SRCADDRTYPE_CON (1<<5) + #define SUNXI_NDMACFG_SRCTYPE_IRRX (0x0<<0) + #define SUNXI_NDMACFG_SRCTYPE_SPDIFRX (0x1<<0) + #define SUNXI_NDMACFG_SRCTYPE_IISRX (0x2<<0) + #define SUNXI_NDMACFG_SRCTYPE_AC97RX (0x3<<0) + #define SUNXI_NDMACFG_SRCTYPE_SPI0RX (0x4<<0) + #define SUNXI_NDMACFG_SRCTYPE_SPI1RX (0x5<<0) + #define SUNXI_NDMACFG_SRCTYPE_SPI2RX (0x6<<0) + #define SUNXI_NDMACFG_SRCTYPE_UART0RX (0x8<<0) + #define SUNXI_NDMACFG_SRCTYPE_UART1RX (0x9<<0) + #define SUNXI_NDMACFG_SRCTYPE_UART2RX (0xA<<0) + #define SUNXI_NDMACFG_SRCTYPE_UART3RX (0xB<<0) + #define SUNXI_NDMACFG_SRCTYPE_AUDIOAD (0xC<<0) + #define SUNXI_NDMACFG_SRCTYPE_TPAD (0xD<<0) + #define SUNXI_NDMACFG_SRCTYPE_NFC (0xF<<0) + #define SUNXI_NDMACFG_SRCTYPE_SRAM (0x10<<0) + #define SUNXI_NDMACFG_SRCTYPE_DRAM (0x11<<0) + #define SUNXI_NDMACFG_SRCTYPE_UART4RX (0x12<<0) + #define SUNXI_NDMACFG_SRCTYPE_UART5RX (0x13<<0) + #define SUNXI_NDMACFG_SRCTYPE_UART6RX (0x14<<0) + #define SUNXI_NDMACFG_SRCTYPE_UART7RX (0x15<<0) + +#define SUNXI_NDMASRCADDR(v) ((v)*0x20 + 0x100 + 4) + +#define SUNXI_NDMADSTADDR(v) ((v)*0x20 + 0x100 + 8) + +#define SUNXI_NDMACNT(v) ((v)*0x20 + 0x100 + 0xC) + + +/* CCM REGISTER */ +#define SUNXI_CCMBASE (0x01C20000) + +#define SUNXI_CCM_AUDIO_HOSC_PLL_REG (0x08) + #define SUNXI_CCM_AUDIO_HOSC_PLL_REG_AUDIOEN (1<<31) + #define SUNXI_CCM_AUDIO_HOSC_PLL_REG_FRE225792MHZ (0<<27) + #define SUNXI_CCM_AUDIO_HOSC_PLL_REG_FRE24576MHZ (1<<27) + +#define SUNXI_CCM_APB_GATE_REG (0x68) + #define SUNXI_CCM_APB_GATE_REG_IISGATE (1<<3) + +#define SUNXI_CCM_AUDIO_CLK_REG (0xb8) + #define SUNXI_CCM_AUDIO_CLK_REG_IISSPECIALGATE (1<<31) + #define SUNXI_CCM_AUDIO_CLK_REG_DIV(v) ((v)<<16) +/*------------------------------------------------------------*/ + +/*------------------------------------------------------------*/ +/* Clock dividers */ +#define SUNXI_DIV_MCLK 0 +#define SUNXI_DIV_BCLK 1 + +#define SUNXI_HDMIAUDIOCLKD_MCLK_MASK 0x0f +#define SUNXI_HDMIAUDIOCLKD_MCLK_OFFS 0 +#define SUNXI_HDMIAUDIOCLKD_BCLK_MASK 0x070 +#define SUNXI_HDMIAUDIOCLKD_BCLK_OFFS 4 +#define SUNXI_HDMIAUDIOCLKD_MCLKEN_OFFS 7 + +unsigned int sunxi_hdmiaudio_get_clockrate(void); +extern struct sunxi_hdmiaudio_info sunxi_hdmiaudio; + +extern void sunxi_snd_txctrl_hdmiaudio(struct snd_pcm_substream *substream, int on); +extern void sunxi_snd_rxctrl_hdmiaudio(struct snd_pcm_substream *substream, int on); + +struct sunxi_hdmiaudio_info { + void __iomem *regs; /* IIS BASE */ + void __iomem *ccmregs; //CCM BASE + void __iomem *ioregs; //IO BASE + + u32 slave; //0: master, 1: slave + u32 mono; //0: stereo, 1: mono + u32 samp_fs; //audio sample rate (unit in kHz) + u32 samp_res; //16 bits, 20 bits , 24 bits, 32 bits) + u32 samp_format; //audio sample format (0: standard I2S, 1: left-justified, 2: right-justified, 3: pcm) + u32 ws_size; //16 BCLK, 20 BCLK, 24 BCLK, 32 BCLK) + u32 mclk_rate; //mclk frequency divide by fs (128fs, 192fs, 256fs, 384fs, 512fs, 768fs) + u32 lrc_pol; //LRC clock polarity (0: normal ,1: inverted) + u32 bclk_pol; //BCLK polarity (0: normal, 1: inverted) + u32 pcm_txtype; //PCM transmitter type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law) + u32 pcm_rxtype; //PCM receiver type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law) + u32 pcm_sw; //PCM slot width (8: 8 bits, 16: 16 bits) + u32 pcm_sync_period;//PCM sync period (16/32/64/128/256) + u32 pcm_sync_type; //PCM sync symbol size (0: short sync, 1: long sync) + u32 pcm_start_slot;//PCM start slot index (1--4) + u32 pcm_lsb_first; //0: MSB first, 1: LSB first + u32 pcm_ch_num; //PCM channel number (1: one channel, 2: two channel) + +}; + +extern struct sunxi_hdmiaudio_info sunxi_hdmiaudio; +#endif diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.c b/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.c index 4916b51..91004e3 100644 --- a/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.c +++ b/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.c @@ -29,30 +29,29 @@ #include <mach/hardware.h> #include <plat/dma_compat.h> -static volatile unsigned int dmasrc; -static volatile unsigned int dmadst; +#include "sunxi-hdmiaudio.h" +#include "sunxi-hdmipcm.h" + +static volatile unsigned int dmasrc = 0; +static volatile unsigned int dmadst = 0; static const struct snd_pcm_hardware sunxi_pcm_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME, + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S32_LE, - .rates = SNDRV_PCM_RATE_8000_192000 | - SNDRV_PCM_RATE_KNOT, + SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .rate_min = 8000, .rate_max = 192000, .channels_min = 1, .channels_max = 2, - .buffer_bytes_max = 128*1024, /* value must be (2^n)Kbyte size */ - .period_bytes_min = 1024*4, - .period_bytes_max = 1024*32, - .periods_min = 4, - .periods_max = 8, - .fifo_size = 128, + .buffer_bytes_max = 128*1024, /* value must be (2^n)Kbyte size */ + .period_bytes_min = 1024*4,//1024*4, + .period_bytes_max = 1024*32,//1024*32, + .periods_min = 4,//4, + .periods_max = 8,//8, + .fifo_size = 128,//32, }; struct sunxi_runtime_data { @@ -75,19 +74,23 @@ static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream) int ret; unsigned long len = prtd->dma_period; - limit = prtd->dma_limit; - while (prtd->dma_loaded < limit) { - if ((pos + len) > prtd->dma_end) - len = prtd->dma_end - pos; + limit = prtd->dma_limit; + while(prtd->dma_loaded < limit) + { + if((pos + len) > prtd->dma_end){ + len = prtd->dma_end - pos; + } ret = sunxi_dma_enqueue(prtd->params, pos, len, 0); if (ret == 0) { prtd->dma_loaded++; pos += prtd->dma_period; - if (pos >= prtd->dma_end) + if(pos >= prtd->dma_end) pos = prtd->dma_start; - } else + }else { break; + } + } prtd->dma_pos = pos; } @@ -98,8 +101,9 @@ static void sunxi_audio_buffdone(struct sunxi_dma_params *dma, void *dev_id) struct snd_pcm_substream *substream = dev_id; prtd = substream->runtime->private_data; - if (substream) - snd_pcm_period_elapsed(substream); + if (substream) { + snd_pcm_period_elapsed(substream); + } spin_lock(&prtd->lock); { @@ -126,12 +130,13 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, if (prtd->params == NULL) { prtd->params = dma; ret = sunxi_dma_request(prtd->params, 1); - if (ret < 0) - return ret; + if (ret < 0) { + return ret; + } } if (sunxi_dma_set_callback(prtd->params, sunxi_audio_buffdone, - substream) != 0) { + substream) != 0) { sunxi_dma_release(prtd->params); prtd->params = NULL; return -EINVAL; @@ -178,25 +183,25 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream) if (!prtd->params) return 0; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){ #if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I struct dma_hw_conf codec_dma_conf; - codec_dma_conf.drqsrc_type = DRQ_TYPE_SDRAM; - codec_dma_conf.drqdst_type = DRQ_TYPE_HDMIAUDIO; - codec_dma_conf.xfer_type = DMAXFER_D_BWORD_S_BWORD; - codec_dma_conf.address_type = DMAADDRT_D_IO_S_LN; - codec_dma_conf.dir = SW_DMA_WDEV; - codec_dma_conf.reload = 0; - codec_dma_conf.hf_irq = SW_DMA_IRQ_FULL; - codec_dma_conf.from = prtd->dma_start; - codec_dma_conf.to = prtd->params->dma_addr; + codec_dma_conf.drqsrc_type = DRQ_TYPE_SDRAM; + codec_dma_conf.drqdst_type = DRQ_TYPE_HDMIAUDIO; + codec_dma_conf.xfer_type = DMAXFER_D_BWORD_S_BWORD; + codec_dma_conf.address_type = DMAADDRT_D_IO_S_LN; + codec_dma_conf.dir = SW_DMA_WDEV; + codec_dma_conf.reload = 0; + codec_dma_conf.hf_irq = SW_DMA_IRQ_FULL; + codec_dma_conf.from = prtd->dma_start; + codec_dma_conf.to = prtd->params->dma_addr; #else dma_config_t codec_dma_conf; memset(&codec_dma_conf, 0, sizeof(codec_dma_conf)); codec_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT; - codec_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; + codec_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; codec_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT; - codec_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4; + codec_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4; codec_dma_conf.address_type.src_addr_mode = DDMA_ADDR_LINEAR; codec_dma_conf.address_type.dst_addr_mode = DDMA_ADDR_IO; codec_dma_conf.src_drq_type = D_SRC_SDRAM; @@ -210,8 +215,8 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream) /* flush the DMA channel */ prtd->dma_loaded = 0; - if (sunxi_dma_flush(prtd->params) == 0) - prtd->dma_pos = prtd->dma_start; + sunxi_dma_flush(prtd->params); + prtd->dma_pos = prtd->dma_start; /* enqueue dma buffers */ sunxi_pcm_enqueue(substream); @@ -258,19 +263,21 @@ static snd_pcm_uframes_t sunxi_pcm_pointer(struct snd_pcm_substream *substream) snd_pcm_uframes_t offset = 0; spin_lock(&prtd->lock); - sunxi_dma_getcurposition(prtd->params, (dma_addr_t *)&dmasrc, - (dma_addr_t *)&dmadst); + + sunxi_dma_getcurposition(prtd->params, (dma_addr_t*)&dmasrc, + (dma_addr_t*)&dmadst); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) res = dmadst - prtd->dma_start; else - offset = bytes_to_frames(runtime, dmasrc + prtd->dma_period - - runtime->dma_addr); + { + offset = bytes_to_frames(runtime, dmasrc + prtd->dma_period - runtime->dma_addr); + } spin_unlock(&prtd->lock); - if (offset >= runtime->buffer_size) + if(offset >= runtime->buffer_size) offset = 0; - return offset; + return offset; } static int sunxi_pcm_open(struct snd_pcm_substream *substream) @@ -307,21 +314,21 @@ static int sunxi_pcm_mmap(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); } static struct snd_pcm_ops sunxi_pcm_ops = { - .open = sunxi_pcm_open, + .open = sunxi_pcm_open, .close = sunxi_pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = sunxi_pcm_hw_params, + .hw_params = sunxi_pcm_hw_params, .hw_free = sunxi_pcm_hw_free, .prepare = sunxi_pcm_prepare, .trigger = sunxi_pcm_trigger, .pointer = sunxi_pcm_pointer, - .mmap = sunxi_pcm_mmap, + .mmap = sunxi_pcm_mmap, }; static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) @@ -334,7 +341,7 @@ static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) buf->dev.dev = pcm->card->dev; buf->private_data = NULL; buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); + &buf->addr, GFP_KERNEL); if (!buf->area) return -ENOMEM; buf->bytes = size; @@ -343,9 +350,9 @@ static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) static void sunxi_pcm_free_dma_buffers(struct snd_pcm *pcm) { - int stream; - struct snd_dma_buffer *buf; struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; for (stream = 0; stream < 2; stream++) { substream = pcm->streams[stream].substream; @@ -357,7 +364,7 @@ static void sunxi_pcm_free_dma_buffers(struct snd_pcm *pcm) continue; dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); + buf->area, buf->addr); buf->area = NULL; } } @@ -366,9 +373,9 @@ static u64 sunxi_pcm_mask = DMA_BIT_MASK(32); static int sunxi_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; int ret = 0; - struct snd_pcm *pcm = rtd->pcm; - struct snd_card *card = rtd->card->snd_card; if (!card->dev->dma_mask) card->dev->dma_mask = &sunxi_pcm_mask; @@ -393,15 +400,14 @@ static int sunxi_pcm_new(struct snd_soc_pcm_runtime *rtd) } static struct snd_soc_platform_driver sunxi_soc_platform_hdmiaudio = { - .ops = &sunxi_pcm_ops, - .pcm_new = sunxi_pcm_new, - .pcm_free = sunxi_pcm_free_dma_buffers, + .ops = &sunxi_pcm_ops, + .pcm_new = sunxi_pcm_new, + .pcm_free = sunxi_pcm_free_dma_buffers, }; static int __devinit sunxi_hdmiaudio_pcm_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, - &sunxi_soc_platform_hdmiaudio); + return snd_soc_register_platform(&pdev->dev, &sunxi_soc_platform_hdmiaudio); } static int __devexit sunxi_hdmiaudio_pcm_remove(struct platform_device *pdev) @@ -411,9 +417,9 @@ static int __devexit sunxi_hdmiaudio_pcm_remove(struct platform_device *pdev) } static struct platform_driver sunxi_hdmiaudio_pcm_driver = { - .probe = sunxi_hdmiaudio_pcm_probe, - .remove = __devexit_p(sunxi_hdmiaudio_pcm_remove), - .driver = { + .probe = sunxi_hdmiaudio_pcm_probe, + .remove = __devexit_p(sunxi_hdmiaudio_pcm_remove), + .driver = { .name = "sunxi-hdmiaudio-pcm-audio", .owner = THIS_MODULE, }, @@ -423,8 +429,7 @@ static struct platform_driver sunxi_hdmiaudio_pcm_driver = { static int __init sunxi_soc_platform_hdmiaudio_init(void) { int err = 0; - err = platform_driver_register(&sunxi_hdmiaudio_pcm_driver); - if (err < 0) + if ((err = platform_driver_register(&sunxi_hdmiaudio_pcm_driver)) < 0) return err; return 0; } diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.h b/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.h new file mode 100644 index 0000000..0649177 --- /dev/null +++ b/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.h @@ -0,0 +1,25 @@ +/* + * sound\soc\sunxi\hdmiaudio\sunxi-hdmipcm.h + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * chenpailin <chenpailin@allwinnertech.com> + * + * some simple description for this code + * + * 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. + * + */ + +#ifndef SUNXI_HDMIPCM_H_ +#define SUNXI_HDMIPCM_H_ + +enum sunxi_dma_buffresult { + SUNXI_RES_OK, + SUNXI_RES_ERR, + SUNXI_RES_ABORT +}; + +#endif //SUNXI_HDMIPCM_H_ diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.c b/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.c index 75157fa..c89f760 100644 --- a/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.c +++ b/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.c @@ -24,27 +24,213 @@ #include <plat/sys_config.h> #include <linux/io.h> +#include "sunxi-hdmiaudio.h" +#include "sunxi-hdmipcm.h" + +#include "sndhdmi.h" + +static struct clk *xtal; + +static int clk_users; +static DEFINE_MUTEX(clk_lock); + +#ifdef ENFORCE_RATES +static struct snd_pcm_hw_constraint_list hw_constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; +#endif + +static int sunxi_sndhdmi_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + #ifdef ENFORCE_RATES + struct snd_pcm_runtime *runtime = substream->runtime;; + #endif + mutex_lock(&clk_lock); + mutex_unlock(&clk_lock); + if (!ret) { + #ifdef ENFORCE_RATES + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_rates); + if (ret < 0) + + #endif + } + return ret; +} + +static void sunxi_sndhdmi_shutdown(struct snd_pcm_substream *substream) +{ + mutex_lock(&clk_lock); + clk_users -= 1; + if (clk_users == 0) { + clk_put(xtal); + xtal = NULL; + } + mutex_unlock(&clk_lock); +} + +typedef struct __MCLK_SET_INF +{ + __u32 samp_rate; // sample rate + __u16 mult_fs; // multiply of smaple rate + + __u8 clk_div; // mpll division + __u8 mpll; // select mpll, 0 - 24.576 Mhz, 1 - 22.5792 Mhz + +} __mclk_set_inf; + +typedef struct __BCLK_SET_INF +{ + __u8 bitpersamp; // bits per sample + __u8 clk_div; // clock division + __u16 mult_fs; // multiplay of sample rate + +} __bclk_set_inf; + +//bclk divider table +static __bclk_set_inf BCLK_INF[] = +{ + // 16bits per sample + {16, 4, 128}, {16, 6, 192}, {16, 8, 256}, + {16, 12, 384}, {16, 16, 512}, + + //24 bits per sample + {24, 4, 192}, {24, 8, 384}, {24, 16, 768}, + + //32 bits per sample + {32, 2, 128}, {32, 4, 256}, {32, 6, 384}, + {32, 8, 512}, {32, 12, 768}, + + //end flag + {0xff, 0, 0}, +}; + +//mclk divider table +static __mclk_set_inf MCLK_INF[] = +{ + // 8k bitrate + { 8000, 128, 24, 0}, { 8000, 192, 16, 0}, { 8000, 256, 12, 0}, + { 8000, 384, 8, 0}, { 8000, 512, 6, 0}, { 8000, 768, 4, 0}, + + // 16k bitrate + { 16000, 128, 12, 0}, { 16000, 192, 8, 0}, { 16000, 256, 6, 0}, + { 16000, 384, 4, 0}, { 16000, 768, 2, 0}, + + // 32k bitrate + { 32000, 128, 6, 0}, { 32000, 192, 4, 0}, { 32000, 384, 2, 0}, + { 32000, 768, 1, 0}, + + // 64k bitrate + { 64000, 192, 2, 0}, { 64000, 384, 1, 0}, + + //128k bitrate + {128000, 192, 1, 0}, + + // 12k bitrate + { 12000, 128, 16, 0}, { 12000, 256, 8, 0}, { 12000, 512, 4, 0}, + + // 24k bitrate + { 24000, 128, 8, 0}, { 24000, 256, 4, 0}, { 24000, 512, 2, 0}, + + // 48K bitrate + { 48000, 128, 4, 0}, { 48000, 256, 2, 0}, { 48000, 512, 1, 0}, + + // 96k bitrate + { 96000, 128 , 2, 0}, { 96000, 256, 1, 0}, + + //192k bitrate + {192000, 128, 1, 0}, + + //11.025k bitrate + { 11025, 128, 16, 1}, { 11205, 256, 8, 1}, { 11205, 512, 4, 1}, + + //22.05k bitrate + { 22050, 128, 8, 1}, { 22050, 256, 4, 1}, + { 22050, 512, 2, 1}, + + //44.1k bitrate + { 44100, 128, 4, 1}, { 44100, 256, 2, 1}, { 44100, 512, 1, 1}, + + //88.2k bitrate + { 88200, 128, 2, 1}, { 88200, 256, 1, 1}, + + //176.4k bitrate + {176400, 128, 1, 1}, + + //end flag 0xffffffff + {0xffffffff, 0, 0, 0}, +}; + +static s32 get_clock_divder(u32 sample_rate, u32 sample_width, u32 * mclk_div, + u32* mpll, u32* bclk_div, u32* mult_fs) +{ + u32 i, j, ret = -EINVAL; + + for(i=0; i< 100; i++) { + if((MCLK_INF[i].samp_rate == sample_rate) && + ((MCLK_INF[i].mult_fs == 256) || (MCLK_INF[i].mult_fs == 128))) { + for(j=0; j<ARRAY_SIZE(BCLK_INF); j++) { + if((BCLK_INF[j].bitpersamp == sample_width) && + (BCLK_INF[j].mult_fs == MCLK_INF[i].mult_fs)) { + //set mclk and bclk division + *mclk_div = MCLK_INF[i].clk_div; + *mpll = MCLK_INF[i].mpll; + *bclk_div = BCLK_INF[j].clk_div; + *mult_fs = MCLK_INF[i].mult_fs; + ret = 0; + break; + } + } + }else if(MCLK_INF[i].samp_rate == 0xffffffff) + break; + } + + return ret; +} + static int sunxi_sndhdmi_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; int ret = 0; - struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai *codec_dai; - struct snd_soc_dai *cpu_dai; + unsigned long rate = params_rate(params); + u32 mclk_div=0, mpll=0, bclk_div=0, mult_fs=0; - if (!substream) { - printk("error:%s,line:%d\n", __func__, __LINE__); - return -EAGAIN; - } - rtd = substream->private_data; - codec_dai = rtd->codec_dai; - cpu_dai = rtd->cpu_dai; + get_clock_divder(rate, 32, &mclk_div, &mpll, &bclk_div, &mult_fs); + + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0 , mpll, 0); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, 0 , mpll, 0); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_MCLK, mclk_div); + if (ret < 0) + return ret; - ret = snd_soc_dai_set_fmt(codec_dai, 0); + ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_BCLK, bclk_div); if (ret < 0) return ret; - ret = snd_soc_dai_set_fmt(cpu_dai, 0); + ret = snd_soc_dai_set_clkdiv(codec_dai, 0, mult_fs); if (ret < 0) return ret; @@ -52,24 +238,26 @@ static int sunxi_sndhdmi_hw_params(struct snd_pcm_substream *substream, } static struct snd_soc_ops sunxi_sndhdmi_ops = { - .hw_params = sunxi_sndhdmi_hw_params, + .startup = sunxi_sndhdmi_startup, + .shutdown = sunxi_sndhdmi_shutdown, + .hw_params = sunxi_sndhdmi_hw_params, }; static struct snd_soc_dai_link sunxi_sndhdmi_dai_link = { - .name = "HDMIAUDIO", - .stream_name = "SUNXI-HDMIAUDIO", - .cpu_dai_name = "sunxi-hdmiaudio.0", + .name = "HDMIAUDIO", + .stream_name = "SUNXI-HDMIAUDIO", + .cpu_dai_name = "sunxi-hdmiaudio.0", .codec_dai_name = "sndhdmi", - .platform_name = "sunxi-hdmiaudio-pcm-audio.0", - .codec_name = "sunxi-hdmiaudio-codec.0", - .ops = &sunxi_sndhdmi_ops, + .platform_name = "sunxi-hdmiaudio-pcm-audio.0", + .codec_name = "sunxi-hdmiaudio-codec.0", + .ops = &sunxi_sndhdmi_ops, }; static struct snd_soc_card snd_soc_sunxi_sndhdmi = { - .name = "sunxi-sndhdmi", + .name = "sunxi-sndhdmi", .owner = THIS_MODULE, - .dai_link = &sunxi_sndhdmi_dai_link, - .num_links = 1, + .dai_link = &sunxi_sndhdmi_dai_link, + .num_links = 1, }; static int __devinit sunxi_sndhdmi_probe(struct platform_device *pdev) diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.h b/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.h new file mode 100644 index 0000000..341aee9 --- /dev/null +++ b/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.h @@ -0,0 +1,26 @@ +/* + * sound\soc\sunxi\hdmiaudio\sunxi-sndhdmi.h + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * chenpailin <chenpailin@allwinnertech.com> + * + * some simple description for this code + * + * 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. + * + */ + +#ifndef SUNXI_SNDHDMI_H_ +#define SUNXI_SNDHDMI_H_ + +struct sunxi_sndhdmi_platform_data { + int hdmiaudio_bclk; + int hdmiaudio_ws; + int hdmiaudio_data; + void (*power)(int); + int model; +} +#endif diff --git a/sound/soc/sunxi/i2s/Kconfig b/sound/soc/sunxi/i2s/Kconfig index a6dff42..f244faa 100644 --- a/sound/soc/sunxi/i2s/Kconfig +++ b/sound/soc/sunxi/i2s/Kconfig @@ -1,5 +1,5 @@ config SND_SUNXI_SOC_I2S_INTERFACE - tristate "SoC i2s interface for the AllWinner sun4i and sun5i chips" + tristate "SoC i2s interface for the AllWinner sun4i, sun5i and sun7i chips" default m help Say Y or M if you want to add support for codecs attached to diff --git a/sound/soc/sunxi/i2s/sndi2s.c b/sound/soc/sunxi/i2s/sndi2s.c index 68bad02..92e01a8 100644 --- a/sound/soc/sunxi/i2s/sndi2s.c +++ b/sound/soc/sunxi/i2s/sndi2s.c @@ -35,11 +35,20 @@ struct sndi2s_priv { }; static int i2s_used = 0; -#define sndi2s_RATES (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT) -#define sndi2s_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE) +static int sunxi_i2s_slave = 0; + +#define sndi2s_RATES_MASTER (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT) +#define sndi2s_RATES_SLAVE (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000|\ + SNDRV_PCM_RATE_352800 | SNDRV_PCM_RATE_384000) -hdmi_audio_t hdmi_parameter; +#if defined CONFIG_ARCH_SUN7I +#define sndi2s_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#else +#define sndi2s_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_LE) +#endif static int sndi2s_mute(struct snd_soc_dai *dai, int mute) { @@ -62,8 +71,6 @@ static int sndi2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - hdmi_parameter.sample_rate = params_rate(params); - return 0; } @@ -75,9 +82,6 @@ static int sndi2s_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int sndi2s_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { - - hdmi_parameter.fs_between = div; - return 0; } @@ -104,7 +108,14 @@ struct snd_soc_dai_driver sndi2s_dai = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, - .rates = sndi2s_RATES, + .rates = sndi2s_RATES_MASTER, + .formats = sndi2s_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = sndi2s_RATES_MASTER, .formats = sndi2s_FORMATS, }, /* pcm operations */ @@ -129,10 +140,8 @@ static int sndi2s_soc_probe(struct snd_soc_codec *codec) /* power down chip */ static int sndi2s_soc_remove(struct snd_soc_codec *codec) { - struct sndhdmi_priv *sndi2s = snd_soc_codec_get_drvdata(codec); - + struct sndi2s_priv *sndi2s = snd_soc_codec_get_drvdata(codec); kfree(sndi2s); - return 0; } @@ -143,6 +152,13 @@ static struct snd_soc_codec_driver soc_codec_dev_sndi2s = { static int __devinit sndi2s_codec_probe(struct platform_device *pdev) { + if(sunxi_i2s_slave) { + sndi2s_dai.playback.rates = sndi2s_RATES_SLAVE; + sndi2s_dai.capture.rates = sndi2s_RATES_SLAVE; + printk("[I2S-0] sndi2s_codec_probe I2S used in slave mode\n"); + } + else + printk("[I2S-0] sndi2s_codec_probe I2S used in master mode\n"); return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sndi2s, &sndi2s_dai, 1); } @@ -169,21 +185,31 @@ static struct platform_driver sndi2s_codec_driver = { static int __init sndi2s_codec_init(void) { int err = 0; - int ret = 0; + int ret = 0, i2s_slave = 0; ret = script_parser_fetch("i2s_para","i2s_used", &i2s_used, sizeof(int)); if (ret) { - printk("[I2S]sndi2s_init fetch i2s using configuration failed\n"); + printk("[I2S-0] sndi2s_init fetch i2s using configuration failed\n"); } if (i2s_used) { + ret = script_parser_fetch("i2s_para","i2s_slave", &i2s_slave, sizeof(int)); + if(ret == 0 && i2s_slave == 1) { + sunxi_i2s_slave = 1; + printk("[I2S-0] sndi2s_codec_init I2S used in slave mode\n"); + } + else { + sunxi_i2s_slave = 0; + printk("[I2S-0] sndi2s_codec_init I2S used in master mode\n"); + } + if((err = platform_device_register(&sndi2s_codec_device)) < 0) return err; if ((err = platform_driver_register(&sndi2s_codec_driver)) < 0) return err; } else { - printk("[I2S]sndi2s cannot find any using configuration for controllers, return directly!\n"); + printk("[I2S-0] sndi2s cannot find any using configuration for controllers, return directly!\n"); return 0; } @@ -204,3 +230,4 @@ MODULE_DESCRIPTION("SNDI2S ALSA soc codec driver"); MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:sunxi-i2s-codec"); + diff --git a/sound/soc/sunxi/i2s/sndi2s.h b/sound/soc/sunxi/i2s/sndi2s.h index e5b95c8..57e2aa5 100644 --- a/sound/soc/sunxi/i2s/sndi2s.h +++ b/sound/soc/sunxi/i2s/sndi2s.h @@ -16,6 +16,8 @@ #ifndef SNDI2S_H_ #define SNDI2S_H_ +#if 0 +cleaning code typedef struct hdmi_audio { __u8 hw_intf; /* 0:iis 1:spdif 2:pcm */ @@ -54,5 +56,6 @@ typedef enum tag_HDMI_CMD HDMI_CMD_AUDIO_ENABLE, HDMI_CMD_GET_HPD_STATUS, }__hdmi_cmd_t; +#endif #endif diff --git a/sound/soc/sunxi/i2s/sunxi-i2s.c b/sound/soc/sunxi/i2s/sunxi-i2s.c index b4dd3ad..f136f80 100644 --- a/sound/soc/sunxi/i2s/sunxi-i2s.c +++ b/sound/soc/sunxi/i2s/sunxi-i2s.c @@ -38,7 +38,7 @@ #include "sunxi-i2sdma.h" #include "sunxi-i2s.h" -static int regsave[8]; +static int regsave[10]; static int i2s_used = 0; static struct sunxi_dma_params sunxi_i2s_pcm_stereo_out = { @@ -58,13 +58,16 @@ static struct sunxi_dma_params sunxi_i2s_pcm_stereo_in = { }; - struct sunxi_i2s_info sunxi_iis; +/* most of fields of this structure is never initialized and useless !!!*/ +struct sunxi_i2s_info sunxi_iis; static u32 i2s_handle = 0; - static struct clk *i2s_apbclk, *i2s_pll2clk, *i2s_pllx8, *i2s_moduleclk; +static struct clk *i2s_apbclk, *i2s_pll2clk, *i2s_pllx8, *i2s_moduleclk; void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on) { u32 reg_val; + int res; + printk("[I2S-0] sunxi_snd_txctrl_i2s is on=(%d)\n", on); reg_val = readl(sunxi_iis.regs + SUNXI_TXCHSEL); reg_val &= ~0x7; @@ -73,7 +76,7 @@ void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on) reg_val = readl(sunxi_iis.regs + SUNXI_TXCHMAP); reg_val = 0; - if (sunxi_is_sun4i()) { + if (sunxi_is_sun4i() || sunxi_is_sun7i()) { if(substream->runtime->channels == 1) { reg_val = 0x76543200; } else { @@ -89,7 +92,7 @@ void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on) writel(reg_val, sunxi_iis.regs + SUNXI_TXCHMAP); reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - if (sunxi_is_sun4i()) { + if (sunxi_is_sun4i() || sunxi_is_sun7i()) { reg_val &= ~SUNXI_IISCTL_SDO3EN; reg_val &= ~SUNXI_IISCTL_SDO2EN; reg_val &= ~SUNXI_IISCTL_SDO1EN; @@ -144,12 +147,13 @@ void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on) reg_val |= SUNXI_IISINT_TXDRQEN; writel(reg_val, sunxi_iis.regs + SUNXI_IISINT); - //Global Enable Digital Audio Interface - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - reg_val |= SUNXI_IISCTL_GEN; - writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); + //Disable mute + res = gpio_write_one_pin_value(i2s_handle, 0, "i2s_mute"); } else { + //Enable mute + res = gpio_write_one_pin_value(i2s_handle, 1, "i2s_mute"); + /* IIS TX DISABLE */ reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); reg_val &= ~SUNXI_IISCTL_TXEN; @@ -159,17 +163,26 @@ void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on) reg_val = readl(sunxi_iis.regs + SUNXI_IISINT); reg_val &= ~SUNXI_IISINT_TXDRQEN; writel(reg_val, sunxi_iis.regs + SUNXI_IISINT); - - //Global disable Digital Audio Interface - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - reg_val &= ~SUNXI_IISCTL_GEN; - writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); } } -void sunxi_snd_rxctrl_i2s(int on) +void sunxi_snd_rxctrl_i2s(struct snd_pcm_substream *substream, int on) { u32 reg_val; + printk("[I2S-0] sunxi_snd_rxctrl_i2s is on=(%d)\n", on); + reg_val = readl(sunxi_iis.regs + SUNXI_RXCHSEL); + reg_val &= ~0x7; + reg_val |= SUNXI_RXCHSEL_CHNUM(substream->runtime->channels); + writel(reg_val, sunxi_iis.regs + SUNXI_RXCHSEL); + + reg_val = readl(sunxi_iis.regs + SUNXI_RXCHMAP); + reg_val = 0; + if(substream->runtime->channels == 1) { + reg_val = 0x00003200; + } else { + reg_val = 0x00003210; + } + writel(reg_val, sunxi_iis.regs + SUNXI_RXCHMAP); //flush RX FIFO reg_val = readl(sunxi_iis.regs + SUNXI_IISFCTL); @@ -190,63 +203,52 @@ void sunxi_snd_rxctrl_i2s(int on) reg_val |= SUNXI_IISINT_RXDRQEN; writel(reg_val, sunxi_iis.regs + SUNXI_IISINT); - //Global Enable Digital Audio Interface - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - reg_val |= SUNXI_IISCTL_GEN; - writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); - } else { /* IIS RX DISABLE */ reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); reg_val &= ~SUNXI_IISCTL_RXEN; writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); - /* DISBALE dma DRQ mode */ + /* DISABLE dma DRQ mode */ reg_val = readl(sunxi_iis.regs + SUNXI_IISINT); reg_val &= ~SUNXI_IISINT_RXDRQEN; writel(reg_val, sunxi_iis.regs + SUNXI_IISINT); - - //Global disable Digital Audio Interface - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - reg_val &= ~SUNXI_IISCTL_GEN; - writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); } } -static inline int sunxi_snd_is_clkmaster(void) -{ - return ((readl(sunxi_iis.regs + SUNXI_IISCTL) & SUNXI_IISCTL_MS) ? 0 : 1); -} - static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { u32 reg_val; u32 reg_val1; + printk("[IIS-0] sunxi_i2s_set_fmt\n"); + //SDO ON reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - if (sunxi_is_sun4i()) { + if (sunxi_is_sun4i() || sunxi_is_sun7i()) { reg_val |= (SUNXI_IISCTL_SDO0EN | SUNXI_IISCTL_SDO1EN | SUNXI_IISCTL_SDO2EN | SUNXI_IISCTL_SDO3EN); } else { reg_val |= SUNXI_IISCTL_SDO0EN; } - writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); + + //writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); /* master or slave selection */ - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - switch(fmt & SND_SOC_DAIFMT_MASTER_MASK){ - case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ - reg_val |= SUNXI_IISCTL_MS; - break; - case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */ - reg_val &= ~SUNXI_IISCTL_MS; - break; - default: - return -EINVAL; + //reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); + if(sunxi_iis.slave) + { + reg_val |= SUNXI_IISCTL_MS; // 1: Slave! + printk("[IIS-0] sunxi_i2s_set_fmt: set slave mode for I2S interface\n"); + } + else + { + reg_val &= ~SUNXI_IISCTL_MS; // 0: Master! + printk("[IIS-0] sunxi_i2s_set_fmt: set master mode for I2S interface\n"); } writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); + sunxi_iis.lrc_pol = 0; /* pcm or i2s mode selection */ reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); reg_val1 = readl(sunxi_iis.regs + SUNXI_IISFAT0); @@ -255,24 +257,36 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) case SND_SOC_DAIFMT_I2S: /* I2S mode */ reg_val &= ~SUNXI_IISCTL_PCM; reg_val1 |= SUNXI_IISFAT0_FMT_I2S; + sunxi_iis.samp_format = 0; + printk("[IIS-0] sunxi_i2s_set_fmt: set I2S mode\n"); break; case SND_SOC_DAIFMT_RIGHT_J: /* Right Justified mode */ reg_val &= ~SUNXI_IISCTL_PCM; reg_val1 |= SUNXI_IISFAT0_FMT_RGT; + sunxi_iis.samp_format = 2; + printk("[IIS-0] sunxi_i2s_set_fmt: set Right Justified mode\n"); break; case SND_SOC_DAIFMT_LEFT_J: /* Left Justified mode */ reg_val &= ~SUNXI_IISCTL_PCM; reg_val1 |= SUNXI_IISFAT0_FMT_LFT; + sunxi_iis.samp_format = 1; + /*printk("[IIS-0] sunxi_i2s_set_fmt: set Left Justified mode\n");*/ break; case SND_SOC_DAIFMT_DSP_A: /* L data msb after FRM LRC */ reg_val |= SUNXI_IISCTL_PCM; reg_val1 &= ~SUNXI_IISFAT0_LRCP; + sunxi_iis.samp_format = 3; + printk("[IIS-0] sunxi_i2s_set_fmt: set L data msb after FRM LRC mode\n"); break; case SND_SOC_DAIFMT_DSP_B: /* L data msb during FRM LRC */ reg_val |= SUNXI_IISCTL_PCM; reg_val1 |= SUNXI_IISFAT0_LRCP; + sunxi_iis.samp_format = 3; + sunxi_iis.lrc_pol = 1; + printk("[IIS-0] sunxi_i2s_set_fmt: set L data msb during FRM LRC mode\n"); break; default: + printk("[IIS-0] sunxi_i2s_set_fmt: unknown mode\n"); return -EINVAL; } writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); @@ -284,25 +298,41 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ reg_val1 &= ~SUNXI_IISFAT0_LRCP; reg_val1 &= ~SUNXI_IISFAT0_BCP; + sunxi_iis.lrc_pol = 0; + sunxi_iis.bclk_pol = 0; + printk("[IIS-0] sunxi_i2s_set_fmt: normal bit clock + frame\n"); break; case SND_SOC_DAIFMT_NB_IF: /* normal bclk + inv frm */ reg_val1 |= SUNXI_IISFAT0_LRCP; reg_val1 &= ~SUNXI_IISFAT0_BCP; + sunxi_iis.lrc_pol = 1; + sunxi_iis.bclk_pol = 0; + printk("[IIS-0] sunxi_i2s_set_fmt: normal bclk + inv frm\n"); break; case SND_SOC_DAIFMT_IB_NF: /* invert bclk + nor frm */ reg_val1 &= ~SUNXI_IISFAT0_LRCP; reg_val1 |= SUNXI_IISFAT0_BCP; + sunxi_iis.lrc_pol = 0; + sunxi_iis.bclk_pol = 1; + printk("[IIS-0] sunxi_i2s_set_fmt: invert bclk + nor frm\n"); break; case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */ reg_val1 |= SUNXI_IISFAT0_LRCP; reg_val1 |= SUNXI_IISFAT0_BCP; + sunxi_iis.lrc_pol = 1; + sunxi_iis.bclk_pol = 1; + printk("[IIS-0] sunxi_i2s_set_fmt: invert bclk + frm\n"); break; } writel(reg_val1, sunxi_iis.regs + SUNXI_IISFAT0); - /* word select size */ + /* clear word select size */ reg_val = readl(sunxi_iis.regs + SUNXI_IISFAT0); reg_val &= ~SUNXI_IISFAT0_WSS_32BCLK; + /* word size hardcoded to 32 (ref. sunxi-sndi2s.c func. sunxi_sndi2s_hw_params()) */ + sunxi_iis.ws_size = 32; + printk("[IIS-0] sunxi_i2s_set_fmt: word size = %d\n", sunxi_iis.ws_size); + /* if(sunxi_iis.ws_size == 16) reg_val |= SUNXI_IISFAT0_WSS_16BCLK; else if(sunxi_iis.ws_size == 20) @@ -310,47 +340,57 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) else if(sunxi_iis.ws_size == 24) reg_val |= SUNXI_IISFAT0_WSS_24BCLK; else - reg_val |= SUNXI_IISFAT0_WSS_32BCLK; + */ + reg_val |= SUNXI_IISFAT0_WSS_32BCLK; + writel(reg_val, sunxi_iis.regs + SUNXI_IISFAT0); /* PCM REGISTER setup */ - reg_val = sunxi_iis.pcm_txtype&0x3; - reg_val |= sunxi_iis.pcm_rxtype<<2; + reg_val = 0; + //reg_val = sunxi_iis.pcm_txtype&0x3; + //reg_val |= sunxi_iis.pcm_rxtype<<2; if(!sunxi_iis.pcm_sync_type) + { reg_val |= SUNXI_IISFAT1_SSYNC; //short sync + printk("[IIS-0] sunxi_i2s_set_fmt: set pcm_sync_type = short sync\n"); + } if(sunxi_iis.pcm_sw == 16) + { reg_val |= SUNXI_IISFAT1_SW; + printk("[IIS-0] sunxi_i2s_set_fmt: pcm_sw == 16\n"); + } reg_val |=((sunxi_iis.pcm_start_slot - 1)&0x3)<<6; //start slot index reg_val |= sunxi_iis.pcm_lsb_first<<9; //MSB or LSB first if(sunxi_iis.pcm_sync_period == 256) - reg_val |= 0x4<<12; + reg_val |= SUNXI_IISFAT1_SYNCLEN_256BCLK; else if (sunxi_iis.pcm_sync_period == 128) - reg_val |= 0x3<<12; + reg_val |= SUNXI_IISFAT1_SYNCLEN_128BCLK; else if (sunxi_iis.pcm_sync_period == 64) - reg_val |= 0x2<<12; + reg_val |= SUNXI_IISFAT1_SYNCLEN_64BCLK; else if (sunxi_iis.pcm_sync_period == 32) - reg_val |= 0x1<<12; + reg_val |= SUNXI_IISFAT1_SYNCLEN_32BCLK; writel(reg_val, sunxi_iis.regs + SUNXI_IISFAT1); /* set FIFO control register */ - reg_val = 0 & 0x3; - reg_val |= (1 & 0x1)<<2; - reg_val |= SUNXI_IISFCTL_RXTL(0xf); //RX FIFO trigger level - reg_val |= SUNXI_IISFCTL_TXTL(0x40); //TX FIFO empty trigger level + reg_val = 0; + reg_val |= SUNXI_IISFCTL_RXTL(0xf); //RX FIFO trigger level + reg_val |= SUNXI_IISFCTL_TXTL(0x40); //TX FIFO empty trigger level writel(reg_val, sunxi_iis.regs + SUNXI_IISFCTL); return 0; } static int sunxi_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { + u32 reg_val; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sunxi_dma_params *dma_data; + printk("[IIS-0] sunxi_i2s_hw_params: %s\n", substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "recording"); /* play or record */ if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -358,7 +398,48 @@ static int sunxi_i2s_hw_params(struct snd_pcm_substream *substream, else dma_data = &sunxi_i2s_pcm_stereo_in; + /* set format info */ + reg_val = readl(sunxi_iis.regs + SUNXI_IISFAT0); + /* clear sample resolution select size */ + reg_val &= ~SUNXI_IISFAT0_SR_RVD; + + switch (params_format(params)) + { + case SNDRV_PCM_FORMAT_S16_LE: + reg_val |= SUNXI_IISFAT0_SR_16BIT; + sunxi_iis.samp_res = 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + reg_val |= SUNXI_IISFAT0_SR_20BIT; + sunxi_iis.samp_res = 20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + reg_val |= SUNXI_IISFAT0_SR_24BIT; + sunxi_iis.samp_res = 24; + break; + default: + pr_err("[IIS-0] sunxi_i2s_hw_params: Unsupported format (%d)\n", (int)params_format(params)); + return -EINVAL; + } + writel(reg_val, sunxi_iis.regs + SUNXI_IISFAT0); + + /* set FIFO control register */ + reg_val = readl(sunxi_iis.regs + SUNXI_IISFCTL); + reg_val |= SUNXI_IISFCTL_TXIM_MOD1; //1: Valid data at the LSB of TXFIFO register + //CHECK EXPANDING FORMAT!!! + if(sunxi_iis.samp_res == 24) { + reg_val &= ~SUNXI_IISFCTL_RXOM_MOD3; //00: Expanding 0 at LSB of DA_RXFIFO register + } + else { + reg_val |= SUNXI_IISFCTL_RXOM_MOD1; //00: Expanding 0 at LSB of DA_RXFIFO register + } + writel(reg_val, sunxi_iis.regs + SUNXI_IISFCTL); + snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data); + sunxi_iis.samp_fs = params_rate(params); + sunxi_iis.channel_num = params_channels(params); + printk("[IIS-0] sunxi_i2s_hw_params: channel num %d, format %d bit, sample rate %d\n", + sunxi_iis.channel_num, sunxi_iis.samp_res, sunxi_iis.samp_fs); return 0; } @@ -375,7 +456,7 @@ static int sunxi_i2s_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - sunxi_snd_rxctrl_i2s(1); + sunxi_snd_rxctrl_i2s(substream, 1); } else { sunxi_snd_txctrl_i2s(substream, 1); } @@ -385,7 +466,7 @@ static int sunxi_i2s_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - sunxi_snd_rxctrl_i2s(0); + sunxi_snd_rxctrl_i2s(substream, 0); } else { sunxi_snd_txctrl_i2s(substream, 0); } @@ -403,9 +484,17 @@ static int sunxi_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { if (!freq) { - clk_set_rate(i2s_pll2clk, 24576000); + /*printk("[IIS-0] sunxi_i2s_set_sysclk: set sysclk=24576000\n");*/ + if(sunxi_iis.slave) + gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_sel"); + else + clk_set_rate(i2s_pll2clk, 24576000); } else { - clk_set_rate(i2s_pll2clk, 22579200); + /*printk("[IIS-0] sunxi_i2s_set_sysclk: set sysclk=22579200\n");*/ + if(sunxi_iis.slave) + gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_sel"); + else + clk_set_rate(i2s_pll2clk, 22579200); } return 0; @@ -414,6 +503,27 @@ static int sunxi_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, static int sunxi_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div) { u32 reg; + /*printk("[IIS-0] sunxi_i2s_set_clkdiv: PLL clock div_id=(%s), div=(%d)\n", + div_id == SUNXI_DIV_MCLK ? "SUNXI_DIV_MCLK" : + (div_id == SUNXI_DIV_BCLK ? "SUNXI_DIV_BCLK" : "SUNXI_DIV_EXTCLK"), div);*/ + + + if(sunxi_iis.slave) { + if(div_id != SUNXI_DIV_EXTCLK) { + pr_err("[I2S-0] try to set external clock divider failed\n"); + return -EINVAL; + } + /*printk("[IIS-0] sunxi_i2s_set_clkdiv: external clock, div=(%d)\n", div);*/ + } + else { + if(div_id != SUNXI_DIV_MCLK && div_id != SUNXI_DIV_BCLK) { + pr_err("[I2S-0] try to set PLL clock divider failed\n"); + return -EINVAL; + } + /*printk("[IIS-0] sunxi_i2s_set_clkdiv: PLL clock div_id=(%s), div=(%d)\n", + div_id == SUNXI_DIV_MCLK ? "SUNXI_DIV_MCLK" : "SUNXI_DIV_BCLK", div);*/ + } + switch (div_id) { case SUNXI_DIV_MCLK: if(div <= 8) @@ -447,18 +557,43 @@ static int sunxi_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div reg = (readl(sunxi_iis.regs + SUNXI_IISCLKD) & ~SUNXI_IISCLKD_BCLK_MASK) | (div <<SUNXI_IISCLKD_BCLK_OFFS); writel(reg, sunxi_iis.regs + SUNXI_IISCLKD); break; + case SUNXI_DIV_EXTCLK: + /*printk("[IIS-0] sunxi_i2s_set_clkdiv set divider=(%d)\n", div);*/ + if(div == 512) { + gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_div1"); + gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_div0"); + } + else if(div == 256) { + gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div1"); + gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_div0"); + } + else if(div == 128) { + gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div1"); + gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div0"); + } + else if(div == 64) { + gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_div1"); + gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div0"); + } + else { + pr_err("[I2S-0] try to set unsupported external clock divider div=(%d)\n", div); + return -EINVAL; + } + break; default: return -EINVAL; } - //diable MCLK output when high samplerate + //disable MCLK output when high samplerate or slave mode reg = readl(sunxi_iis.regs + SUNXI_IISCLKD); - if (!(reg & 0xF)) { + if (!(reg & 0xF) || sunxi_iis.slave) { reg &= ~SUNXI_IISCLKD_MCLKOEN; writel(reg, sunxi_iis.regs + SUNXI_IISCLKD); + /*printk("[IIS-0] sunxi_i2s_set_clkdiv: disable MCLK\n");*/ } else { reg |= SUNXI_IISCLKD_MCLKOEN; writel(reg, sunxi_iis.regs + SUNXI_IISCLKD); + /*printk("[IIS-0] sunxi_i2s_set_clkdiv: enable MCLK\n");*/ } return 0; @@ -483,6 +618,8 @@ static void iisregsave(void) regsave[5] = readl(sunxi_iis.regs + SUNXI_IISCLKD); regsave[6] = readl(sunxi_iis.regs + SUNXI_TXCHSEL); regsave[7] = readl(sunxi_iis.regs + SUNXI_TXCHMAP); + regsave[8] = readl(sunxi_iis.regs + SUNXI_RXCHSEL); + regsave[9] = readl(sunxi_iis.regs + SUNXI_RXCHMAP); } static void iisregrestore(void) @@ -495,57 +632,71 @@ static void iisregrestore(void) writel(regsave[5], sunxi_iis.regs + SUNXI_IISCLKD); writel(regsave[6], sunxi_iis.regs + SUNXI_TXCHSEL); writel(regsave[7], sunxi_iis.regs + SUNXI_TXCHMAP); + writel(regsave[8], sunxi_iis.regs + SUNXI_RXCHSEL); + writel(regsave[9], sunxi_iis.regs + SUNXI_RXCHMAP); } static int sunxi_i2s_suspend(struct snd_soc_dai *cpu_dai) { u32 reg_val; - printk("[IIS]Entered %s\n", __func__); + printk("[I2S-0] Entered %s\n", __func__); - //Global Enable Digital Audio Interface + //Global Disable Digital Audio Interface reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); reg_val &= ~SUNXI_IISCTL_GEN; writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); iisregsave(); - //release the module clock - clk_disable(i2s_moduleclk); - + if(!sunxi_iis.slave) { + //release the module clock, only for master mode + clk_disable(i2s_moduleclk); + } clk_disable(i2s_apbclk); //printk("[IIS]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008); - printk("[IIS]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); - printk("[IIS]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__); + printk("[I2S-0] SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); + printk("[I2S-0] SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__); return 0; } static int sunxi_i2s_resume(struct snd_soc_dai *cpu_dai) { u32 reg_val; - printk("[IIS]Entered %s\n", __func__); + printk("[I2S-0] Entered %s\n", __func__); - //release the module clock + //enable the module clock clk_enable(i2s_apbclk); - //release the module clock - clk_enable(i2s_moduleclk); + if(!sunxi_iis.slave) { + + //enable the module clock + clk_enable(i2s_moduleclk); + } iisregrestore(); //Global Enable Digital Audio Interface reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); + if(sunxi_iis.slave) + reg_val |= SUNXI_IISCTL_MS; // 1: Slave! + else + reg_val &= ~SUNXI_IISCTL_MS; // 0: Master! reg_val |= SUNXI_IISCTL_GEN; writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); //printk("[IIS]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008); - printk("[IIS]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); - printk("[IIS]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__); + printk("[I2S-0] SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); + printk("[I2S-0] SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__); return 0; } -#define SUNXI_I2S_RATES (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT) +#define SUNXI_I2S_RATES_MASTER (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT) +#define SUNXI_I2S_RATES_SLAVE (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_352800 | SNDRV_PCM_RATE_384000) static struct snd_soc_dai_ops sunxi_iis_dai_ops = { .trigger = sunxi_i2s_trigger, .hw_params = sunxi_i2s_hw_params, @@ -562,14 +713,14 @@ static struct snd_soc_dai_driver sunxi_iis_dai = { .playback = { .channels_min = 1, .channels_max = 2, - .rates = SUNXI_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + .rates = SUNXI_I2S_RATES_MASTER, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, .capture = { .channels_min = 1, .channels_max = 2, - .rates = SUNXI_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + .rates = SUNXI_I2S_RATES_MASTER, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, .symmetric_rates = 1, .ops = &sunxi_iis_dai_ops, @@ -579,42 +730,65 @@ static int __devinit sunxi_i2s_dev_probe(struct platform_device *pdev) { int reg_val = 0; int ret; + printk("[I2S-0] Entered %s\n", __func__); sunxi_iis.regs = ioremap(SUNXI_IISBASE, 0x100); if (sunxi_iis.regs == NULL) return -ENXIO; //i2s apbclk - i2s_apbclk = clk_get(NULL, "apb_i2s"); + i2s_apbclk = clk_get(NULL, +#if defined CONFIG_ARCH_SUN7I + "apb_i2s0" +#else + "apb_i2s" +#endif + ); + if(-1 == clk_enable(i2s_apbclk)){ - printk("i2s_apbclk failed! line = %d\n", __LINE__); + pr_err("[I2S-0] i2s_apbclk failed! line = %d\n", __LINE__); goto out; } - i2s_pllx8 = clk_get(NULL, "audio_pllx8"); - - //i2s pll2clk - i2s_pll2clk = clk_get(NULL, "audio_pll"); - - //i2s module clk - i2s_moduleclk = clk_get(NULL, "i2s"); + if(!sunxi_iis.slave) { + + i2s_pllx8 = clk_get(NULL, "audio_pllx8"); + //i2s pll2clk + i2s_pll2clk = clk_get(NULL, "audio_pll"); + //i2s module clk + i2s_moduleclk = clk_get(NULL, +#if defined CONFIG_ARCH_SUN7I + "i2s0" +#else + "i2s" +#endif + ); - if(clk_set_parent(i2s_moduleclk, i2s_pll2clk)){ - printk("try to set parent of i2s_moduleclk to i2s_pll2ck failed! line = %d\n",__LINE__); - goto out1; - } + if(clk_set_parent(i2s_moduleclk, i2s_pll2clk)){ + pr_err("[I2S-0] try to set parent of i2s_moduleclk to i2s_pll2ck failed! line = %d\n",__LINE__); + goto out1; + } - if(clk_set_rate(i2s_moduleclk, 24576000/8)){ - printk("set i2s_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__); - goto out1; - } + if(clk_set_rate(i2s_moduleclk, 24576000/8)){ + pr_err("[I2S-0] set i2s_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__); + goto out1; + } - if(-1 == clk_enable(i2s_moduleclk)){ - printk("open i2s_moduleclk failed! line = %d\n", __LINE__); - goto out1; - } + if(-1 == clk_enable(i2s_moduleclk)){ + pr_err("[I2S-0] open i2s_moduleclk failed! line = %d\n", __LINE__); + goto out1; + } + } else + { + sunxi_iis_dai.playback.rates = SUNXI_I2S_RATES_SLAVE; + sunxi_iis_dai.capture.rates = SUNXI_I2S_RATES_SLAVE; + } reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); + if(sunxi_iis.slave) + reg_val |= SUNXI_IISCTL_MS; // 1: Slave! + else + reg_val &= ~SUNXI_IISCTL_MS; // 0: Master! reg_val |= SUNXI_IISCTL_GEN; writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); @@ -627,7 +801,8 @@ static int __devinit sunxi_i2s_dev_probe(struct platform_device *pdev) goto out; out2: - clk_disable(i2s_moduleclk); + if(!sunxi_iis.slave) + clk_disable(i2s_moduleclk); out1: clk_disable(i2s_apbclk); out: @@ -636,17 +811,26 @@ static int __devinit sunxi_i2s_dev_probe(struct platform_device *pdev) static int __devexit sunxi_i2s_dev_remove(struct platform_device *pdev) { + int reg_val = 0; + printk("[I2S-0] Entered %s\n", __func__); + if(i2s_used) { - i2s_used = 0; - //release the module clock - clk_disable(i2s_moduleclk); + //Global disable Digital Audio Interface + reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); + reg_val &= ~SUNXI_IISCTL_GEN; + writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); - //release pllx8clk - clk_put(i2s_pllx8); + i2s_used = 0; + if(!sunxi_iis.slave) { + //release the module clock + clk_disable(i2s_moduleclk); - //release pll2clk - clk_put(i2s_pll2clk); + //release pllx8clk + clk_put(i2s_pllx8); + //release pll2clk + clk_put(i2s_pll2clk); + } //release apbclk clk_put(i2s_apbclk); @@ -674,15 +858,26 @@ static struct platform_driver sunxi_i2s_driver = { static int __init sunxi_i2s_init(void) { - int err = 0; + int err = 0, i2s_slave = 0; int ret; + printk("[I2S-0] Entered %s\n", __func__); + ret = script_parser_fetch("i2s_para","i2s_used", &i2s_used, sizeof(int)); if (ret) { - printk("[I2S]sunxi_i2s_init fetch i2s using configuration failed\n"); - } + printk("[I2S-0] sunxi_i2s_init fetch i2s using configuration failed\n"); + } if (i2s_used) { + ret = script_parser_fetch("i2s_para","i2s_slave", &i2s_slave, sizeof(int)); + if (ret == 0 && i2s_slave) { + sunxi_iis.slave = 1; + printk("[I2S-0] sunxi_i2s_init I2S used in slave mode\n"); + } else { + sunxi_iis.slave = 0; + printk("[I2S-0] sunxi_i2s_init I2S used in master mode\n"); + } + i2s_handle = gpio_request_ex("i2s_para", NULL); if((err = platform_device_register(&sunxi_i2s_device)) < 0) @@ -691,7 +886,7 @@ static int __init sunxi_i2s_init(void) if ((err = platform_driver_register(&sunxi_i2s_driver)) < 0) return err; } else { - printk("[I2S]sunxi-i2s cannot find any using configuration for controllers, return directly!\n"); + printk("[I2S-0] sunxi-i2s cannot find any using configuration for controllers, return directly!\n"); return 0; } return 0; @@ -700,6 +895,7 @@ module_init(sunxi_i2s_init); static void __exit sunxi_i2s_exit(void) { + printk("[I2S-0] Entered %s\n", __func__); platform_driver_unregister(&sunxi_i2s_driver); } module_exit(sunxi_i2s_exit); diff --git a/sound/soc/sunxi/i2s/sunxi-i2s.h b/sound/soc/sunxi/i2s/sunxi-i2s.h index f12d6d5..ba6ac32 100644 --- a/sound/soc/sunxi/i2s/sunxi-i2s.h +++ b/sound/soc/sunxi/i2s/sunxi-i2s.h @@ -20,71 +20,71 @@ /* REGISTER definition */ /* IIS REGISTER */ -#define SUNXI_IISBASE (0x01C22400) +#define SUNXI_IISBASE (0x01C22400) -#define SUNXI_IISCTL (0x00) +#define SUNXI_IISCTL (0x00) #define SUNXI_IISCTL_SDO3EN (1<<11) #define SUNXI_IISCTL_SDO2EN (1<<10) #define SUNXI_IISCTL_SDO1EN (1<<9) #define SUNXI_IISCTL_SDO0EN (1<<8) - #define SUNXI_IISCTL_ASS (1<<6) + #define SUNXI_IISCTL_ASS (1<<6) #define SUNXI_IISCTL_MS (1<<5) - #define SUNXI_IISCTL_PCM (1<<4) - #define SUNXI_IISCTL_LOOP (1<<3) - #define SUNXI_IISCTL_TXEN (1<<2) - #define SUNXI_IISCTL_RXEN (1<<1) - #define SUNXI_IISCTL_GEN (1<<0) - -#define SUNXI_IISFAT0 (0x04) - #define SUNXI_IISFAT0_LRCP (1<<7) - #define SUNXI_IISFAT0_BCP (1<<6) - #define SUNXI_IISFAT0_SR_RVD (3<<4) - #define SUNXI_IISFAT0_SR_16BIT (0<<4) - #define SUNXI_IISFAT0_SR_20BIT (1<<4) - #define SUNXI_IISFAT0_SR_24BIT (2<<4) - #define SUNXI_IISFAT0_WSS_16BCLK (0<<2) - #define SUNXI_IISFAT0_WSS_20BCLK (1<<2) - #define SUNXI_IISFAT0_WSS_24BCLK (2<<2) - #define SUNXI_IISFAT0_WSS_32BCLK (3<<2) - #define SUNXI_IISFAT0_FMT_I2S (0<<0) - #define SUNXI_IISFAT0_FMT_LFT (1<<0) - #define SUNXI_IISFAT0_FMT_RGT (2<<0) - #define SUNXI_IISFAT0_FMT_RVD (3<<0) - -#define SUNXI_IISFAT1 (0x08) - #define SUNXI_IISFAT1_SYNCLEN_16BCLK (0<<12) - #define SUNXI_IISFAT1_SYNCLEN_32BCLK (1<<12) - #define SUNXI_IISFAT1_SYNCLEN_64BCLK (2<<12) - #define SUNXI_IISFAT1_SYNCLEN_128BCLK (3<<12) - #define SUNXI_IISFAT1_SYNCLEN_256BCLK (4<<12) - #define SUNXI_IISFAT1_SYNCOUTEN (1<<11) - #define SUNXI_IISFAT1_OUTMUTE (1<<10) - #define SUNXI_IISFAT1_MLS (1<<9) - #define SUNXI_IISFAT1_SEXT (1<<8) - #define SUNXI_IISFAT1_SI_1ST (0<<6) - #define SUNXI_IISFAT1_SI_2ND (1<<6) - #define SUNXI_IISFAT1_SI_3RD (2<<6) - #define SUNXI_IISFAT1_SI_4TH (3<<6) - #define SUNXI_IISFAT1_SW (1<<5) - #define SUNXI_IISFAT1_SSYNC (1<<4) - #define SUNXI_IISFAT1_RXPDM_16PCM (0<<2) - #define SUNXI_IISFAT1_RXPDM_8PCM (1<<2) - #define SUNXI_IISFAT1_RXPDM_8ULAW (2<<2) - #define SUNXI_IISFAT1_RXPDM_8ALAW (3<<2) - #define SUNXI_IISFAT1_TXPDM_16PCM (0<<0) - #define SUNXI_IISFAT1_TXPDM_8PCM (1<<0) - #define SUNXI_IISFAT1_TXPDM_8ULAW (2<<0) - #define SUNXI_IISFAT1_TXPDM_8ALAW (3<<0) - -#define SUNXI_IISTXFIFO (0x0C) - -#define SUNXI_IISRXFIFO (0x10) - -#define SUNXI_IISFCTL (0x14) - #define SUNXI_IISFCTL_FIFOSRC (1<<31) - #define SUNXI_IISFCTL_FTX (1<<25) - #define SUNXI_IISFCTL_FRX (1<<24) - #define SUNXI_IISFCTL_TXTL(v) ((v)<<12) + #define SUNXI_IISCTL_PCM (1<<4) + #define SUNXI_IISCTL_LOOP (1<<3) + #define SUNXI_IISCTL_TXEN (1<<2) + #define SUNXI_IISCTL_RXEN (1<<1) + #define SUNXI_IISCTL_GEN (1<<0) + +#define SUNXI_IISFAT0 (0x04) + #define SUNXI_IISFAT0_LRCP (1<<7) + #define SUNXI_IISFAT0_BCP (1<<6) + #define SUNXI_IISFAT0_SR_RVD (3<<4) + #define SUNXI_IISFAT0_SR_16BIT (0<<4) + #define SUNXI_IISFAT0_SR_20BIT (1<<4) + #define SUNXI_IISFAT0_SR_24BIT (2<<4) + #define SUNXI_IISFAT0_WSS_16BCLK (0<<2) + #define SUNXI_IISFAT0_WSS_20BCLK (1<<2) + #define SUNXI_IISFAT0_WSS_24BCLK (2<<2) + #define SUNXI_IISFAT0_WSS_32BCLK (3<<2) + #define SUNXI_IISFAT0_FMT_I2S (0<<0) + #define SUNXI_IISFAT0_FMT_LFT (1<<0) + #define SUNXI_IISFAT0_FMT_RGT (2<<0) + #define SUNXI_IISFAT0_FMT_RVD (3<<0) + +#define SUNXI_IISFAT1 (0x08) + #define SUNXI_IISFAT1_SYNCLEN_16BCLK (0<<12) + #define SUNXI_IISFAT1_SYNCLEN_32BCLK (1<<12) + #define SUNXI_IISFAT1_SYNCLEN_64BCLK (2<<12) + #define SUNXI_IISFAT1_SYNCLEN_128BCLK (3<<12) + #define SUNXI_IISFAT1_SYNCLEN_256BCLK (4<<12) + #define SUNXI_IISFAT1_SYNCOUTEN (1<<11) + #define SUNXI_IISFAT1_OUTMUTE (1<<10) + #define SUNXI_IISFAT1_MLS (1<<9) + #define SUNXI_IISFAT1_SEXT (1<<8) + #define SUNXI_IISFAT1_SI_1ST (0<<6) + #define SUNXI_IISFAT1_SI_2ND (1<<6) + #define SUNXI_IISFAT1_SI_3RD (2<<6) + #define SUNXI_IISFAT1_SI_4TH (3<<6) + #define SUNXI_IISFAT1_SW (1<<5) + #define SUNXI_IISFAT1_SSYNC (1<<4) + #define SUNXI_IISFAT1_RXPDM_16PCM (0<<2) + #define SUNXI_IISFAT1_RXPDM_8PCM (1<<2) + #define SUNXI_IISFAT1_RXPDM_8ULAW (2<<2) + #define SUNXI_IISFAT1_RXPDM_8ALAW (3<<2) + #define SUNXI_IISFAT1_TXPDM_16PCM (0<<0) + #define SUNXI_IISFAT1_TXPDM_8PCM (1<<0) + #define SUNXI_IISFAT1_TXPDM_8ULAW (2<<0) + #define SUNXI_IISFAT1_TXPDM_8ALAW (3<<0) + +#define SUNXI_IISTXFIFO (0x0C) + +#define SUNXI_IISRXFIFO (0x10) + +#define SUNXI_IISFCTL (0x14) + #define SUNXI_IISFCTL_FIFOSRC (1<<31) + #define SUNXI_IISFCTL_FTX (1<<25) + #define SUNXI_IISFCTL_FRX (1<<24) + #define SUNXI_IISFCTL_TXTL(v) ((v)<<12) #define SUNXI_IISFCTL_RXTL(v) ((v)<<4) #define SUNXI_IISFCTL_TXIM_MOD0 (0<<2) #define SUNXI_IISFCTL_TXIM_MOD1 (1<<2) @@ -93,174 +93,101 @@ #define SUNXI_IISFCTL_RXOM_MOD2 (2<<0) #define SUNXI_IISFCTL_RXOM_MOD3 (3<<0) -#define SUNXI_IISFSTA (0x18) - #define SUNXI_IISFSTA_TXE (1<<28) +#define SUNXI_IISFSTA (0x18) + #define SUNXI_IISFSTA_TXE (1<<28) #define SUNXI_IISFSTA_TXECNT(v) ((v)<<16) - #define SUNXI_IISFSTA_RXA (1<<8) + #define SUNXI_IISFSTA_RXA (1<<8) #define SUNXI_IISFSTA_RXACNT(v) ((v)<<0) -#define SUNXI_IISINT (0x1C) - #define SUNXI_IISINT_TXDRQEN (1<<7) - #define SUNXI_IISINT_TXUIEN (1<<6) - #define SUNXI_IISINT_TXOIEN (1<<5) - #define SUNXI_IISINT_TXEIEN (1<<4) - #define SUNXI_IISINT_RXDRQEN (1<<2) - #define SUNXI_IISINT_RXOIEN (1<<1) - #define SUNXI_IISINT_RXAIEN (1<<0) - -#define SUNXI_IISISTA (0x20) - #define SUNXI_IISISTA_TXUISTA (1<<6) - #define SUNXI_IISISTA_TXOISTA (1<<5) - #define SUNXI_IISISTA_TXEISTA (1<<4) - #define SUNXI_IISISTA_RXOISTA (1<<1) - #define SUNXI_IISISTA_RXAISTA (1<<0) - -#define SUNXI_IISCLKD (0x24) - #define SUNXI_IISCLKD_MCLKOEN (1<<7) +#define SUNXI_IISINT (0x1C) + #define SUNXI_IISINT_TXDRQEN (1<<7) + #define SUNXI_IISINT_TXUIEN (1<<6) + #define SUNXI_IISINT_TXOIEN (1<<5) + #define SUNXI_IISINT_TXEIEN (1<<4) + #define SUNXI_IISINT_RXDRQEN (1<<3) + #define SUNXI_IISINT_RXUIEN (1<<2) + #define SUNXI_IISINT_RXOIEN (1<<1) + #define SUNXI_IISINT_RXAIEN (1<<0) + +#define SUNXI_IISISTA (0x20) + #define SUNXI_IISISTA_TXUISTA (1<<6) + #define SUNXI_IISISTA_TXOISTA (1<<5) + #define SUNXI_IISISTA_TXEISTA (1<<4) + #define SUNXI_IISISTA_RXUISTA (1<<2) + #define SUNXI_IISISTA_RXOISTA (1<<1) + #define SUNXI_IISISTA_RXAISTA (1<<0) + +#define SUNXI_IISCLKD (0x24) + #define SUNXI_IISCLKD_MCLKOEN (1<<7) #define SUNXI_IISCLKD_BCLKDIV_2 (0<<4) #define SUNXI_IISCLKD_BCLKDIV_4 (1<<4) #define SUNXI_IISCLKD_BCLKDIV_6 (2<<4) #define SUNXI_IISCLKD_BCLKDIV_8 (3<<4) - #define SUNXI_IISCLKD_BCLKDIV_12 (4<<4) - #define SUNXI_IISCLKD_BCLKDIV_16 (5<<4) - #define SUNXI_IISCLKD_BCLKDIV_32 (6<<4) - #define SUNXI_IISCLKD_BCLKDIV_64 (7<<4) + #define SUNXI_IISCLKD_BCLKDIV_12 (4<<4) + #define SUNXI_IISCLKD_BCLKDIV_16 (5<<4) + #define SUNXI_IISCLKD_BCLKDIV_32 (6<<4) + #define SUNXI_IISCLKD_BCLKDIV_64 (7<<4) #define SUNXI_IISCLKD_MCLKDIV_1 (0<<0) #define SUNXI_IISCLKD_MCLKDIV_2 (1<<0) #define SUNXI_IISCLKD_MCLKDIV_4 (2<<0) #define SUNXI_IISCLKD_MCLKDIV_6 (3<<0) #define SUNXI_IISCLKD_MCLKDIV_8 (4<<0) - #define SUNXI_IISCLKD_MCLKDIV_12 (5<<0) - #define SUNXI_IISCLKD_MCLKDIV_16 (6<<0) - #define SUNXI_IISCLKD_MCLKDIV_24 (7<<0) - #define SUNXI_IISCLKD_MCLKDIV_32 (8<<0) - #define SUNXI_IISCLKD_MCLKDIV_48 (9<<0) - #define SUNXI_IISCLKD_MCLKDIV_64 (10<<0) - -#define SUNXI_IISTXCNT (0x28) - -#define SUNXI_IISRXCNT (0x2C) - -#define SUNXI_TXCHSEL (0x30) - #define SUNXI_TXCHSEL_CHNUM(v) (((v)-1)<<0) - -#define SUNXI_TXCHMAP (0x34) - #define SUNXI_TXCHMAP_CH7(v) (((v)-1)<<28) - #define SUNXI_TXCHMAP_CH6(v) (((v)-1)<<24) - #define SUNXI_TXCHMAP_CH5(v) (((v)-1)<<20) - #define SUNXI_TXCHMAP_CH4(v) (((v)-1)<<16) - #define SUNXI_TXCHMAP_CH3(v) (((v)-1)<<12) - #define SUNXI_TXCHMAP_CH2(v) (((v)-1)<<8) - #define SUNXI_TXCHMAP_CH1(v) (((v)-1)<<4) - #define SUNXI_TXCHMAP_CH0(v) (((v)-1)<<0) - -#define SUNXI_RXCHSEL (0x38) - #define SUNXI_RXCHSEL_CHNUM(v) (((v)-1)<<0) - -#define SUNXI_RXCHMAP (0x3C) - #define SUNXI_RXCHMAP_CH3(v) (((v)-1)<<12) - #define SUNXI_RXCHMAP_CH2(v) (((v)-1)<<8) - #define SUNXI_RXCHMAP_CH1(v) (((v)-1)<<4) - #define SUNXI_RXCHMAP_CH0(v) (((v)-1)<<0) - - -/* DMA REGISTER */ -#define SUNXI_DMABASE (0x01C02000) - -#define SUNXI_DMAIRQEN (0x0) - #define SUNXI_DMAIRQEN_NDMA_FULLEN(v) (1<<((v)*2+1)) - #define SUNXI_DMAIRQEN_NDMA_HALFEN(v) (1<<((v)*2)) - -#define SUNXI_DMAIRQPENDING (0x4) - #define SUNXI_DMAIRQPENGDING_NDMA_FULLPEND(v) (1<<((v)*2+1)) - #define SUNXI_DMAIRQPENGDING_NDMA_HALFPEND(v) (1<<((v)*2)) - -#define SUNXI_NDMACFG(v) ((v)*0x20+0x100) - #define SUNXI_NDMACFG_DMALOAD (1<<31) - #define SUNXI_NDMACFG_BUSY (1<<30) - #define SUNXI_NDMACFG_CONTINUOUS (1<<29) - #define SUNXI_NDMACFG_WAIT(v) (((v)-1)<<26) //wait clock = 2^n example: 8 clocks = 2^3 - #define SUNXI_NDMACFG_DSTDATAWIDTH_8BIT (0<<24) - #define SUNXI_NDMACFG_DSTDATAWIDTH_16BIT (1<<24) - #define SUNXI_NDMACFG_DSTDATAWIDTH_32BIT (2<<24) - #define SUNXI_NDMACFG_DSTDATAWIDTH_RVD (3<<24) - #define SUNXI_NDMACFG_DSTBURST4 (1<<23) - #define SUNXI_NDMACFG_DSTADDRTYPE_INC (0<<21) - #define SUNXI_NDMACFG_DSTADDRTYPE_CON (1<<21) - #define SUNXI_NDMACFG_DSTTYPE_IRTX (0x0<<16) - #define SUNXI_NDMACFG_DSTTYPE_SPDIFTX (0x1<<16) - #define SUNXI_NDMACFG_DSTTYPE_IISTX (0x2<<16) - #define SUNXI_NDMACFG_DSTTYPE_AC97TX (0x3<<16) - #define SUNXI_NDMACFG_DSTTYPE_SPI0TX (0x4<<16) - #define SUNXI_NDMACFG_DSTTYPE_SPI1TX (0x5<<16) - #define SUNXI_NDMACFG_DSTTYPE_SPI2TX (0x6<<16) - #define SUNXI_NDMACFG_DSTTYPE_UART0TX (0x8<<16) - #define SUNXI_NDMACFG_DSTTYPE_UART1TX (0x9<<16) - #define SUNXI_NDMACFG_DSTTYPE_UART2TX (0xA<<16) - #define SUNXI_NDMACFG_DSTTYPE_UART3TX (0xB<<16) - #define SUNXI_NDMACFG_DSTTYPE_AUDIODA (0xC<<16) - #define SUNXI_NDMACFG_DSTTYPE_NFC (0xF<<16) - #define SUNXI_NDMACFG_DSTTYPE_SRAM (0x10<<16) - #define SUNXI_NDMACFG_DSTTYPE_DRAM (0x11<<16) - #define SUNXI_NDMACFG_DSTTYPE_UART4TX (0x12<<16) - #define SUNXI_NDMACFG_DSTTYPE_UART5TX (0x13<<16) - #define SUNXI_NDMACFG_DSTTYPE_UART6TX (0x14<<16) - #define SUNXI_NDMACFG_DSTTYPE_UART7TX (0x15<<16) - #define SUNXI_NDMACFG_SRCDATAWIDTH_8BIT (0<<8) - #define SUNXI_NDMACFG_SRCDATAWIDTH_16BIT (1<<8) - #define SUNXI_NDMACFG_SRCDATAWIDTH_32BIT (2<<8) - #define SUNXI_NDMACFG_SRCDATAWIDTH_RVD (3<<8) - #define SUNXI_NDMACFG_SRCBURST4 (1<<7) - #define SUNXI_NDMACFG_SRCADDRTYPE_INC (0<<5) - #define SUNXI_NDMACFG_SRCADDRTYPE_CON (1<<5) - #define SUNXI_NDMACFG_SRCTYPE_IRRX (0x0<<0) - #define SUNXI_NDMACFG_SRCTYPE_SPDIFRX (0x1<<0) - #define SUNXI_NDMACFG_SRCTYPE_IISRX (0x2<<0) - #define SUNXI_NDMACFG_SRCTYPE_AC97RX (0x3<<0) - #define SUNXI_NDMACFG_SRCTYPE_SPI0RX (0x4<<0) - #define SUNXI_NDMACFG_SRCTYPE_SPI1RX (0x5<<0) - #define SUNXI_NDMACFG_SRCTYPE_SPI2RX (0x6<<0) - #define SUNXI_NDMACFG_SRCTYPE_UART0RX (0x8<<0) - #define SUNXI_NDMACFG_SRCTYPE_UART1RX (0x9<<0) - #define SUNXI_NDMACFG_SRCTYPE_UART2RX (0xA<<0) - #define SUNXI_NDMACFG_SRCTYPE_UART3RX (0xB<<0) - #define SUNXI_NDMACFG_SRCTYPE_AUDIOAD (0xC<<0) - #define SUNXI_NDMACFG_SRCTYPE_TPAD (0xD<<0) - #define SUNXI_NDMACFG_SRCTYPE_NFC (0xF<<0) - #define SUNXI_NDMACFG_SRCTYPE_SRAM (0x10<<0) - #define SUNXI_NDMACFG_SRCTYPE_DRAM (0x11<<0) - #define SUNXI_NDMACFG_SRCTYPE_UART4RX (0x12<<0) - #define SUNXI_NDMACFG_SRCTYPE_UART5RX (0x13<<0) - #define SUNXI_NDMACFG_SRCTYPE_UART6RX (0x14<<0) - #define SUNXI_NDMACFG_SRCTYPE_UART7RX (0x15<<0) - -#define SUNXI_NDMASRCADDR(v) ((v)*0x20 + 0x100 + 4) - -#define SUNXI_NDMADSTADDR(v) ((v)*0x20 + 0x100 + 8) - -#define SUNXI_NDMACNT(v) ((v)*0x20 + 0x100 + 0xC) + #define SUNXI_IISCLKD_MCLKDIV_12 (5<<0) + #define SUNXI_IISCLKD_MCLKDIV_16 (6<<0) + #define SUNXI_IISCLKD_MCLKDIV_24 (7<<0) + #define SUNXI_IISCLKD_MCLKDIV_32 (8<<0) + #define SUNXI_IISCLKD_MCLKDIV_48 (9<<0) + #define SUNXI_IISCLKD_MCLKDIV_64 (10<<0) + +#define SUNXI_IISTXCNT (0x28) + +#define SUNXI_IISRXCNT (0x2C) + +#define SUNXI_TXCHSEL (0x30) + #define SUNXI_TXCHSEL_CHNUM(v) (((v)-1)<<0) + +#define SUNXI_TXCHMAP (0x34) + #define SUNXI_TXCHMAP_CH7(v) (((v)-1)<<28) + #define SUNXI_TXCHMAP_CH6(v) (((v)-1)<<24) + #define SUNXI_TXCHMAP_CH5(v) (((v)-1)<<20) + #define SUNXI_TXCHMAP_CH4(v) (((v)-1)<<16) + #define SUNXI_TXCHMAP_CH3(v) (((v)-1)<<12) + #define SUNXI_TXCHMAP_CH2(v) (((v)-1)<<8) + #define SUNXI_TXCHMAP_CH1(v) (((v)-1)<<4) + #define SUNXI_TXCHMAP_CH0(v) (((v)-1)<<0) + +#define SUNXI_RXCHSEL (0x38) + #define SUNXI_RXCHSEL_CHNUM(v) (((v)-1)<<0) + +#define SUNXI_RXCHMAP (0x3C) + #define SUNXI_RXCHMAP_CH3(v) (((v)-1)<<12) + #define SUNXI_RXCHMAP_CH2(v) (((v)-1)<<8) + #define SUNXI_RXCHMAP_CH1(v) (((v)-1)<<4) + #define SUNXI_RXCHMAP_CH0(v) (((v)-1)<<0) + /* CCM REGISTER */ -#define SUNXI_CCMBASE (0x01C20000) +#define SUNXI_CCMBASE (0x01C20000) -#define SUNXI_CCM_AUDIO_HOSC_PLL_REG (0x08) +#define SUNXI_CCM_AUDIO_HOSC_PLL_REG (0x08) #define SUNXI_CCM_AUDIO_HOSC_PLL_REG_AUDIOEN (1<<31) #define SUNXI_CCM_AUDIO_HOSC_PLL_REG_FRE225792MHZ (0<<27) #define SUNXI_CCM_AUDIO_HOSC_PLL_REG_FRE24576MHZ (1<<27) -#define SUNXI_CCM_APB_GATE_REG (0x68) - #define SUNXI_CCM_APB_GATE_REG_IISGATE (1<<3) +#define SUNXI_CCM_APB_GATE_REG (0x68) + #define SUNXI_CCM_APB_GATE_REG_IISGATE (1<<3) -#define SUNXI_CCM_AUDIO_CLK_REG (0xb8) +#define SUNXI_CCM_AUDIO_CLK_REG (0xb8) #define SUNXI_CCM_AUDIO_CLK_REG_IISSPECIALGATE (1<<31) - #define SUNXI_CCM_AUDIO_CLK_REG_DIV(v) ((v)<<16) + #define SUNXI_CCM_AUDIO_CLK_REG_DIV(v) ((v)<<16) /*------------------------------------------------------------*/ /*------------------------------------------------------------*/ /* Clock dividers */ -#define SUNXI_DIV_MCLK 0 -#define SUNXI_DIV_BCLK 1 +#define SUNXI_DIV_MCLK 0 +#define SUNXI_DIV_BCLK 1 +#define SUNXI_DIV_EXTCLK 2 #define SUNXI_IISCLKD_MCLK_MASK 0x0f #define SUNXI_IISCLKD_MCLK_OFFS 0 @@ -269,36 +196,34 @@ #define SUNXI_IISCLKD_MCLKEN_OFFS 7 unsigned int sunxi_i2s_get_clockrate(void); -extern struct sunxi_i2s_info sunxi_i2s; -//extern struct snd_soc_dai sunxi_iis_dai; extern void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on); -extern void sunxi_snd_rxctrl_i2s(int on); +extern void sunxi_snd_rxctrl_i2s(struct snd_pcm_substream *substream, int on); struct sunxi_i2s_info { - void __iomem *regs; /* IIS BASE */ - void __iomem *ccmregs; //CCM BASE - void __iomem *ioregs; //IO BASE + void __iomem *regs; //IIS BASE + void __iomem *ccmregs; //CCM BASE + void __iomem *ioregs; //IO BASE - u32 slave; //0: master, 1: slave - u32 mono; //0: stereo, 1: mono - u32 samp_fs; //audio sample rate (unit in kHz) + u32 slave; //0: master, 1: slave + u32 channel_num; // + u32 samp_fs; //audio sample rate (unit in kHz) u32 samp_res; //16 bits, 20 bits , 24 bits, 32 bits) u32 samp_format; //audio sample format (0: standard I2S, 1: left-justified, 2: right-justified, 3: pcm) - u32 ws_size; //16 BCLK, 20 BCLK, 24 BCLK, 32 BCLK) + u32 ws_size; //16 BCLK, 20 BCLK, 24 BCLK, 32 BCLK) u32 mclk_rate; //mclk frequency divide by fs (128fs, 192fs, 256fs, 384fs, 512fs, 768fs) - u32 lrc_pol; //LRC clock polarity (0: normal ,1: inverted) + u32 lrc_pol; //LRC clock polarity (0: normal ,1: inverted) u32 bclk_pol; //BCLK polarity (0: normal, 1: inverted) - u32 pcm_txtype; //PCM transmitter type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law) - u32 pcm_rxtype; //PCM receiver type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law) - u32 pcm_sw; //PCM slot width (8: 8 bits, 16: 16 bits) - u32 pcm_sync_period;//PCM sync period (16/32/64/128/256) - u32 pcm_sync_type; //PCM sync symbol size (0: short sync, 1: long sync) - u32 pcm_start_slot;//PCM start slot index (1--4) - u32 pcm_lsb_first; //0: MSB first, 1: LSB first - u32 pcm_ch_num; //PCM channel number (1: one channel, 2: two channel) + u32 pcm_txtype; //PCM transmitter type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law) + u32 pcm_rxtype; //PCM receiver type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law) + u32 pcm_sw; //PCM slot width (8: 8 bits, 16: 16 bits) + u32 pcm_sync_period; //PCM sync period (16/32/64/128/256) + u32 pcm_sync_type; //PCM sync symbol size (0: short sync, 1: long sync) + u32 pcm_start_slot; //PCM start slot index (1--4) + u32 pcm_lsb_first; //0: MSB first, 1: LSB first + u32 pcm_ch_num; //PCM channel number (1: one channel, 2: two channel) }; -extern struct sunxi_i2s_info sunxi_i2s; +//extern struct sunxi_i2s_info sunxi_iis; #endif diff --git a/sound/soc/sunxi/i2s/sunxi-i2sdma.c b/sound/soc/sunxi/i2s/sunxi-i2sdma.c index 488ef4d..57f82c1 100644 --- a/sound/soc/sunxi/i2s/sunxi-i2sdma.c +++ b/sound/soc/sunxi/i2s/sunxi-i2sdma.c @@ -32,17 +32,32 @@ #include "sunxi-i2s.h" #include "sunxi-i2sdma.h" -static volatile unsigned int dmasrc = 0; -static volatile unsigned int dmadst = 0; +static const struct snd_pcm_hardware sunxi_pcm_out_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .rates = SNDRV_PCM_RATE_8000_384000 | SNDRV_PCM_RATE_KNOT, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 128*1024, /* value must be (2^n)Kbyte size */ + .period_bytes_min = 1024*4,//1024*4, + .period_bytes_max = 1024*32,//1024*32, + .periods_min = 4,//4, + .periods_max = 8,//8, + .fifo_size = 128, +}; -static const struct snd_pcm_hardware sunxi_pcm_hardware = { +static const struct snd_pcm_hardware sunxi_pcm_in_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .rates = SNDRV_PCM_RATE_8000_384000 | SNDRV_PCM_RATE_KNOT, .rate_min = 8000, - .rate_max = 192000, + .rate_max = 384000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 128*1024, /* value must be (2^n)Kbyte size */ @@ -50,7 +65,7 @@ static const struct snd_pcm_hardware sunxi_pcm_hardware = { .period_bytes_max = 1024*32,//1024*32, .periods_min = 4,//4, .periods_max = 8,//8, - .fifo_size = 128,//32, + .fifo_size = 64, }; struct sunxi_runtime_data { @@ -63,32 +78,32 @@ struct sunxi_runtime_data { dma_addr_t dma_pos; dma_addr_t dma_end; struct sunxi_dma_params *params; + /*DMA data width*/ + unsigned int dma_width; }; static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream) { + int ret = 0; struct sunxi_runtime_data *prtd = substream->runtime->private_data; dma_addr_t pos = prtd->dma_pos; - unsigned int limit; - int ret; - unsigned long len = prtd->dma_period; - limit = prtd->dma_limit; - while(prtd->dma_loaded < limit) { - if((pos + len) > prtd->dma_end) { + unsigned int limit = prtd->dma_limit; + int read = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + + while(prtd->dma_loaded < limit){ + if((pos + len) > prtd->dma_end){ len = prtd->dma_end - pos; } - - ret = sunxi_dma_enqueue(prtd->params, pos, len, 0); - if(ret == 0) { + ret = sunxi_dma_enqueue(prtd->params, pos, len, read); + if(ret == 0){ prtd->dma_loaded++; pos += prtd->dma_period; if(pos >= prtd->dma_end) pos = prtd->dma_start; - }else { + }else{ break; } - } prtd->dma_pos = pos; } @@ -125,17 +140,32 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, if (!dma) return 0; + prtd->dma_width = 16; + /* set DMA width for using in sunxi_pcm_prepare*/ + switch(params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + prtd->dma_width = 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + prtd->dma_width = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + prtd->dma_width = 32; + break; + } if (prtd->params == NULL) { prtd->params = dma; ret = sunxi_dma_request(prtd->params, 0); if (ret < 0) { - return ret; + printk("[IIS-0] sunxi_dma_request failed! ret == %d\n", ret); + return ret; } } if (sunxi_dma_set_callback(prtd->params, sunxi_audio_buffdone, substream) != 0) { sunxi_dma_release(prtd->params); + printk("[IIS-0] sunxi_dma_set_callback failed! ret == %d\n", ret); prtd->params = NULL; return -EINVAL; } @@ -151,6 +181,7 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, prtd->dma_start = runtime->dma_addr; prtd->dma_pos = prtd->dma_start; prtd->dma_end = prtd->dma_start + totbytes; + spin_unlock_irq(&prtd->lock); return 0; } @@ -196,24 +227,62 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream) #else dma_config_t codec_dma_conf; memset(&codec_dma_conf, 0, sizeof(codec_dma_conf)); - codec_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; - codec_dma_conf.xfer_type.src_bst_len = DATA_BRST_1; - codec_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; - codec_dma_conf.xfer_type.dst_bst_len = DATA_BRST_1; + + printk("[IIS-0] sunxi_pcm_prepare: playback DMA data width=(%d)\n", prtd->dma_width); + if(prtd->dma_width > 16) + { + codec_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT; + codec_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT; + } + else + { + codec_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; + codec_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; + } + codec_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; + codec_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4; codec_dma_conf.address_type.src_addr_mode = NDMA_ADDR_INCREMENT; codec_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_NOCHANGE; codec_dma_conf.src_drq_type = N_SRC_SDRAM; codec_dma_conf.dst_drq_type = N_DST_IIS0_TX; codec_dma_conf.bconti_mode = false; - codec_dma_conf.irq_spt = CHAN_IRQ_FD; + codec_dma_conf.irq_spt = CHAN_IRQ_FD; //buf full done irq +#endif + ret = sunxi_dma_config(prtd->params, &codec_dma_conf, 0); + } + else { +#if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I +#else + dma_config_t codec_dma_conf; + memset(&codec_dma_conf, 0, sizeof(codec_dma_conf)); + + printk("[IIS-0] sunxi_pcm_prepare: capture DMA data width=(%d)\n", prtd->dma_width); + if(prtd->dma_width > 16) + { + codec_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT; + codec_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT; + } + else + { + codec_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; + codec_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; + } + codec_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; + codec_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4; + codec_dma_conf.address_type.src_addr_mode = NDMA_ADDR_NOCHANGE; + codec_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_INCREMENT; + codec_dma_conf.src_drq_type = N_SRC_IIS0_RX; + codec_dma_conf.dst_drq_type = N_DST_SDRAM; + codec_dma_conf.bconti_mode = false; + codec_dma_conf.irq_spt = CHAN_IRQ_FD; //buf full done irq #endif ret = sunxi_dma_config(prtd->params, &codec_dma_conf, 0); } /* flush the DMA channel */ prtd->dma_loaded = 0; - if (sunxi_dma_flush(prtd->params) == 0) - prtd->dma_pos = prtd->dma_start; + sunxi_dma_flush(prtd->params); + prtd->dma_pos = prtd->dma_start; /* enqueue dma buffers */ sunxi_pcm_enqueue(substream); @@ -231,15 +300,16 @@ static int sunxi_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - printk("[IIS] dma trigger start\n"); - printk("[IIS] 0x01c22400+0x24 = %#x, line= %d\n", readl(0xf1c22400+0x24), __LINE__); + printk("[IIS-0] dma trigger start\n"); + prtd->state |= ST_RUNNING; sunxi_dma_start(prtd->params); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - printk("[IIS] dma trigger stop\n"); + printk("[IIS-0] dma trigger stop\n"); + prtd->state &= ~ST_RUNNING; sunxi_dma_stop(prtd->params); break; @@ -258,31 +328,42 @@ static snd_pcm_uframes_t sunxi_pcm_pointer(struct snd_pcm_substream *substream) struct sunxi_runtime_data *prtd = runtime->private_data; unsigned long res = 0; snd_pcm_uframes_t offset = 0; + unsigned int dmasrc = 0; + unsigned int dmadst = 0; spin_lock(&prtd->lock); sunxi_dma_getcurposition(prtd->params, - (dma_addr_t*)&dmasrc, (dma_addr_t*)&dmadst); + (dma_addr_t*)&dmasrc, (dma_addr_t*)&dmadst); - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - res = dmadst - prtd->dma_start; - else - { - offset = bytes_to_frames(runtime, dmasrc + prtd->dma_period - runtime->dma_addr); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + res = dmasrc + prtd->dma_period - prtd->dma_start; + } + else { + res = dmadst + prtd->dma_period - prtd->dma_start; + //res = dmadst - prtd->dma_start; } + offset = bytes_to_frames(runtime, res); spin_unlock(&prtd->lock); if(offset >= runtime->buffer_size) offset = 0; - return offset; + + return offset; } + static int sunxi_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct sunxi_runtime_data *prtd; snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_hardware); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_out_hardware); + } + else { + snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_in_hardware); + } prtd = kzalloc(sizeof(struct sunxi_runtime_data), GFP_KERNEL); if (prtd == NULL) @@ -331,13 +412,19 @@ static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = sunxi_pcm_hardware.buffer_bytes_max; - + size_t size = 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + size = sunxi_pcm_out_hardware.buffer_bytes_max; + } + else { + size = sunxi_pcm_in_hardware.buffer_bytes_max; + } buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; buf->private_data = NULL; buf->area = dma_alloc_writecombine(pcm->card->dev, size, &buf->addr, GFP_KERNEL); + if (!buf->area) return -ENOMEM; buf->bytes = size; diff --git a/sound/soc/sunxi/i2s/sunxi-i2sdma.h b/sound/soc/sunxi/i2s/sunxi-i2sdma.h index 50418a5..8f0b029 100644 --- a/sound/soc/sunxi/i2s/sunxi-i2sdma.h +++ b/sound/soc/sunxi/i2s/sunxi-i2sdma.h @@ -29,7 +29,7 @@ enum sunxi_dma_buffresult { }; /* platform data */ -extern struct snd_soc_platform sunxi_soc_platform_i2s; -extern struct sunxi_i2s_info sunxi_iis; +//extern struct snd_soc_platform sunxi_soc_platform_i2s; +//extern struct sunxi_i2s_info sunxi_iis; #endif //SUNXI_PCM_H_ diff --git a/sound/soc/sunxi/i2s/sunxi-sndi2s.c b/sound/soc/sunxi/i2s/sunxi-sndi2s.c index 7c1a3d4..ab10c86 100644 --- a/sound/soc/sunxi/i2s/sunxi-sndi2s.c +++ b/sound/soc/sunxi/i2s/sunxi-sndi2s.c @@ -29,9 +29,8 @@ #include "sndi2s.h" -static struct clk *xtal; -static int clk_users; -static DEFINE_MUTEX(clk_lock); +/* slave mode flag*/ +static int sunxi_i2s_slave = 0; #ifdef ENFORCE_RATES static struct snd_pcm_hw_constraint_list hw_constraints_rates = { @@ -45,7 +44,7 @@ static int sunxi_sndi2s_startup(struct snd_pcm_substream *substream) { int ret = 0; #ifdef ENFORCE_RATES - struct snd_pcm_runtime *runtime = substream->runtime;; + struct snd_pcm_runtime *runtime = substream->runtime; #endif if (!ret) { @@ -62,14 +61,6 @@ static int sunxi_sndi2s_startup(struct snd_pcm_substream *substream) static void sunxi_sndi2s_shutdown(struct snd_pcm_substream *substream) { - mutex_lock(&clk_lock); - clk_users -= 1; - if (clk_users == 0) { - clk_put(xtal); - xtal = NULL; - - } - mutex_unlock(&clk_lock); } typedef struct __MCLK_SET_INF @@ -91,6 +82,14 @@ typedef struct __BCLK_SET_INF } __bclk_set_inf; +typedef struct __EXTCLK_SET_INF +{ + __u32 samp_rate; // sample rate + __u16 clk_div; // masterclock division + __u16 mpll; // select mpll, 0 - 24.576 Mhz, 1 - 22.5792 Mhz + +} __extclk_set_inf; + static __bclk_set_inf BCLK_INF[] = { @@ -164,7 +163,50 @@ static __mclk_set_inf MCLK_INF[] = {0xffffffff, 0, 0, 0}, }; -static s32 get_clock_divder(u32 sample_rate, u32 sample_width, u32 * mclk_div, u32* mpll, u32* bclk_div, u32* mult_fs) +static __extclk_set_inf EXTCLK_INF[] = +{ + //44.1k bitrate + { 44100, 512, 1}, + //48k bitrate + { 48000, 512, 0}, + //88.2k bitrate + { 88200, 256, 1}, + //96k bitrate + { 96000, 256, 0}, + //176.4k bitrate + { 176400, 128, 1}, + //192k bitrate + { 192000, 128, 0}, + + //352.8k bitrate + { 352800, 64, 1}, + //384 bitrate + { 384000, 64, 0}, + + //end flag 0xffffffff + {0xffffffff, 0, 0} +}; + + +static s32 get_clock_divder_slave(u32 sample_rate, u32 sample_width, u32* bclk_div, u32* mpll, u32* mult_fs) +{ + u32 i, ret = -EINVAL; + + for(i=0; i< 100; i++) { + if(EXTCLK_INF[i].samp_rate == sample_rate) { + //set mpll and bclk division + *mpll = EXTCLK_INF[i].mpll; + *bclk_div = EXTCLK_INF[i].clk_div; + ret = 0; + break; + } + else if(EXTCLK_INF[i].samp_rate == 0xffffffff) + break; + } + return ret; +} + +static s32 get_clock_divder_master(u32 sample_rate, u32 sample_width, u32 * mclk_div, u32* mpll, u32* bclk_div, u32* mult_fs) { u32 i, j, ret = -EINVAL; @@ -201,34 +243,56 @@ static int sunxi_sndi2s_hw_params(struct snd_pcm_substream *substream, unsigned long rate = params_rate(params); u32 mclk_div=0, mpll=0, bclk_div=0, mult_fs=0; - get_clock_divder(rate, 32, &mclk_div, &mpll, &bclk_div, &mult_fs); + if(!sunxi_i2s_slave) { + get_clock_divder_master(rate, /*fixed sample width*/32, &mclk_div, &mpll, &bclk_div, &mult_fs); + printk("[IIS-0] get_clock_divder_master: rate=(%lu), mclk_div=(%d), mpll=(%d), bclk_div=(%d), mult_fs=(%d)\n", + rate, mclk_div, mpll, bclk_div, mult_fs); + } else { + get_clock_divder_slave(rate, /*fixed sample width*/32, &bclk_div, &mpll, &mult_fs); + printk("[IIS-0] get_clock_divder_slave: rate=(%lu), bclk_div=(%d), mpll=(%d), mult_fs=(%d)\n", + rate, bclk_div, mpll, mult_fs); + } + //call sunxi_iis_set_fmt ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + SND_SOC_DAIFMT_NB_NF/* | SND_SOC_DAIFMT_CBM_CFM*/); if (ret < 0) return ret; + //call sunxi_iis_set_fmt ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + SND_SOC_DAIFMT_NB_NF/* | SND_SOC_DAIFMT_CBM_CFM*/); if (ret < 0) return ret; + //call sunxi_iis_set_sysclk ret = snd_soc_dai_set_sysclk(cpu_dai, 0 , mpll, 0); if (ret < 0) return ret; + //call sndi2s_set_dai_sysclk ret = snd_soc_dai_set_sysclk(codec_dai, 0 , mpll, 0); if (ret < 0) return ret; - ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_MCLK, mclk_div); - if (ret < 0) - return ret; + if(!sunxi_i2s_slave) { + //call sunxi_iis_set_clkdiv + ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_MCLK, mclk_div); + if (ret < 0) + return ret; - ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_BCLK, bclk_div); - if (ret < 0) - return ret; + //call sunxi_iis_set_clkdiv + ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_BCLK, bclk_div); + if (ret < 0) + return ret; + } else { + //call sunxi_iis_set_clkdiv + ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_EXTCLK, bclk_div); + if (ret < 0) + return ret; + } + //call sndi2s_set_dai_clkdiv ret = snd_soc_dai_set_clkdiv(codec_dai, 0, mult_fs); if (ret < 0) return ret; @@ -243,7 +307,7 @@ static struct snd_soc_ops sunxi_sndi2s_ops = { }; static struct snd_soc_dai_link sunxi_sndi2s_dai_link = { - .name = "I2S", + .name = "I2S", .stream_name = "SUNXI-I2S", .cpu_dai_name = "sunxi-i2s.0", .codec_dai_name = "sndi2s", @@ -286,12 +350,26 @@ static struct platform_driver sunxi_sndi2s_driver = { static int __init sunxi_sndi2s_init(void) { - int ret, i2s_used = 0; + int ret, i2s_used = 0, i2s_slave = 0; + + printk("[I2S-0] Entered %s\n", __func__); ret = script_parser_fetch("i2s_para", "i2s_used", &i2s_used, 1); if (ret != 0 || !i2s_used) return -ENODEV; + script_parser_fetch("i2s_para","i2s_slave", &i2s_slave, sizeof(int)); + if (i2s_slave) + { + sunxi_i2s_slave = 1; + printk("[I2S-0] sunxi_sndi2s_init I2S used in slave mode\n"); + } + else + { + sunxi_i2s_slave = 0; + printk("[I2S-0] sunxi_sndi2s_init I2S used in master mode\n"); + } + ret = platform_device_register(&sunxi_sndi2s_device); if (ret < 0) return ret; diff --git a/sound/soc/sunxi/i2s/sunxi-sndi2s.h b/sound/soc/sunxi/i2s/sunxi-sndi2s.h index 47f0961..89cc71a 100644 --- a/sound/soc/sunxi/i2s/sunxi-sndi2s.h +++ b/sound/soc/sunxi/i2s/sunxi-sndi2s.h @@ -14,7 +14,7 @@ */ #ifndef SUNXI_SNDI2S_H_ #define SUNXI_SNDI2S_H_ - +/* cleaning code struct sunxi_sndi2s_platform_data { int iis_bclk; int iis_ws; @@ -22,4 +22,5 @@ struct sunxi_sndi2s_platform_data { void (*power)(int); int model; } +*/ #endif diff --git a/sound/soc/sunxi/spdif/sndspdif.c b/sound/soc/sunxi/spdif/sndspdif.c index 73e4a95..667e5bb 100644 --- a/sound/soc/sunxi/spdif/sndspdif.c +++ b/sound/soc/sunxi/spdif/sndspdif.c @@ -26,7 +26,7 @@ #include "sndspdif.h" #define SNDSPDIF_RATES (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT) -#define SNDSPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) +#define SNDSPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) struct sndspdif_priv { int sysclk; @@ -94,9 +94,18 @@ struct snd_soc_dai_driver sndspdif_dai = { .rates = SNDSPDIF_RATES, .formats = SNDSPDIF_FORMATS, }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDSPDIF_RATES, + .formats = SNDSPDIF_FORMATS, + }, /* pcm operations */ .ops = &sndspdif_dai_ops, + //clear start? .symmetric_rates = 1, + //clear end? }; EXPORT_SYMBOL(sndspdif_dai); @@ -106,7 +115,7 @@ static int sndspdif_soc_probe(struct snd_soc_codec *codec) sndspdif = kzalloc(sizeof(struct sndspdif_priv), GFP_KERNEL); if(sndspdif == NULL){ - printk("%s,%d\n",__func__,__LINE__); + pr_err("[SPDIF] try to alloc sndspdif failed %s,%d\n", __func__,__LINE__); return -ENOMEM; } snd_soc_codec_set_drvdata(codec, sndspdif); @@ -161,22 +170,27 @@ static int __init sndspdif_codec_init(void) int ret, spdif_used = 0; ret = script_parser_fetch("spdif_para", "spdif_used", &spdif_used, 1); - if (ret != 0 || !spdif_used) + if (ret != 0 || !spdif_used) { + printk("[SPDIF] [spdif_para] isn't defined or spdif_used=0\n"); return -ENODEV; + } spd_gpio_hdle = gpio_request_ex("spdif_para", "spdif_dout"); if (0 == spd_gpio_hdle) { - pr_err("try to request spdif_para gpio failed\n"); + pr_err("[SPDIF] try to request spdif_para gpio failed %s,%d\n", __func__,__LINE__); return -1; } ret = platform_device_register(&sndspdif_codec_device); - if (ret < 0) + if (ret < 0) { + pr_err("[SPDIF] try to SPDIF platform_device_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); return ret; + } ret = platform_driver_register(&sndspdif_codec_driver); if (ret < 0) { platform_device_unregister(&sndspdif_codec_device); + pr_err("[SPDIF] try to SPDIF platform_driver_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); return ret; } diff --git a/sound/soc/sunxi/spdif/sunxi_sndspdif.c b/sound/soc/sunxi/spdif/sunxi_sndspdif.c index 885e7ae..f088e0f 100644 --- a/sound/soc/sunxi/spdif/sunxi_sndspdif.c +++ b/sound/soc/sunxi/spdif/sunxi_sndspdif.c @@ -45,7 +45,7 @@ static int sunxi_sndspdif_startup(struct snd_pcm_substream *substream) { int ret = 0; #ifdef ENFORCE_RATES - struct snd_pcm_runtime *runtime = substream->runtime;; + struct snd_pcm_runtime *runtime = substream->runtime; #endif if (!ret) { #ifdef ENFORCE_RATES @@ -71,7 +71,7 @@ static void sunxi_sndspdif_shutdown(struct snd_pcm_substream *substream) typedef struct __MCLK_SET_INF { __u32 samp_rate; // sample rate - __u16 mult_fs; // multiply of smaple rate + __u16 mult_fs; // multiply of smaple rate __u8 clk_div; // mpll division __u8 mpll; // select mpll, 0 - 24.576 Mhz, 1 - 22.5792 Mhz @@ -83,7 +83,7 @@ typedef struct __BCLK_SET_INF { __u8 bitpersamp; // bits per sample __u8 clk_div; // clock division - __u16 mult_fs; // multiplay of sample rate + __u16 mult_fs; // multiply of sample rate } __bclk_set_inf; @@ -218,13 +218,13 @@ static struct snd_soc_ops sunxi_sndspdif_ops = { }; static struct snd_soc_dai_link sunxi_sndspdif_dai_link = { - .name = "SPDIF", + .name = "SPDIF", .stream_name = "SUNXI-SPDIF", .cpu_dai_name = "sunxi-spdif.0", .codec_dai_name = "sndspdif", .platform_name = "sunxi-spdif-pcm-audio.0", .codec_name = "sunxi-spdif-codec.0", - .ops = &sunxi_sndspdif_ops, + .ops = &sunxi_sndspdif_ops, }; static struct snd_soc_card snd_soc_sunxi_sndspdif = { @@ -236,6 +236,12 @@ static struct snd_soc_card snd_soc_sunxi_sndspdif = { static int __devinit sunxi_sndspdif_probe(struct platform_device *pdev) { + int ret, spdif_used = 0; + + ret = script_parser_fetch("spdif_para", "spdif_used", &spdif_used, 1); + if (ret != 0 || !spdif_used) + return -ENODEV; + snd_soc_sunxi_sndspdif.dev = &pdev->dev; return snd_soc_register_card(&snd_soc_sunxi_sndspdif); } diff --git a/sound/soc/sunxi/spdif/sunxi_spdif.c b/sound/soc/sunxi/spdif/sunxi_spdif.c index 88389ae..47efb02 100644 --- a/sound/soc/sunxi/spdif/sunxi_spdif.c +++ b/sound/soc/sunxi/spdif/sunxi_spdif.c @@ -37,7 +37,7 @@ #include "sunxi_spdma.h" #include "sunxi_spdif.h" -static int regsave[6]; +static int regsave[9]; static struct sunxi_dma_params sunxi_spdif_stereo_out = { .client.name = "SPDIF out", @@ -48,9 +48,9 @@ static struct sunxi_dma_params sunxi_spdif_stereo_out = { }; static struct sunxi_dma_params sunxi_spdif_stereo_in = { - .client.name = "SPDIF out", + .client.name = "SPDIF in", #if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I - .channel = DMACH_NSPDIF, + .channel = DMACH_NSPDIF, //??? #endif .dma_addr = SUNXI_SPDIFBASE + SUNXI_SPDIF_RXFIFO, }; @@ -63,34 +63,33 @@ void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on) { u32 reg_val; + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); if (substream->runtime->channels == 1) { - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); reg_val |= SUNXI_SPDIF_TXCFG_SINGLEMOD; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); } + else { + reg_val &= ~SUNXI_SPDIF_TXCFG_SINGLEMOD; + } + reg_val |= SUNXI_SPDIF_TXCFG_ASS; //Sending the last audio (may be 0?) + reg_val |= SUNXI_SPDIF_TXCFG_CHSTMODE; //Channel status A&B generated form TX_CHSTA + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - //soft reset SPDIF - writel(0x1, sunxi_spdif.regs + SUNXI_SPDIF_CTL); - - //MCLK OUTPUT enable - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); - reg_val |= SUNXI_SPDIF_CTL_MCLKOUTEN; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); - - //flush TX FIFO + /*flush TX FIFO and set FIFO empty trigger level*/ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); - reg_val |= SUNXI_SPDIF_FCTL_FTX; + reg_val |= SUNXI_SPDIF_FCTL_TXTL(0x10); //TX FIFO empty Trigger Level + reg_val |= SUNXI_SPDIF_FCTL_FTX; //flush TX FIFO writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); - //clear interrupt status + /*clear interrupt status*/ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_ISTA); + reg_val |= SUNXI_SPDIF_ISTA_TXCLR; writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_ISTA); - //clear TX counter + /*clear TX counter*/ writel(0, sunxi_spdif.regs + SUNXI_SPDIF_TXCNT); if (on) { - //SPDIF TX ENBALE + //SPDIF TX ENABLE reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); reg_val |= SUNXI_SPDIF_TXCFG_TXEN; writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); @@ -99,13 +98,8 @@ void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on) reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT); reg_val |= SUNXI_SPDIF_INT_TXDRQEN; writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT); - - //global enable - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); - reg_val |= SUNXI_SPDIF_CTL_GEN; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); } else { - //SPDIF TX DISABALE + //SPDIF TX DISABLE reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); reg_val &= ~SUNXI_SPDIF_TXCFG_TXEN; writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); @@ -114,16 +108,48 @@ void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on) reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT); reg_val &= ~SUNXI_SPDIF_INT_TXDRQEN; writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT); - - //global disable - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); - reg_val &= ~SUNXI_SPDIF_CTL_GEN; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); } } -void sunxi_snd_rxctrl(int on) +void sunxi_snd_rxctrl(struct snd_pcm_substream *substream, int on) { + u32 reg_val; + + /*flush RX FIFO and set FIFO empty trigger level*/ + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); + reg_val |= SUNXI_SPDIF_FCTL_RXTL(0x0F); //RX FIFO Trigger Level + reg_val |= SUNXI_SPDIF_FCTL_FRX; //flush RX FIFO + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); + + /*clear interrupt status*/ + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_ISTA); + reg_val |= SUNXI_SPDIF_ISTA_RXCLR; + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_ISTA); + + /*clear RX counter*/ + writel(0, sunxi_spdif.regs + SUNXI_SPDIF_RXCNT); + + if (on) { + /*SPDIF RX ENABLE*/ + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); + reg_val |= SUNXI_SPDIF_RXCFG_RXEN; + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); + + /*DRQ ENABLE*/ + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT); + reg_val |= SUNXI_SPDIF_INT_RXDRQEN; + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT); + } else { + /*SPDIF TX DISABLE*/ + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); + reg_val &= ~SUNXI_SPDIF_RXCFG_RXEN; + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); + + /*DRQ DISABLE*/ + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT); + reg_val &= ~SUNXI_SPDIF_INT_RXDRQEN; + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT); + } } static inline int sunxi_snd_is_clkmaster(void) @@ -135,63 +161,122 @@ static int sunxi_spdif_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { u32 reg_val; - reg_val = 0; - reg_val &= ~SUNXI_SPDIF_TXCFG_SINGLEMOD; - reg_val |= SUNXI_SPDIF_TXCFG_ASS; - reg_val &= ~SUNXI_SPDIF_TXCFG_NONAUDIO; - reg_val |= SUNXI_SPDIF_TXCFG_FMT16BIT; - reg_val |= SUNXI_SPDIF_TXCFG_CHSTMODE; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - - reg_val = 0; - reg_val &= ~SUNXI_SPDIF_FCTL_FIFOSRC; - reg_val |= SUNXI_SPDIF_FCTL_TXTL(16); - reg_val |= SUNXI_SPDIF_FCTL_RXTL(15); - reg_val |= SUNXI_SPDIF_FCTL_TXIM(1); - reg_val |= SUNXI_SPDIF_FCTL_RXOM(3); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); - if (!fmt) {//PCM - reg_val = 0; + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); + reg_val &= ~SUNXI_SPDIF_TXCFG_NONAUDIO; + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); + + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); reg_val |= (SUNXI_SPDIF_TXCHSTA0_CHNUM(2)); + reg_val &= ~SUNXI_SPDIF_TXCHSTA0_AUDIO; writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val = 0; - reg_val |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); + reg_val |= (SUNXI_SPDIF_RXCHSTA0_CHNUM(2)); + reg_val &= ~SUNXI_SPDIF_RXCHSTA0_AUDIO; + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); } else { //non PCM reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); reg_val |= SUNXI_SPDIF_TXCFG_NONAUDIO; writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - reg_val = 0; + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); reg_val |= (SUNXI_SPDIF_TXCHSTA0_CHNUM(2)); reg_val |= SUNXI_SPDIF_TXCHSTA0_AUDIO; writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val = 0; - reg_val |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); + reg_val |= (SUNXI_SPDIF_RXCHSTA0_CHNUM(2)); + reg_val |= SUNXI_SPDIF_RXCHSTA0_AUDIO; + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); } return 0; } static int sunxi_spdif_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sunxi_dma_params *dma_data; + u32 reg_val, reg_val1; + int format; + switch (params_format(params)) + { + case SNDRV_PCM_FORMAT_S16_LE: + format = 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + format = 20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + format = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + format = 24; + break; + default: + return -EINVAL; + } - /* play or record */ - if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + /* playback or capture */ + if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dma_data = &sunxi_spdif_stereo_out; - else + + reg_val1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); + reg_val &= ~SUNXI_SPDIF_TXCFG_FMTRVD; + reg_val1 &= ~SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN; + reg_val1 &= ~(SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(7)); + if(format == 16) { + reg_val |= SUNXI_SPDIF_TXCFG_FMT16BIT; + reg_val1 |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1)); + } + else if(format == 20) { + reg_val |= SUNXI_SPDIF_TXCFG_FMT20BIT; + reg_val1 |= SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN; + reg_val1 |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1)); + } + else { + reg_val |= SUNXI_SPDIF_TXCFG_FMT24BIT; + reg_val1 |= SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN; + reg_val1 |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(5)); + } + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); + writel(reg_val1, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + + /* Set TX FIFO Input Mode */ + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); + reg_val |= SUNXI_SPDIF_FCTL_TXIM1; //1. Valid data at the LSB of OWA_TXFIFO register + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); + } + else { dma_data = &sunxi_spdif_stereo_in; + /* Set TX FIFO Input Mode */ + reg_val1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); + reg_val &= ~SUNXI_SPDIF_FCTL_RXOM3; + reg_val1 &= ~SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN; + reg_val1 &= ~(SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(7)); + + if(format == 16) { + reg_val |= SUNXI_SPDIF_FCTL_RXOM3; + reg_val1 |= (SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(1)); + } + else if(format == 20) { + reg_val1 |= SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN; + reg_val1 |= (SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(1)); + } + else { + reg_val1 |= SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN; + reg_val1 |= (SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(5)); + } + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); + writel(reg_val1, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); + } snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data); - return 0; } @@ -208,7 +293,7 @@ static int sunxi_spdif_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - sunxi_snd_rxctrl(1); + sunxi_snd_rxctrl(substream, 1); } else { sunxi_snd_txctrl(substream, 1); } @@ -218,9 +303,9 @@ static int sunxi_spdif_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - sunxi_snd_rxctrl(0); + sunxi_snd_rxctrl(substream, 0); } else { - sunxi_snd_txctrl(substream, 0); + sunxi_snd_txctrl(substream, 0); } break; default: @@ -246,23 +331,31 @@ static int sunxi_spdif_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div) { - u32 reg_val = 0; - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val &= ~(SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xf)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val &= ~(SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xf)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + u32 reg_val_txchsta0 = 0; + u32 reg_val_txchsta1 = 0; + u32 reg_val_rxchsta0 = 0; + u32 reg_val_rxchsta1 = 0; + u32 reg_val_txcfg = 0; switch(div_id) { case SUNXI_DIV_MCLK: { - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - reg_val &= ~SUNXI_SPDIF_TXCFG_TXRATIO(0x1F); - reg_val |= SUNXI_SPDIF_TXCFG_TXRATIO(div-1); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); + reg_val_txchsta0 = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); + reg_val_txchsta0 &= ~(SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xf)); + + reg_val_txchsta1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta1 &= ~(SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xf)); + + reg_val_rxchsta0 = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); + reg_val_rxchsta0 &= ~(SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xf)); + + reg_val_rxchsta1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); + reg_val_rxchsta1 &= ~(SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xf)); + + reg_val_txcfg = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); + reg_val_txcfg &= ~SUNXI_SPDIF_TXCFG_TXRATIO(0x1F); + reg_val_txcfg |= SUNXI_SPDIF_TXCFG_TXRATIO(div-1); + writel(reg_val_txcfg, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); if(clk_get_rate(spdif_pll2clk) == 24576000) { @@ -270,67 +363,49 @@ static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int d { //24KHZ case 8: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x6)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x9)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x6)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x9)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x6)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x9)); break; //32KHZ case 6: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x3)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xC)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x3)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xC)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x3)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xC)); break; //48KHZ case 4: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x2)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xD)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x2)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xD)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x2)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xD)); break; //96KHZ case 2: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xA)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x5)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xA)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x5)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xA)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x5)); break; //192KHZ case 1: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xE)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x1)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xE)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x1)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xE)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x1)); break; default: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(1)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0)); break; } }else{ //22.5792MHz @@ -338,59 +413,50 @@ static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int d { //22.05khz case 8: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x4)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xb)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x4)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xb)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x4)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xb)); break; //44.1KHZ case 4: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x0)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xF)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x0)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xF)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x0)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xF)); break; //88.2khz case 2: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x8)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x7)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x8)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x7)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x8)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x7)); break; //176.4KHZ case 1: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xC)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x3)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xC)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x3)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xC)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x3)); break; default: - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); - reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0)); - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1)); + reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0)); + reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(1)); + reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0)); + break; } } + writel(reg_val_txchsta0, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); + writel(reg_val_txchsta1, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + + writel(reg_val_rxchsta0, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); + writel(reg_val_rxchsta1, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); } break; case SUNXI_DIV_BCLK: @@ -399,7 +465,6 @@ static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int d default: return -EINVAL; } - return 0; } @@ -422,10 +487,13 @@ static void spdifregsave(void) { regsave[0] = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); regsave[1] = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - regsave[2] = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL) | (0x3<<16); + regsave[2] = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL) | (0x3<<16); //clear TX, RX fifo regsave[3] = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT); regsave[4] = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); regsave[5] = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + regsave[6] = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); + regsave[7] = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); + regsave[8] = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); } static void spdifregrestore(void) @@ -436,14 +504,18 @@ static void spdifregrestore(void) writel(regsave[3], sunxi_spdif.regs + SUNXI_SPDIF_INT); writel(regsave[4], sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); writel(regsave[5], sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); + writel(regsave[6], sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); + writel(regsave[7], sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); + writel(regsave[8], sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); } //#ifdef CONFIG_PM static int sunxi_spdif_suspend(struct snd_soc_dai *cpu_dai) { u32 reg_val; - printk("[SPDIF]Enter %s\n", __func__); + printk("[SPDIF] Enter %s\n", __func__); + //global disable reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); reg_val &= ~SUNXI_SPDIF_CTL_GEN; writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); @@ -453,7 +525,9 @@ static int sunxi_spdif_suspend(struct snd_soc_dai *cpu_dai) //disable the module clock clk_disable(spdif_moduleclk); + //clear start? clk_disable(spdif_apbclk); + //clear end? printk("[SPDIF]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); printk("[SPDIF]SPECIAL CLK 0x01c200C0 = %#x, line= %d\n", *(volatile int*)0xF1C200C0, __LINE__); @@ -464,9 +538,9 @@ static int sunxi_spdif_suspend(struct snd_soc_dai *cpu_dai) static int sunxi_spdif_resume(struct snd_soc_dai *cpu_dai) { u32 reg_val; - printk("[SPDIF]Enter %s\n", __func__); + printk("[SPDIF] Enter %s\n", __func__); - //disable the module clock + //enable the module clock clk_enable(spdif_apbclk); //enable the module clock @@ -474,11 +548,18 @@ static int sunxi_spdif_resume(struct snd_soc_dai *cpu_dai) spdifregrestore(); + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); + reg_val &= ~SUNXI_SPDIF_FCTL_FIFOSRC; //set TX FIFO source select as APB bus + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); + //soft reset SPDIF + reg_val |= SUNXI_SPDIF_CTL_RESET; + //global enable reg_val |= SUNXI_SPDIF_CTL_GEN; writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); - //printk("[SPDIF]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008); + printk("[SPDIF]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008); printk("[SPDIF]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); printk("[SPDIF]SPECIAL CLK 0x01c200C0 = %#x, line = %d\n", *(volatile int*)0xF1C200C0, __LINE__); @@ -487,11 +568,11 @@ static int sunxi_spdif_resume(struct snd_soc_dai *cpu_dai) #define SUNXI_SPDIF_RATES (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT) static struct snd_soc_dai_ops sunxi_spdif_dai_ops = { - .trigger = sunxi_spdif_trigger, + .trigger = sunxi_spdif_trigger, .hw_params = sunxi_spdif_hw_params, - .set_fmt = sunxi_spdif_set_fmt, - .set_clkdiv = sunxi_spdif_set_clkdiv, - .set_sysclk = sunxi_spdif_set_sysclk, + .set_fmt = sunxi_spdif_set_fmt, + .set_clkdiv = sunxi_spdif_set_clkdiv, + .set_sysclk = sunxi_spdif_set_sysclk, }; static struct snd_soc_dai_driver sunxi_spdif_dai = { .probe = sunxi_spdif_dai_probe, @@ -502,14 +583,14 @@ static struct snd_soc_dai_driver sunxi_spdif_dai = { .channels_min = 1, .channels_max = 2, .rates = SUNXI_SPDIF_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .formats = SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,}, .capture = { .channels_min = 1, .channels_max = 2, .rates = SUNXI_SPDIF_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .symmetric_rates = 1, + .formats = SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,}, .ops = &sunxi_spdif_dai_ops, + .symmetric_rates = 1, }; static int __devinit sunxi_spdif_dev_probe(struct platform_device *pdev) @@ -521,46 +602,58 @@ static int __devinit sunxi_spdif_dev_probe(struct platform_device *pdev) if(sunxi_spdif.regs == NULL) return -ENXIO; - //spdif apbclk - spdif_apbclk = clk_get(NULL, "apb_spdif"); - if(-1 == clk_enable(spdif_apbclk)){ - printk("spdif_apbclk failed! line = %d\n", __LINE__); - } + //spdif apbclk + spdif_apbclk = clk_get(NULL, "apb_spdif"); + if(-1 == clk_enable(spdif_apbclk)){ + printk("spdif_apbclk failed! line = %d\n", __LINE__); + } - spdif_pllx8 = clk_get(NULL, "audio_pllx8"); + spdif_pllx8 = clk_get(NULL, "audio_pllx8"); - //spdif pll2clk - spdif_pll2clk = clk_get(NULL, "audio_pll"); + //spdif pll2clk + spdif_pll2clk = clk_get(NULL, "audio_pll"); - //spdif module clk - spdif_moduleclk = clk_get(NULL, "spdif"); + //spdif module clk + spdif_moduleclk = clk_get(NULL, "spdif"); - if(clk_set_parent(spdif_moduleclk, spdif_pll2clk)){ - printk("try to set parent of spdif_moduleclk to spdif_pll2ck failed! line = %d\n",__LINE__); - } + if(clk_set_parent(spdif_moduleclk, spdif_pll2clk)){ + printk("try to set parent of spdif_moduleclk to spdif_pll2ck failed! line = %d\n",__LINE__); + } - if(clk_set_rate(spdif_moduleclk, 24576000/8)){ - printk("set spdif_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__); - } + if(clk_set_rate(spdif_moduleclk, 24576000/8)){ + printk("set spdif_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__); + } - if(-1 == clk_enable(spdif_moduleclk)){ - printk("open spdif_moduleclk failed! line = %d\n", __LINE__); - } + if(-1 == clk_enable(spdif_moduleclk)){ + printk("open spdif_moduleclk failed! line = %d\n", __LINE__); + } - //global enbale - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); - reg_val |= SUNXI_SPDIF_CTL_GEN; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); + reg_val &= ~SUNXI_SPDIF_FCTL_FIFOSRC; //set TX FIFO source select as APB bus + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); + + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); + //soft reset SPDIF + reg_val |= SUNXI_SPDIF_CTL_RESET; + //global enable + reg_val |= SUNXI_SPDIF_CTL_GEN; + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); - ret = snd_soc_register_dai(&pdev->dev, &sunxi_spdif_dai); + ret = snd_soc_register_dai(&pdev->dev, &sunxi_spdif_dai); - iounmap(sunxi_spdif.ioregs); + iounmap(sunxi_spdif.ioregs); return 0; } static int __devexit sunxi_spdif_dev_remove(struct platform_device *pdev) { + int reg_val = 0; + //global disable + reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); + reg_val &= ~SUNXI_SPDIF_CTL_GEN; + writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); + /* release the module clock */ clk_disable(spdif_moduleclk); diff --git a/sound/soc/sunxi/spdif/sunxi_spdif.h b/sound/soc/sunxi/spdif/sunxi_spdif.h index 67955d8..08630c9 100644 --- a/sound/soc/sunxi/spdif/sunxi_spdif.h +++ b/sound/soc/sunxi/spdif/sunxi_spdif.h @@ -17,127 +17,134 @@ #define SUNXI_SPDIF_H_ /*------------------SPDIF register definition--------------------*/ -#define SUNXI_SPDIFBASE 0x01C21000 +#define SUNXI_SPDIFBASE 0x01C21000 -#define SUNXI_SPDIF_CTL (0x00) +#define SUNXI_SPDIF_CTL (0x00) #define SUNXI_SPDIF_CTL_MCLKDIV(v) ((v)<<4) //v even - #define SUNXI_SPDIF_CTL_MCLKOUTEN (1<<2) - #define SUNXI_SPDIF_CTL_GEN (1<<1) - #define SUNXI_SPDIF_CTL_RESET (1<<0) + #define SUNXI_SPDIF_CTL_MCLKOUTEN (1<<2) + #define SUNXI_SPDIF_CTL_GEN (1<<1) + #define SUNXI_SPDIF_CTL_RESET (1<<0) -#define SUNXI_SPDIF_TXCFG (0x04) +#define SUNXI_SPDIF_TXCFG (0x04) #define SUNXI_SPDIF_TXCFG_SINGLEMOD (1<<31) - #define SUNXI_SPDIF_TXCFG_ASS (1<<17) + #define SUNXI_SPDIF_TXCFG_ASS (1<<17) #define SUNXI_SPDIF_TXCFG_NONAUDIO (1<<16) - #define SUNXI_SPDIF_TXCFG_TXRATIO(v) ((v)<<4) - #define SUNXI_SPDIF_TXCFG_FMTRVD (3<<2) + #define SUNXI_SPDIF_TXCFG_TXRATIO(v) ((v)<<4) + #define SUNXI_SPDIF_TXCFG_FMTRVD (3<<2) #define SUNXI_SPDIF_TXCFG_FMT16BIT (0<<2) #define SUNXI_SPDIF_TXCFG_FMT20BIT (1<<2) #define SUNXI_SPDIF_TXCFG_FMT24BIT (2<<2) #define SUNXI_SPDIF_TXCFG_CHSTMODE (1<<1) - #define SUNXI_SPDIF_TXCFG_TXEN (1<<0) + #define SUNXI_SPDIF_TXCFG_TXEN (1<<0) #define SUNXI_SPDIF_RXCFG (0x08) #define SUNXI_SPDIF_RXCFG_LOCKFLAG (1<<4) - #define SUNXI_SPDIF_RXCFG_CHSTSRC (1<<3) - #define SUNXI_SPDIF_RXCFG_CHSTCP (1<<1) - #define SUNXI_SPDIF_RXCFG_RXEN (1<<0) - -#define SUNXI_SPDIF_TXFIFO (0x0C) - -#define SUNXI_SPDIF_RXFIFO (0x10) - -#define SUNXI_SPDIF_FCTL (0x14) - #define SUNXI_SPDIF_FCTL_FIFOSRC (1<<31) - #define SUNXI_SPDIF_FCTL_FTX (1<<17) - #define SUNXI_SPDIF_FCTL_FRX (1<<16) - #define SUNXI_SPDIF_FCTL_TXTL(v) ((v)<<8) - #define SUNXI_SPDIF_FCTL_RXTL(v) (((v))<<3) - #define SUNXI_SPDIF_FCTL_TXIM(v) ((v)<<2) - #define SUNXI_SPDIF_FCTL_RXOM(v) ((v)<<0) - -#define SUNXI_SPDIF_FSTA (0x18) - #define SUNXI_SPDIF_FSTA_TXE (1<<14) + #define SUNXI_SPDIF_RXCFG_CHSTSRC (1<<3) + #define SUNXI_SPDIF_RXCFG_CHSTCP (1<<1) + #define SUNXI_SPDIF_RXCFG_RXEN (1<<0) + +#define SUNXI_SPDIF_TXFIFO (0x0C) + +#define SUNXI_SPDIF_RXFIFO (0x10) + +#define SUNXI_SPDIF_FCTL (0x14) + #define SUNXI_SPDIF_FCTL_FIFOSRC (1<<31) + #define SUNXI_SPDIF_FCTL_FTX (1<<17) + #define SUNXI_SPDIF_FCTL_FRX (1<<16) + #define SUNXI_SPDIF_FCTL_TXTL(v) ((v)<<8) + #define SUNXI_SPDIF_FCTL_RXTL(v) ((v)<<3) + #define SUNXI_SPDIF_FCTL_TXIM0 (0<<2) + #define SUNXI_SPDIF_FCTL_TXIM1 (1<<2) + #define SUNXI_SPDIF_FCTL_RXOM0 (0<<0) + #define SUNXI_SPDIF_FCTL_RXOM1 (1<<0) + #define SUNXI_SPDIF_FCTL_RXOM2 (2<<0) + #define SUNXI_SPDIF_FCTL_RXOM3 (3<<0) + +#define SUNXI_SPDIF_FSTA (0x18) + #define SUNXI_SPDIF_FSTA_TXE (1<<14) #define SUNXI_SPDIF_FSTA_TXECNTSHT (8) - #define SUNXI_SPDIF_FSTA_RXA (1<<6) + #define SUNXI_SPDIF_FSTA_RXA (1<<6) #define SUNXI_SPDIF_FSTA_RXACNTSHT (0) -#define SUNXI_SPDIF_INT (0x1C) - #define SUNXI_SPDIF_INT_RXLOCKEN (1<<18) +#define SUNXI_SPDIF_INT (0x1C) + #define SUNXI_SPDIF_INT_RXLOCKEN (1<<18) #define SUNXI_SPDIF_INT_RXUNLOCKEN (1<<17) #define SUNXI_SPDIF_INT_RXPARERREN (1<<16) - #define SUNXI_SPDIF_INT_TXDRQEN (1<<7) - #define SUNXI_SPDIF_INT_TXUIEN (1<<6) - #define SUNXI_SPDIF_INT_TXOIEN (1<<5) - #define SUNXI_SPDIF_INT_TXEIEN (1<<4) - #define SUNXI_SPDIF_INT_RXDRQEN (1<<2) - #define SUNXI_SPDIF_INT_RXOIEN (1<<1) - #define SUNXI_SPDIF_INT_RXAIEN (1<<0) - -#define SUNXI_SPDIF_ISTA (0x20) + #define SUNXI_SPDIF_INT_TXDRQEN (1<<7) + #define SUNXI_SPDIF_INT_TXUIEN (1<<6) + #define SUNXI_SPDIF_INT_TXOIEN (1<<5) + #define SUNXI_SPDIF_INT_TXEIEN (1<<4) + #define SUNXI_SPDIF_INT_RXDRQEN (1<<2) + #define SUNXI_SPDIF_INT_RXOIEN (1<<1) + #define SUNXI_SPDIF_INT_RXAIEN (1<<0) + +#define SUNXI_SPDIF_ISTA (0x20) #define SUNXI_SPDIF_ISTA_RXLOCKSTA (1<<18) - #define SUNXI_SPDIF_ISTA_RXUNLOCKSTA (1<<17) - #define SUNXI_SPDIF_ISTA_RXPARERRSTA (1<<16) - #define SUNXI_SPDIF_ISTA_TXUSTA (1<<6) - #define SUNXI_SPDIF_ISTA_TXOSTA (1<<5) - #define SUNXI_SPDIF_ISTA_TXESTA (1<<4) - #define SUNXI_SPDIF_ISTA_RXOSTA (1<<1) - #define SUNXI_SPDIF_ISTA_RXASTA (1<<0) - -#define SUNXI_SPDIF_TXCNT (0x24) - -#define SUNXI_SPDIF_RXCNT (0x28) - -#define SUNXI_SPDIF_TXCHSTA0 (0x2C) - #define SUNXI_SPDIF_TXCHSTA0_CLK(v) ((v)<<28) - #define SUNXI_SPDIF_TXCHSTA0_SAMFREQ(v) ((v)<<24) - #define SUNXI_SPDIF_TXCHSTA0_CHNUM(v) ((v)<<20) - #define SUNXI_SPDIF_TXCHSTA0_SRCNUM(v) ((v)<<16) - #define SUNXI_SPDIF_TXCHSTA0_CATACOD(v) ((v)<<8) - #define SUNXI_SPDIF_TXCHSTA0_MODE(v) ((v)<<6) - #define SUNXI_SPDIF_TXCHSTA0_EMPHASIS(v) ((v)<<3) - #define SUNXI_SPDIF_TXCHSTA0_CP (1<<2) - #define SUNXI_SPDIF_TXCHSTA0_AUDIO (1<<1) - #define SUNXI_SPDIF_TXCHSTA0_PRO (1<<0) - -#define SUNXI_SPDIF_TXCHSTA1 (0x30) - #define SUNXI_SPDIF_TXCHSTA1_CGMSA(v) ((v)<<8) + #define SUNXI_SPDIF_ISTA_RXUNLOCKSTA (1<<17) + #define SUNXI_SPDIF_ISTA_RXPARERRSTA (1<<16) + #define SUNXI_SPDIF_ISTA_TXUSTA (1<<6) + #define SUNXI_SPDIF_ISTA_TXOSTA (1<<5) + #define SUNXI_SPDIF_ISTA_TXESTA (1<<4) + #define SUNXI_SPDIF_ISTA_RXOSTA (1<<1) + #define SUNXI_SPDIF_ISTA_RXASTA (1<<0) + #define SUNXI_SPDIF_ISTA_RXCLR (SUNXI_SPDIF_ISTA_RXLOCKSTA | SUNXI_SPDIF_ISTA_RXUNLOCKSTA | SUNXI_SPDIF_ISTA_RXPARERRSTA | SUNXI_SPDIF_ISTA_RXOSTA | SUNXI_SPDIF_ISTA_RXASTA) + #define SUNXI_SPDIF_ISTA_TXCLR (SUNXI_SPDIF_ISTA_TXUSTA | SUNXI_SPDIF_ISTA_TXOSTA | SUNXI_SPDIF_ISTA_TXESTA) + + +#define SUNXI_SPDIF_TXCNT (0x24) + +#define SUNXI_SPDIF_RXCNT (0x28) + +#define SUNXI_SPDIF_TXCHSTA0 (0x2C) + #define SUNXI_SPDIF_TXCHSTA0_CLK(v) ((v)<<28) + #define SUNXI_SPDIF_TXCHSTA0_SAMFREQ(v) ((v)<<24) + #define SUNXI_SPDIF_TXCHSTA0_CHNUM(v) ((v)<<20) + #define SUNXI_SPDIF_TXCHSTA0_SRCNUM(v) ((v)<<16) + #define SUNXI_SPDIF_TXCHSTA0_CATACOD(v) ((v)<<8) + #define SUNXI_SPDIF_TXCHSTA0_MODE(v) ((v)<<6) + #define SUNXI_SPDIF_TXCHSTA0_EMPHASIS(v) ((v)<<3) + #define SUNXI_SPDIF_TXCHSTA0_CP (1<<2) + #define SUNXI_SPDIF_TXCHSTA0_AUDIO (1<<1) + #define SUNXI_SPDIF_TXCHSTA0_PRO (1<<0) + +#define SUNXI_SPDIF_TXCHSTA1 (0x30) + #define SUNXI_SPDIF_TXCHSTA1_CGMSA(v) ((v)<<8) #define SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(v) ((v)<<4) #define SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(v) ((v)<<1) - #define SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN (1<<0) - -#define SUNXI_SPDIF_RXCHSTA0 (0x34) - #define SUNXI_SPDIF_RXCHSTA0_CLK(v) ((v)<<28) - #define SUNXI_SPDIF_RXCHSTA0_SAMFREQ(v) ((v)<<24) - #define SUNXI_SPDIF_RXCHSTA0_CHNUM(v) ((v)<<20) - #define SUNXI_SPDIF_RXCHSTA0_SRCNUM(v) ((v)<<16) - #define SUNXI_SPDIF_RXCHSTA0_CATACOD(v) ((v)<<8) - #define SUNXI_SPDIF_RXCHSTA0_MODE(v) ((v)<<6) - #define SUNXI_SPDIF_RXCHSTA0_EMPHASIS(v) ((v)<<3) - #define SUNXI_SPDIF_RXCHSTA0_CP (1<<2) - #define SUNXI_SPDIF_RXCHSTA0_AUDIO (1<<1) - #define SUNXI_SPDIF_RXCHSTA0_PRO (1<<0) - -#define SUNXI_SPDIF_RXCHSTA1 (0x38) - #define SUNXI_SPDIF_RXCHSTA1_CGMSA(v) ((v)<<8) + #define SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN (1<<0) + +#define SUNXI_SPDIF_RXCHSTA0 (0x34) + #define SUNXI_SPDIF_RXCHSTA0_CLK(v) ((v)<<28) + #define SUNXI_SPDIF_RXCHSTA0_SAMFREQ(v) ((v)<<24) + #define SUNXI_SPDIF_RXCHSTA0_CHNUM(v) ((v)<<20) + #define SUNXI_SPDIF_RXCHSTA0_SRCNUM(v) ((v)<<16) + #define SUNXI_SPDIF_RXCHSTA0_CATACOD(v) ((v)<<8) + #define SUNXI_SPDIF_RXCHSTA0_MODE(v) ((v)<<6) + #define SUNXI_SPDIF_RXCHSTA0_EMPHASIS(v) ((v)<<3) + #define SUNXI_SPDIF_RXCHSTA0_CP (1<<2) + #define SUNXI_SPDIF_RXCHSTA0_AUDIO (1<<1) + #define SUNXI_SPDIF_RXCHSTA0_PRO (1<<0) + +#define SUNXI_SPDIF_RXCHSTA1 (0x38) + #define SUNXI_SPDIF_RXCHSTA1_CGMSA(v) ((v)<<8) #define SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(v) ((v)<<4) #define SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(v) ((v)<<1) - #define SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN (1<<0) + #define SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN (1<<0) /*--------------------------------CCM register definition---------------------*/ -#define SUNXI_CCMBASE (0x01C20000) +#define SUNXI_CCMBASE (0x01C20000) -#define SUNXI_CCMBASE_AUDIOHOSCPLL (0x08) - #define SUNXI_CCMBASE_AUDIOHOSCPLL_EN (1<<31) - #define SUNXI_CCMBASE_AUDIOHOSCPLL_24576M (1<<27) - #define SUNXI_CCMBASE_AUDIOHOSCPLL_225792M (0<<27) +#define SUNXI_CCMBASE_AUDIOHOSCPLL (0x08) + #define SUNXI_CCMBASE_AUDIOHOSCPLL_EN (1<<31) + #define SUNXI_CCMBASE_AUDIOHOSCPLL_24576M (1<<27) + #define SUNXI_CCMBASE_AUDIOHOSCPLL_225792M (0<<27) -#define SUNXI_CCMBASE_APBGATE (0x68) - #define SUNXI_CCMBASE_APBGATE_SPDIFGATE (1<<1) +#define SUNXI_CCMBASE_APBGATE (0x68) + #define SUNXI_CCMBASE_APBGATE_SPDIFGATE (1<<1) -#define SUNXI_CCMBASE_AUDIOCLK (0xC0) +#define SUNXI_CCMBASE_AUDIOCLK (0xC0) #define SUNXI_CCMBASE_AUDIOCLK_SPDIFSPEGATE (1<<31) - #define SUNXI_CCMBASE_AUDIOCLK_DIV(v) ((v)<<16) + #define SUNXI_CCMBASE_AUDIOCLK_DIV(v) ((v)<<16) /* Clock dividers */ #define SUNXI_DIV_MCLK 0 @@ -156,6 +163,6 @@ extern struct sunxi_spdif_info sunxi_spdif; unsigned int sunxi_spdif_get_clockrate(void); extern void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on); -extern void sunxi_snd_rxctrl(int on); +extern void sunxi_snd_rxctrl(struct snd_pcm_substream *substream, int on); #endif diff --git a/sound/soc/sunxi/spdif/sunxi_spdma.c b/sound/soc/sunxi/spdif/sunxi_spdma.c index ec4ac99..d5377fc 100644 --- a/sound/soc/sunxi/spdif/sunxi_spdma.c +++ b/sound/soc/sunxi/spdif/sunxi_spdma.c @@ -34,25 +34,40 @@ #include "sunxi_spdif.h" #include "sunxi_spdma.h" -static volatile unsigned int dmasrc = 0; -static volatile unsigned int dmadst = 0; +static const struct snd_pcm_hardware sunxi_pcm_out_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE |SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 128*1024, /* value must be (2^n)Kbyte size */ + .period_bytes_min = 1024,//1024*4, + .period_bytes_max = 1024*32,//1024*128, + .periods_min = 4, + .periods_max = 8, + .fifo_size = 32, +}; -static const struct snd_pcm_hardware sunxi_pcm_hardware = { +static const struct snd_pcm_hardware sunxi_pcm_in_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE |SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .rate_min = 8000, .rate_max = 192000, .channels_min = 1, .channels_max = 2, - .buffer_bytes_max = 128*1024, //1024*1024 /* value must be (2^n)Kbyte size */ - .period_bytes_min = 1024*4,//1024*4, + .buffer_bytes_max = 128*1024, /* value must be (2^n)Kbyte size */ + .period_bytes_min = 1024,//1024*4, .period_bytes_max = 1024*32,//1024*128, - .periods_min = 4,//8, - .periods_max = 8,//8, - .fifo_size = 32,//32, + .periods_min = 4, + .periods_max = 8, + .fifo_size = 32, }; struct sunxi_runtime_data { @@ -65,6 +80,8 @@ struct sunxi_runtime_data { dma_addr_t dma_pos; dma_addr_t dma_end; struct sunxi_dma_params *params; + /*DMA data width*/ + unsigned int dma_width; }; static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream) @@ -73,8 +90,8 @@ static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream) dma_addr_t pos = prtd->dma_pos; unsigned int limit; int ret; - unsigned long len = prtd->dma_period; + int read = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1; limit = prtd->dma_limit; while(prtd->dma_loaded < limit){ @@ -82,7 +99,7 @@ static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream) len = prtd->dma_end - pos; } - ret = sunxi_dma_enqueue(prtd->params, pos, len, 0); + ret = sunxi_dma_enqueue(prtd->params, pos, len, read); if (ret == 0) { prtd->dma_loaded++; pos += prtd->dma_period; @@ -120,17 +137,24 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, struct sunxi_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; unsigned long totbytes = params_buffer_bytes(params); - struct sunxi_dma_params *dma = - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + struct sunxi_dma_params *dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); int ret = 0; if (!dma) return 0; + if (SNDRV_PCM_FORMAT_S16_LE == params_format(params)) { + prtd->dma_width = 16; + } + else { + prtd->dma_width = 32; + } + if (prtd->params == NULL) { prtd->params = dma; ret = sunxi_dma_request(prtd->params, 0); if (ret < 0) { - return ret; + pr_err("[SPDIF] sunxi_dma_request failed! (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); + return ret; } } @@ -138,6 +162,7 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, substream) != 0) { sunxi_dma_release(prtd->params); prtd->params = NULL; + pr_err("[SPDIF] sunxi_dma_set_callback failed! (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); return -EINVAL; } @@ -153,6 +178,7 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, prtd->dma_pos = prtd->dma_start; prtd->dma_end = prtd->dma_start + totbytes; spin_unlock_irq(&prtd->lock); + return 0; } @@ -197,16 +223,22 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream) #else dma_config_t spdif_dma_conf; memset(&spdif_dma_conf, 0, sizeof(spdif_dma_conf)); - spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; + if(prtd->dma_width > 16) { + spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT; + spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT; + } + else { + spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; + spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; + } spdif_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; - spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; spdif_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4; spdif_dma_conf.address_type.src_addr_mode = NDMA_ADDR_INCREMENT; spdif_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_NOCHANGE; spdif_dma_conf.bconti_mode = false; spdif_dma_conf.irq_spt = CHAN_IRQ_FD; spdif_dma_conf.src_drq_type = N_SRC_SDRAM; - spdif_dma_conf.dst_drq_type = N_DST_SPDIF_TX;//DRQDST_SPDIFTX; + spdif_dma_conf.dst_drq_type = N_DST_SPDIF_TX; #endif ret = sunxi_dma_config(prtd->params, &spdif_dma_conf, 0); } else { @@ -221,15 +253,32 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream) spdif_dma_conf.hf_irq = SW_DMA_IRQ_FULL|SW_DMA_IRQ_HALF; spdif_dma_conf.from = prtd->params->dma_addr; spdif_dma_conf.to = prtd->dma_start; - ret = sunxi_dma_config(prtd->params, &spdif_dma_conf, 0); #else - return -EINVAL; + dma_config_t spdif_dma_conf; + memset(&spdif_dma_conf, 0, sizeof(spdif_dma_conf)); + if(prtd->dma_width > 16) { + spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT; + spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT; + } + else { + spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; + spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; + } + spdif_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; + spdif_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4; + spdif_dma_conf.address_type.src_addr_mode = NDMA_ADDR_NOCHANGE; + spdif_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_INCREMENT; + spdif_dma_conf.bconti_mode = false; + spdif_dma_conf.irq_spt = CHAN_IRQ_FD; + spdif_dma_conf.src_drq_type = N_SRC_SPDIF_RX; + spdif_dma_conf.dst_drq_type = N_DST_SDRAM; #endif + ret = sunxi_dma_config(prtd->params, &spdif_dma_conf, 0); } /* flush the DMA channel */ prtd->dma_loaded = 0; - if (sunxi_dma_flush(prtd->params) == 0) - prtd->dma_pos = prtd->dma_start; + sunxi_dma_flush(prtd->params); + prtd->dma_pos = prtd->dma_start; /* enqueue dma buffers */ sunxi_pcm_enqueue(substream); @@ -248,12 +297,14 @@ static int sunxi_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: sunxi_dma_start(prtd->params); + prtd->state |= ST_RUNNING; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: sunxi_dma_stop(prtd->params); + prtd->state &= ~ST_RUNNING; break; default: @@ -271,21 +322,26 @@ static snd_pcm_uframes_t sunxi_pcm_pointer(struct snd_pcm_substream *substream) struct sunxi_runtime_data *prtd = runtime->private_data; unsigned long res = 0; snd_pcm_uframes_t offset = 0; + dma_addr_t dmasrc = 0; + dma_addr_t dmadst = 0; + spin_lock(&prtd->lock); sunxi_dma_getcurposition(prtd->params, (dma_addr_t*)&dmasrc, (dma_addr_t*)&dmadst); - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE){ - res = dmadst - prtd->dma_start; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){ + res = dmasrc + prtd->dma_period - prtd->dma_start; } else { - offset = bytes_to_frames(runtime, dmasrc + prtd->dma_period - runtime->dma_addr); + res = dmadst + prtd->dma_period - prtd->dma_start; } + offset = bytes_to_frames(runtime, res); spin_unlock(&prtd->lock); if(offset >= runtime->buffer_size) offset = 0; - return offset; + + return offset; } static int sunxi_pcm_open(struct snd_pcm_substream *substream) @@ -294,11 +350,17 @@ static int sunxi_pcm_open(struct snd_pcm_substream *substream) struct sunxi_runtime_data *prtd; snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_hardware); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_out_hardware); + } else { + snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_in_hardware); + } prtd = kzalloc(sizeof(struct sunxi_runtime_data), GFP_KERNEL); - if (prtd == NULL) + if (prtd == NULL) { + pr_err("[SPDIF] try to alloc sunxi_runtime_data failed %s,%d\n", __func__,__LINE__); return -ENOMEM; + } spin_lock_init(&prtd->lock); @@ -311,7 +373,6 @@ static int sunxi_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct sunxi_runtime_data *prtd = runtime->private_data; kfree(prtd); - return 0; } @@ -342,8 +403,13 @@ static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = sunxi_pcm_hardware.buffer_bytes_max; + size_t size = 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + size = sunxi_pcm_out_hardware.buffer_bytes_max; + } else { + size = sunxi_pcm_in_hardware.buffer_bytes_max; + } buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; buf->private_data = NULL; @@ -384,6 +450,7 @@ static int sunxi_pcm_new(struct snd_soc_pcm_runtime *rtd) struct snd_pcm *pcm = rtd->pcm; int ret = 0; + if (!card->dev->dma_mask) card->dev->dma_mask = &sunxi_pcm_mask; if (!card->dev->coherent_dma_mask) @@ -443,16 +510,19 @@ static int __init sunxi_soc_platform_spdif_init(void) int ret, spdif_used = 0; ret = script_parser_fetch("spdif_para", "spdif_used", &spdif_used, 1); - if (ret != 0 || !spdif_used) + if (ret != 0 || !spdif_used) return -ENODEV; ret = platform_device_register(&sunxi_spdif_pcm_device); - if (ret < 0) + if (ret < 0) { + pr_err("[SPDIF] try to SPDIF platform_device_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); return ret; + } ret = platform_driver_register(&sunxi_spdif_pcm_driver); if (ret < 0) { platform_device_unregister(&sunxi_spdif_pcm_device); + pr_err("[SPDIF] try to SPDIF platform_driver_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); return ret; } return 0; diff --git a/sound/soc/sunxi/sunxi-codec.c b/sound/soc/sunxi/sunxi-codec.c index 1b7f8f1..197fa84 100644 --- a/sound/soc/sunxi/sunxi-codec.c +++ b/sound/soc/sunxi/sunxi-codec.c @@ -1155,8 +1155,8 @@ static int snd_sunxi_codec_prepare(struct snd_pcm_substream *substream) &codec_play_dma_conf, 0); /* flush the DMA channel */ play_prtd->dma_loaded = 0; - if (sunxi_dma_flush(play_prtd->params) == 0) - play_prtd->dma_pos = play_prtd->dma_start; + sunxi_dma_flush(play_prtd->params); + play_prtd->dma_pos = play_prtd->dma_start; /* enqueue dma buffers */ sunxi_pcm_enqueue(substream); return play_ret; @@ -1196,8 +1196,8 @@ static int snd_sunxi_codec_prepare(struct snd_pcm_substream *substream) &codec_capture_dma_conf, 0); /* flush the DMA channel */ capture_prtd->dma_loaded = 0; - if (sunxi_dma_flush(capture_prtd->params) == 0) - capture_prtd->dma_pos = capture_prtd->dma_start; + sunxi_dma_flush(capture_prtd->params); + capture_prtd->dma_pos = capture_prtd->dma_start; /* enqueue dma buffers */ sunxi_pcm_enqueue(substream);