[alsa-devel] [PATCH V3 2/5] sound: asoc: Adding support for SPEAr13XX ASoC platform driver

Lu Guanqun guanqun.lu at intel.com
Mon Apr 11 15:00:30 CEST 2011


On Mon, Apr 11, 2011 at 01:30:01PM +0800, Rajeev Kumar wrote:
> This patch adds the support for the SPEAr13XX Platform Driver (I2S based)
> as per the ASoC framework.
> 
> SPEAr13XX internally uses a Designware I2S IP and some glue logic on top
> of the same.
> 
> Signed-off-by: Rajeev Kumar <rajeev-dlh.kumar at st.com>
> ---
>  sound/soc/spear/spear13xx-i2s.c |  523 +++++++++++++++++++++++++++++++++++++++
>  sound/soc/spear/spear13xx-i2s.h |   19 ++
>  sound/soc/spear/spear13xx-pcm.c |  500 +++++++++++++++++++++++++++++++++++++
>  sound/soc/spear/spear13xx-pcm.h |   50 ++++
>  4 files changed, 1092 insertions(+), 0 deletions(-)
>  create mode 100644 sound/soc/spear/spear13xx-i2s.c
>  create mode 100644 sound/soc/spear/spear13xx-i2s.h
>  create mode 100644 sound/soc/spear/spear13xx-pcm.c
>  create mode 100644 sound/soc/spear/spear13xx-pcm.h
> 
> diff --git a/sound/soc/spear/spear13xx-i2s.c b/sound/soc/spear/spear13xx-i2s.c
> new file mode 100644
> index 0000000..33d2d11
> --- /dev/null
> +++ b/sound/soc/spear/spear13xx-i2s.c
> @@ -0,0 +1,523 @@
> +/*
> + * ALSA SoC I2S Audio Layer for ST spear13xx processor
> + *
> + * sound/soc/spear/spear13xx-i2s.c
> + *
> + * Copyright (C) 2011 ST Microelectronics
> + * Rajeev Kumar <rajeev-dlh.kumar at st.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <mach/misc_regs.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/soc.h>
> +#include "spear13xx-pcm.h"
> +#include "spear13xx-i2s.h"
> +
> +/* common register for all channel */
> +#define IER            0x000
> +#define IRER           0x004
> +#define ITER           0x008
> +#define CER            0x00C
> +#define CCR            0x010
> +#define RXFFR          0x014
> +#define TXFFR          0x018
> +
> +/* I2STxRxRegisters for channel 0 */
> +#define LRBR0_LTHR0    0x020
> +#define RRBR0_RTHR0    0x024
> +#define RER0           0x028
> +#define TER0           0x02C
> +#define RCR0           0x030
> +#define TCR0           0x034
> +#define ISR0           0x038
> +#define IMR0           0x03C
> +#define ROR0           0x040
> +#define TOR0           0x044
> +#define RFCR0          0x048
> +#define TFCR0          0x04C
> +#define RFF0           0x050
> +#define TFF0           0x054
> +
> +/* I2STxRxRegisters for channel 1 */
> +#define LRBR1_LTHR1    0x060
> +#define RRBR1_RTHR1    0x064
> +#define RER1           0x068
> +#define TER1           0x06C
> +#define RCR1           0x070
> +#define TCR1           0x074
> +#define ISR1           0x078
> +#define IMR1           0x07C
> +#define ROR1           0x080
> +#define TOR1           0x084
> +#define RFCR1          0x088
> +#define TFCR1          0x08C
> +#define RFF1           0x090
> +#define TFF1           0x094
> +
> +/* I2STxRxRegisters for channel 2 */
> +#define LRBR2_LTHR2    0x0A0
> +#define RRBR2_RTHR2    0x0A4
> +#define RER2           0x0A8
> +#define TER2           0x0AC
> +#define RCR2           0x0B0
> +#define TCR2           0x0B4
> +#define ISR2           0x0B8
> +#define IMR2           0x0BC
> +#define ROR2           0x0C0
> +#define TOR2           0x0C4
> +#define RFCR2          0x0C8
> +#define TFCR2          0x0CC
> +#define RFF2           0x0D0
> +#define TFF2           0x0D4
> +
> +/* I2STxRxRegisters for channel 3*/
> +#define LRBR3_LTHR3    0x0E0
> +#define RRBR3_RTHR3    0x0E4
> +#define RER3           0x0E8
> +#define TER3           0x0EC
> +#define RCR3           0x0F0
> +#define TCR3           0x0F4
> +#define ISR3           0x0F8
> +#define IMR3           0x0FC
> +#define ROR3           0x100
> +#define TOR3           0x104
> +#define RFCR3          0x108
> +#define TFCR3          0x10C
> +#define RFF3           0x110
> +#define TFF3           0x114
> +
> +/* I2SDMARegisters */
> +#define RXDMA          0x01C0
> +#define RRXDMA         0x01C4
> +#define TXDMA          0x01C8
> +#define RTXDMA         0x01CC
> +
> +/* I2SCOMPRegisters */
> +#define I2S_COMP_PARAM_2       0x01F0
> +#define I2S_COMP_PARAM_1       0x01F4
> +#define I2S_COMP_VERSION       0x01F8
> +#define I2S_COMP_TYPE          0x01FC
> +
> +#define SPEAR13XX_I2S_RATES    SNDRV_PCM_RATE_8000_96000
> +#define SPEAR13XX_I2S_FORMAT   SNDRV_PCM_FMTBIT_S16_LE
> +#define MAX_CHANNEL_NUM                2
> +#define MIN_CHANNEL_NUM                2
> +
> +struct spear13xx_i2s_dev {
> +       void __iomem *i2s_base;
> +       struct resource *res;
> +       struct clk *clk;
> +       int play_irq;
> +       int mode;
> +       int active;
> +       int capture_irq;
> +       struct device *dev;
> +       struct spear13xx_pcm_dma_params *dma_params[2];
> +};
> +
> +void get_dma_start_addr(struct snd_pcm_substream *substream)
> +{
> +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +       struct spear13xx_runtime_data *prtd = substream->runtime->private_data;
> +       struct spear13xx_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +
> +       prtd->txdma = dev->res->start + TXDMA;
> +       prtd->rxdma = dev->res->start + RXDMA;
> +
> +       substream->runtime->private_data = prtd;
> +}
> +
> +static inline void i2s_write_reg(void *io_base, int reg, u32 val)
> +{
> +       writel(val, io_base + reg);
> +}
> +
> +static inline u32 i2s_read_reg(void *io_base, int reg)
> +{
> +       return readl(io_base + reg);
> +}
> +
> +static void i2s_start_play(struct spear13xx_i2s_dev *dev,
> +               struct snd_pcm_substream *substream)
> +{
> +       u32 val; /*dma mode slection*/
> +
> +       val = readl(PERIP_CFG);
> +       val &= ~0xFFFFFFFC;
> +       i2s_write_reg(dev->i2s_base, TER0, 0);
> +       i2s_write_reg(dev->i2s_base, TER1, 0);
> +
> +       /* for 2.0 audio*/
> +       if (dev->mode <= 2) {
> +               if (!val) {
> +                       i2s_write_reg(dev->i2s_base, TCR0, 0x2);
> +                       i2s_write_reg(dev->i2s_base, TFCR0, 0x07);
> +                       i2s_write_reg(dev->i2s_base, IMR0, 0x00);
> +                       i2s_write_reg(dev->i2s_base, TER0, 1);
> +               } else {
> +                       i2s_write_reg(dev->i2s_base, TCR1, 0x2);
> +                       i2s_write_reg(dev->i2s_base, TFCR1, 0x07);
> +                       i2s_write_reg(dev->i2s_base, IMR1, 0x00);
> +                       i2s_write_reg(dev->i2s_base, TER1, 1);
> +               }
> +       } else { /*audio 2.0 onwards */
> +               i2s_write_reg(dev->i2s_base, TCR0, 0x5);
> +               i2s_write_reg(dev->i2s_base, TCR1, 0x5);
> +
> +               i2s_write_reg(dev->i2s_base, TFCR0, 0x07);
> +               i2s_write_reg(dev->i2s_base, TFCR1, 0x07);
> +               i2s_write_reg(dev->i2s_base, IMR0, 0x00);
> +               i2s_write_reg(dev->i2s_base, IMR1, 0x00);
> +               i2s_write_reg(dev->i2s_base, TER0, 1);
> +               i2s_write_reg(dev->i2s_base, TER1, 1);
> +       }
> +
> +       i2s_write_reg(dev->i2s_base, ITER, 1);
> +}
> +
> +static void i2s_start_rec(struct spear13xx_i2s_dev *dev,
> +               struct snd_pcm_substream *substream)
> +{
> +       u32 val; /*dma mode slection*/
> +
> +       val = readl(PERIP_CFG);
> +       val &= ~0xFFFFFFFC;
> +       i2s_write_reg(dev->i2s_base, RER0, 0);
> +       i2s_write_reg(dev->i2s_base, RER1, 0);
> +
> +       /* for 2.0 audio*/
> +       if (dev->mode <= 2) {
> +               if (!val) {
> +                       i2s_write_reg(dev->i2s_base, RCR0, 0x2);
> +                       i2s_write_reg(dev->i2s_base, RFCR0, 0x07);
> +                       i2s_write_reg(dev->i2s_base, IMR0, 0x00);
> +                       i2s_write_reg(dev->i2s_base, RER0, 1);
> +               } else {
> +                       i2s_write_reg(dev->i2s_base, RCR1, 0x2);
> +                       i2s_write_reg(dev->i2s_base, RFCR1, 0x07);
> +                       i2s_write_reg(dev->i2s_base, IMR1, 0x00);
> +                       i2s_write_reg(dev->i2s_base, TER1, 1);
> +               }
> +       } else { /*audio 2.0 onwards */
> +               i2s_write_reg(dev->i2s_base, RCR0, 0x5);
> +               i2s_write_reg(dev->i2s_base, RCR1, 0x5);
> +
> +               i2s_write_reg(dev->i2s_base, RFCR0, 0x07);
> +               i2s_write_reg(dev->i2s_base, RFCR1, 0x07);
> +               i2s_write_reg(dev->i2s_base, IMR0, 0x00);
> +               i2s_write_reg(dev->i2s_base, IMR1, 0x00);
> +               i2s_write_reg(dev->i2s_base, RER0, 1);
> +               i2s_write_reg(dev->i2s_base, RER1, 1);
> +       }
> +
> +       i2s_write_reg(dev->i2s_base, IRER, 1);
> +}
> +
> +static void i2s_stop(struct spear13xx_i2s_dev *dev,
> +               struct snd_pcm_substream *substream)
> +{
> +       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +               i2s_write_reg(dev->i2s_base, ITER, 0);
> +               i2s_write_reg(dev->i2s_base, ITER, 1);
> +               i2s_write_reg(dev->i2s_base, IMR0, 0x30);
> +               i2s_write_reg(dev->i2s_base, IMR1, 0x30);
> +       } else {
> +               i2s_write_reg(dev->i2s_base, IRER, 0);
> +               i2s_write_reg(dev->i2s_base, IRER, 1);
> +               i2s_write_reg(dev->i2s_base, IMR0, 0x03);
> +               i2s_write_reg(dev->i2s_base, IMR1, 0x03);
> +       }
> +       if (!dev->active--) {
> +               i2s_write_reg(dev->i2s_base, CER, 0);
> +               i2s_write_reg(dev->i2s_base, IER, 0);
> +               dev->active = 0;
> +       }
> +
> +}
> +
> +static irqreturn_t i2s_play_irq(int irq, void *_dev)
> +{
> +       struct spear13xx_i2s_dev *dev = (struct spear13xx_i2s_dev *)_dev;
> +       u32 ch0, ch1;
> +
> +       /* check for the tx data overrun condition */
> +       ch0 = i2s_read_reg(dev->i2s_base, ISR0) & 0x20;
> +       ch1 = i2s_read_reg(dev->i2s_base, ISR1) & 0x20;
> +       if (ch0 || ch1) {
> +
> +               /* disable tx block */
> +               i2s_write_reg(dev->i2s_base, ITER, 0);
> +
> +               /* flush all the tx fifo */
> +               i2s_write_reg(dev->i2s_base, TXFFR, 1);
> +
> +               /* clear tx data overrun interrupt: channel 0 */
> +               i2s_read_reg(dev->i2s_base, TOR0);
> +
> +               /* clear tx data overrun interrupt: channel 1 */
> +               i2s_read_reg(dev->i2s_base, TOR1);
> +
> +       }
> +
> +       return IRQ_HANDLED;
> +}

I remember Mark had some comments on adding some logs when it's overrun,
how do you think?

-- 
guanqun


More information about the Alsa-devel mailing list