[alsa-devel] [PATCH v2 1/2] ASoC: Add driver for CLPS711X DAI interface
Alexander Shiyan
shc_work at mail.ru
Thu Apr 24 07:04:56 CEST 2014
On Sat, 12 Apr 2014 10:15:28 +0400
Alexander Shiyan <shc_work at mail.ru> wrote:
Ping.
> This patch adds a driver for the DAI interface found on Cirrus Logic
> CLPS711X family CPUs.
>
> Signed-off-by: Alexander Shiyan <shc_work at mail.ru>
> ---
> sound/soc/cirrus/Kconfig | 8 +
> sound/soc/cirrus/Makefile | 5 +
> sound/soc/cirrus/clps711x-dai.c | 506 ++++++++++++++++++++++++++++++++++++++++
> sound/soc/cirrus/clps711x-dai.h | 61 +++++
> sound/soc/cirrus/clps711x-fiq.S | 56 +++++
> 5 files changed, 636 insertions(+)
> create mode 100644 sound/soc/cirrus/clps711x-dai.c
> create mode 100644 sound/soc/cirrus/clps711x-dai.h
> create mode 100644 sound/soc/cirrus/clps711x-fiq.S
>
> diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig
> index 5477c54..3c8d040 100644
> --- a/sound/soc/cirrus/Kconfig
> +++ b/sound/soc/cirrus/Kconfig
> @@ -1,3 +1,11 @@
> +config SND_CLPS711X_SOC
> + tristate "SoC Audio support for the Cirrus Logic CLPS711X CPUs"
> + depends on ARCH_CLPS711X || (ARM && COMPILE_TEST)
> + select FIQ
> + help
> + Say Y or M if you want to add support for codecs attached to
> + the CLPS711X DAI interface.
> +
> config SND_EP93XX_SOC
> tristate "SoC Audio support for the Cirrus Logic EP93xx series"
> depends on ARCH_EP93XX || COMPILE_TEST
> diff --git a/sound/soc/cirrus/Makefile b/sound/soc/cirrus/Makefile
> index 5514146..f5d72fc 100644
> --- a/sound/soc/cirrus/Makefile
> +++ b/sound/soc/cirrus/Makefile
> @@ -1,3 +1,8 @@
> +# CLPS711X Platform Support
> +snd-soc-clps711x-objs := clps711x-dai.o clps711x-fiq.o
> +
> +obj-$(CONFIG_SND_CLPS711X_SOC) += snd-soc-clps711x.o
> +
> # EP93xx Platform Support
> snd-soc-ep93xx-objs := ep93xx-pcm.o
> snd-soc-ep93xx-i2s-objs := ep93xx-i2s.o
> diff --git a/sound/soc/cirrus/clps711x-dai.c b/sound/soc/cirrus/clps711x-dai.c
> new file mode 100644
> index 0000000..ed677c8
> --- /dev/null
> +++ b/sound/soc/cirrus/clps711x-dai.c
> @@ -0,0 +1,506 @@
> +/*
> + * Currus Logic CLPS711X DAI driver
> + *
> + * Copyright (C) 2014 Alexander Shiyan <shc_work at mail.ru>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#include <linux/mfd/syscon.h>
> +#include <linux/mfd/syscon/clps711x.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +
> +#include <asm/fiq.h>
> +
> +#include "clps711x-dai.h"
> +
> +#define CLPS711X_FMTS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)
> +#define CLPS711X_RATES (SNDRV_PCM_RATE_8000_48000)
> +
> +struct clps711x_dai {
> + u32 head;
> + u32 last_ptr;
> +
> + struct hrtimer hrt;
> + unsigned long reload;
> +
> + unsigned long pll_hz;
> + unsigned long ext_hz;
> +
> + void __iomem *base;
> + int irq;
> +
> + struct pt_regs regs;
> + struct fiq_handler fiq;
> + atomic_t running;
> +
> + struct snd_pcm_substream *substream;
> +};
> +
> +static int clps711x_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_LEFT_J)
> + return -EINVAL;
> +
> + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
> + return -EINVAL;
> +
> + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int clps711x_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> + unsigned int freq, int dir)
> +{
> + struct clps711x_dai *s = dev_get_drvdata(dai->dev);
> +
> + s->ext_hz = freq;
> +
> + return 0;
> +}
> +
> +static int clps711x_dai_update_best_err(unsigned int clk, unsigned int div,
> + unsigned int rate, int *besterr)
> +{
> + int err = abs(DIV_ROUND_CLOSEST(clk, div) - rate);
> +
> + if ((*besterr < 0) || (*besterr > err)) {
> + *besterr = err;
> + return 0;
> + }
> +
> + return 1;
> +}
> +
> +static int clps711x_dai_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params,
> + struct snd_soc_dai *dai)
> +{
> + struct clps711x_dai *s = dev_get_drvdata(dai->dev);
> + int i, besterr = -1;
> + u32 dai64 = 0;
> +
> + if ((params_rate(params) < 8000) || (params_rate(params) > 48000))
> + return -EINVAL;
> +
> + if (!s->pll_hz && !s->ext_hz)
> + return -EINVAL;
> +
> + /* Find best settings for desired samplerate */
> + for (i = 0x01; (i < 0x80) && besterr && s->pll_hz; i++)
> + if (!clps711x_dai_update_best_err(s->pll_hz, 128 * i,
> + params_rate(params),
> + &besterr))
> + dai64 = DAI64FS_AUDIV(i);
> + for (i = 0x01; (i < 0x80) && besterr && s->ext_hz; i++)
> + if (!clps711x_dai_update_best_err(s->ext_hz, 128 * i,
> + params_rate(params),
> + &besterr))
> + dai64 = DAI64FS_AUDIV(i) | DAI64FS_AUDIOCLKSRC;
> +
> + writel(DAI64FS_I2SF64 | DAI64FS_AUDIOCLKEN | DAI64FS_MCLK256EN | dai64,
> + s->base + DAI64FS);
> +
> + return 0;
> +}
> +
> +static const struct snd_soc_dai_ops clps711x_dai_ops = {
> + .hw_params = clps711x_dai_hw_params,
> + .set_fmt = clps711x_dai_set_fmt,
> + .set_sysclk = clps711x_dai_set_sysclk,
> +};
> +
> +static int clps711x_dai_probe(struct snd_soc_dai *dai)
> +{
> + struct clps711x_dai *s = dev_get_drvdata(dai->dev);
> +
> + snd_soc_dai_set_drvdata(dai, s);
> +
> + return 0;
> +}
> +
> +static struct snd_soc_dai_driver clps711x_dai_driver = {
> + .ops = &clps711x_dai_ops,
> + .probe = clps711x_dai_probe,
> + .playback = {
> + .stream_name = "Playback",
> + .formats = CLPS711X_FMTS,
> + .rates = CLPS711X_RATES,
> + .channels_min = 2,
> + .channels_max = 2,
> + },
> +};
> +
> +static const struct snd_soc_component_driver clps711x_i2s_component = {
> + .name = "clps711x-i2s",
> +};
> +
> +static int clps711x_pcm_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct clps711x_dai *dai = snd_soc_platform_get_drvdata(rtd->platform);
> + int ret;
> +
> + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
> + if (ret < 0)
> + return ret;
> +
> + dai->substream = substream;
> + dai->reload = (1000000000 / params_rate(params)) *
> + params_period_size(params) / 2;
> +
> + local_fiq_disable();
> + get_fiq_regs(&dai->regs);
> + dai->regs.ARM_r8 = (u32)substream->runtime->dma_area;
> + set_fiq_regs(&dai->regs);
> + local_fiq_enable();
> +
> + return 0;
> +}
> +
> +static int clps711x_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct clps711x_dai *dai = snd_soc_platform_get_drvdata(rtd->platform);
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + atomic_set(&dai->running, 1);
> + hrtimer_start(&dai->hrt, ns_to_ktime(dai->reload),
> + HRTIMER_MODE_REL);
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + atomic_set(&dai->running, 0);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static snd_pcm_uframes_t clps711x_pcm_ptr(struct snd_pcm_substream *substream)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct clps711x_dai *dai = snd_soc_platform_get_drvdata(rtd->platform);
> +
> + return bytes_to_frames(substream->runtime, dai->last_ptr);
> +}
> +
> +static int clps711x_pcm_copy(struct snd_pcm_substream *substream, int channel,
> + snd_pcm_uframes_t pos, void __user *buf,
> + snd_pcm_uframes_t count)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct clps711x_dai *dai = snd_soc_platform_get_drvdata(rtd->platform);
> + int sz = frames_to_bytes(substream->runtime, count);
> +
> + if (copy_from_user(substream->runtime->dma_area + dai->head, buf, sz))
> + return -EFAULT;
> +
> + local_fiq_disable();
> + dai->head += sz;
> + dai->head %= CLPS711X_SNDBUF_SIZE;
> + local_fiq_enable();
> +
> + return 0;
> +}
> +
> +static int clps711x_pcm_silence(struct snd_pcm_substream *substream,
> + int channel, snd_pcm_uframes_t pos,
> + snd_pcm_uframes_t count)
> +{
> + /* Silence is provided in the FIQ interrupt routine */
> + /* This empty function is necessary */
> + return 0;
> +}
> +
> +static enum hrtimer_restart clps711x_pcm_timer(struct hrtimer *hrt)
> +{
> + struct clps711x_dai *dai = container_of(hrt, struct clps711x_dai, hrt);
> + u32 delta;
> +
> + if (!atomic_read(&dai->running)) {
> + dai->head = 0;
> + dai->last_ptr = 0;
> +
> + local_fiq_disable();
> + get_fiq_regs(&dai->regs);
> + dai->regs.ARM_r10 = 0;
> + set_fiq_regs(&dai->regs);
> + local_fiq_enable();
> +
> + return HRTIMER_NORESTART;
> + }
> +
> + get_fiq_regs(&dai->regs);
> +
> + delta = CLPS711X_SNDBUF_SIZE + dai->regs.ARM_r10 - dai->last_ptr;
> + delta %= CLPS711X_SNDBUF_SIZE;
> + if ((delta >= dai->substream->runtime->period_size) ||
> + (dai->regs.ARM_r10 == dai->head)) {
> + dai->last_ptr = dai->regs.ARM_r10;
> + snd_pcm_period_elapsed(dai->substream);
> + }
> +
> + hrtimer_forward_now(hrt, ns_to_ktime(dai->reload));
> +
> + return HRTIMER_RESTART;
> +}
> +
> +static const struct snd_pcm_hardware clps711x_pcm_hardware = {
> + .info = SNDRV_PCM_INFO_INTERLEAVED |
> + SNDRV_PCM_INFO_BLOCK_TRANSFER,
> + .formats = CLPS711X_FMTS,
> + .buffer_bytes_max = CLPS711X_SNDBUF_SIZE,
> + .period_bytes_min = 32,
> + .period_bytes_max = CLPS711X_SNDBUF_SIZE / 8,
> + .periods_min = 4,
> + .periods_max = CLPS711X_SNDBUF_SIZE / 64 - 1,
> +};
> +
> +static int clps711x_pcm_open(struct snd_pcm_substream *substream)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct clps711x_dai *dai = snd_soc_platform_get_drvdata(rtd->platform);
> + int ret;
> +
> + atomic_set(&dai->running, 0);
> + hrtimer_init(&dai->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> + dai->hrt.function = clps711x_pcm_timer;
> +
> + dai->head = 0;
> + dai->last_ptr = 0;
> +
> + get_fiq_regs(&dai->regs);
> + dai->regs.ARM_r9 = (u32)&dai->head;
> + dai->regs.ARM_r10 = 0;
> + dai->regs.ARM_fp = (u32)dai->base;
> + set_fiq_regs(&dai->regs);
> +
> + enable_irq(dai->irq);
> +
> + ret = snd_pcm_hw_constraint_integer(substream->runtime,
> + SNDRV_PCM_HW_PARAM_PERIODS);
> + if (ret < 0)
> + return ret;
> +
> + return snd_soc_set_runtime_hwparams(substream, &clps711x_pcm_hardware);
> +}
> +
> +static int clps711x_pcm_close(struct snd_pcm_substream *substream)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct clps711x_dai *dai = snd_soc_platform_get_drvdata(rtd->platform);
> +
> + disable_irq(dai->irq);
> + hrtimer_cancel(&dai->hrt);
> +
> + return 0;
> +}
> +
> +static const struct snd_pcm_ops clps711x_pcm_ops = {
> + .open = clps711x_pcm_open,
> + .close = clps711x_pcm_close,
> + .ioctl = snd_pcm_lib_ioctl,
> + .hw_params = clps711x_pcm_hw_params,
> + .hw_free = snd_pcm_lib_free_pages,
> + .trigger = clps711x_pcm_trigger,
> + .pointer = clps711x_pcm_ptr,
> + .copy = clps711x_pcm_copy,
> + .silence = clps711x_pcm_silence,
> +};
> +
> +static int clps711x_pcm_new(struct snd_soc_pcm_runtime *rtd)
> +{
> + struct clps711x_dai *dai = snd_soc_platform_get_drvdata(rtd->platform);
> + const char *devname = dev_name(rtd->platform->dev);
> + int ret;
> +
> + /* Request FIQ */
> + dai->fiq.name = devname;
> + ret = claim_fiq(&dai->fiq);
> + if (ret)
> + return ret;
> +
> + /* Request FIQ interrupt */
> + ret = request_irq(dai->irq, no_action, 0, devname, NULL);
> + if (ret)
> + return ret;
> +
> + /* Install FIQ handler */
> + set_fiq_handler(&daifiq_start, &daifiq_end - &daifiq_start);
> +
> + return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
> + SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL),
> + CLPS711X_SNDBUF_SIZE, CLPS711X_SNDBUF_SIZE);
> +}
> +
> +static void clps711x_pcm_free(struct snd_pcm *pcm)
> +{
> + struct snd_soc_pcm_runtime *rtd = pcm->private_data;
> + struct clps711x_dai *dai = snd_soc_platform_get_drvdata(rtd->platform);
> +
> + free_irq(dai->irq, NULL);
> +
> + release_fiq(&dai->fiq);
> +
> + snd_pcm_lib_preallocate_free_for_all(pcm);
> +}
> +
> +static struct snd_soc_platform_driver clps711x_soc_platform_drv = {
> + .ops = &clps711x_pcm_ops,
> + .pcm_new = clps711x_pcm_new,
> + .pcm_free = clps711x_pcm_free,
> +};
> +
> +static int clps711x_enable_fifo(void __iomem *base, u32 channel)
> +{
> + unsigned long timeout;
> +
> + writel(DAIDR2_FIFOEN | channel, base + DAIDR2);
> + timeout = jiffies + msecs_to_jiffies(5);
> + while (!(readl(base + DAISR) & DAISR_FIFO))
> + if (time_is_before_jiffies(timeout))
> + return -ETIMEDOUT;
> +
> + return 0;
> +}
> +
> +static int clps711x_dai_platform_probe(struct platform_device *pdev)
> +{
> + u32 dai64 = DAI64FS_AUDIOCLKEN | DAI64FS_MCLK256EN | DAI64FS_I2SF64;
> + struct device *dev = &pdev->dev;
> + struct clps711x_dai *dai;
> + struct regmap *syscon;
> + struct resource *res;
> + struct clk *clk;
> + int ret;
> +
> + /* Check for proper buffer size */
> + BUG_ON(!is_power_of_2(CLPS711X_SNDBUF_SIZE));
> +
> + dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL);
> + if (!dai)
> + return -ENOMEM;
> +
> + dai->irq = platform_get_irq(pdev, 0);
> + if (dai->irq < 0)
> + return dai->irq;
> +
> + syscon = syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon3");
> + if (IS_ERR(syscon))
> + return PTR_ERR(syscon);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + dai->base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(dai->base))
> + return PTR_ERR(dai->base);
> +
> + clk = devm_clk_get(dev, "pll");
> + if (IS_ERR(clk))
> + return PTR_ERR(clk);
> +
> + dai->pll_hz = clk_get_rate(clk) / 2;
> + if (!dai->pll_hz) {
> + dai64 |= DAI64FS_AUDIOCLKSRC;
> + dev_notice(dev, "External MCLK will be used only\n");
> + }
> +
> + clk = devm_clk_get(dev, "mclk");
> + if (IS_ERR(clk)) {
> + if (PTR_ERR(clk) == -EPROBE_DEFER)
> + return -EPROBE_DEFER;
> + } else
> + dai->ext_hz = clk_get_rate(clk);
> +
> + platform_set_drvdata(pdev, dai);
> +
> + ret = devm_snd_soc_register_component(dev, &clps711x_i2s_component,
> + &clps711x_dai_driver, 1);
> + if (ret)
> + return ret;
> +
> + /* Enable DAI interface */
> + regmap_update_bits(syscon, SYSCON_OFFSET,
> + SYSCON3_DAISEL | SYSCON3_128FS, SYSCON3_DAISEL);
> +
> + /* Clear interrupt flags */
> + writel(~0, dai->base + DAISR);
> +
> + /* Enable DAI */
> + writel(DAIR_RESERVED | DAIR_DAIEN | DAIR_ECS | DAIR_LCTM | DAIR_RCTM,
> + dai->base + DAIR);
> +
> + /* Set initial value to DAI register */
> + writel(dai64 | DAI64FS_AUDIV(10), dai->base + DAI64FS);
> +
> + /* Enable FIFOs */
> + ret = clps711x_enable_fifo(dai->base, DAIDR2_FIFOLEFT);
> + if (ret)
> + goto out_err;
> + ret = clps711x_enable_fifo(dai->base, DAIDR2_FIFORIGHT);
> + if (ret)
> + goto out_err;
> +
> + ret = snd_soc_register_platform(dev, &clps711x_soc_platform_drv);
> + if (!ret)
> + return 0;
> +
> +out_err:
> + /* Disable DAI */
> + writel(DAIR_RESERVED, dai->base + DAIR);
> +
> + return ret;
> +}
> +
> +static int clps711x_dai_platform_remove(struct platform_device *pdev)
> +{
> + struct clps711x_dai *dai = platform_get_drvdata(pdev);
> +
> + /* Disable DAI */
> + writel(DAIR_RESERVED, dai->base + DAIR);
> +
> + snd_soc_unregister_platform(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id clps711x_dai_dt_ids[] = {
> + { .compatible = "cirrus,clps711x-dai", },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, clps711x_dai_dt_ids);
> +
> +static struct platform_driver clps711x_dai_platform_driver = {
> + .driver = {
> + .name = "clps711x-dai",
> + .owner = THIS_MODULE,
> + .of_match_table = clps711x_dai_dt_ids,
> + },
> + .probe = clps711x_dai_platform_probe,
> + .remove = clps711x_dai_platform_remove,
> +};
> +module_platform_driver(clps711x_dai_platform_driver);
> +
> +MODULE_AUTHOR("Alexander Shiyan <shc_work at mail.ru>");
> +MODULE_DESCRIPTION("CLPS711X DAI driver");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/cirrus/clps711x-dai.h b/sound/soc/cirrus/clps711x-dai.h
> new file mode 100644
> index 0000000..c79d26a
> --- /dev/null
> +++ b/sound/soc/cirrus/clps711x-dai.h
> @@ -0,0 +1,61 @@
> +/*
> + * Currus Logic CLPS711X DAI definitions
> + *
> + * Copyright (C) 2014 Alexander Shiyan <shc_work at mail.ru>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef __CLPS711X_DAI_H
> +#define __CLPS711X_DAI_H
> +
> +#include <linux/sizes.h>
> +
> +#define CLPS711X_SNDBUF_SIZE SZ_128K
> +
> +#define DAIR (0x0000)
> +# define DAIR_RESERVED (0x0404)
> +# define DAIR_DAIEN (1 << 16)
> +# define DAIR_ECS (1 << 17)
> +# define DAIR_LCTM (1 << 19)
> +# define DAIR_LCRM (1 << 20)
> +# define DAIR_RCTM (1 << 21)
> +# define DAIR_RCRM (1 << 22)
> +# define DAIR_LBM (1 << 23)
> +#define DAIDR0 (0x0040)
> +#define DAIDR1 (0x0080)
> +#define DAIDR2 (0x00c0)
> +# define DAIDR2_FIFOEN (1 << 15)
> +# define DAIDR2_FIFOLEFT (0x0d << 16)
> +# define DAIDR2_FIFORIGHT (0x11 << 16)
> +#define DAISR (0x0100)
> +# define DAISR_RCTS (1 << 0)
> +# define DAISR_RCRS (1 << 1)
> +# define DAISR_LCTS (1 << 2)
> +# define DAISR_LCRS (1 << 3)
> +# define DAISR_RCTU (1 << 4)
> +# define DAISR_RCRO (1 << 5)
> +# define DAISR_LCTU (1 << 6)
> +# define DAISR_LCRO (1 << 7)
> +# define DAISR_RCNF (1 << 8)
> +# define DAISR_RCNE (1 << 9)
> +# define DAISR_LCNF (1 << 10)
> +# define DAISR_LCNE (1 << 11)
> +# define DAISR_FIFO (1 << 12)
> +#define DAI64FS (0x0600)
> +# define DAI64FS_I2SF64 (1 << 0)
> +# define DAI64FS_AUDIOCLKEN (1 << 1)
> +# define DAI64FS_AUDIOCLKSRC (1 << 2)
> +# define DAI64FS_MCLK256EN (1 << 3)
> +# define DAI64FS_LOOPBACK (1 << 5)
> +# define DAI64FS_AUDIV_MASK (0x7f00)
> +# define DAI64FS_AUDIV(x) (((x) << 8) & DAI64FS_AUDIV_MASK)
> +
> +#ifndef __ASSEMBLY__
> +extern unsigned char daifiq_start, daifiq_end;
> +#endif
> +
> +#endif
> diff --git a/sound/soc/cirrus/clps711x-fiq.S b/sound/soc/cirrus/clps711x-fiq.S
> new file mode 100644
> index 0000000..aa2dfb9
> --- /dev/null
> +++ b/sound/soc/cirrus/clps711x-fiq.S
> @@ -0,0 +1,56 @@
> +/*
> + * Currus Logic CLPS711X DAI FIQ Handler
> + *
> + * Copyright (C) 2014 Alexander Shiyan <shc_work at mail.ru>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <asm/assembler.h>
> +#include <linux/linkage.h>
> +
> +#include "clps711x-dai.h"
> +
> + .text
> + .arm
> +
> + .global daifiq_end
> +
> +ENTRY(daifiq_start)
> + @ r8 - Buffer address
> + @ r9 - Head Pointer
> + @ r10 - Tail Pointer
> + @ r11 - Registers base virtual address
> + # r12 - Temp
> +
> +daifiq_loop:
> + ldr r12, [r9] @ Get Head
> + subs r12, r12, r10 @ Compare Head & Tail
> + beq daifiq_play @ Send zeroes if Head = Tail
> +
> + ldr r12, [r8, r10] @ Get buffer value
> + add r10, r10, #4 @ Increment pointer
> + bic r10, r10, #CLPS711X_SNDBUF_SIZE @ Mask with buffer size
> +
> +daifiq_play:
> + @ Put data to FIFOs
> + strh r12, [r11, #DAIDR1] @ Left channel
> + mov r12, r12, lsr #16
> + strh r12, [r11, #DAIDR0] @ Right channel
> +
> + @ Check DAI Flags (FIFOs not full)
> + ldr r12, [r11, #DAISR]
> + and r12, r12, #DAISR_RCNF | DAISR_LCNF
> + cmp r12, #DAISR_RCNF | DAISR_LCNF
> + beq daifiq_loop
> +
> + @ Clear DAI Interrupt Flags
> + mvn r12, #0
> + str r12, [r11, #DAISR]
> +
> + @ Return from FIQ
> + subs pc, lr, #4
> +daifiq_end:
> --
> 1.8.3.2
>
>
--
Alexander Shiyan <shc_work at mail.ru>
More information about the Alsa-devel
mailing list