[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