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/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 "); 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 <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,16 +227,54 @@ 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); } @@ -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 50418a58..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 7c1a3d4b..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; //0. Valid data at the MSB 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..038903d 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,10 +253,27 @@ 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; @@ -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;