[alsa-devel] [PATCH v2 1/2] ASoC: Add driver for CLPS711X DAI interface
This patch adds a driver for the DAI interface found on Cirrus Logic CLPS711X family CPUs.
Signed-off-by: Alexander Shiyan shc_work@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@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@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@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@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:
On Sat, 12 Apr 2014 10:15:28 +0400 Alexander Shiyan shc_work@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@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@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@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@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@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
On Thu, Apr 24, 2014 at 09:04:56AM +0400, Alexander Shiyan wrote:
On Sat, 12 Apr 2014 10:15:28 +0400 Alexander Shiyan shc_work@mail.ru wrote:
Ping.
Don't top post and don't send contentless pings please, it's just more mail to read and dealing with the ping makes me think I did something with the patch.
On Sat, Apr 12, 2014 at 10:15:28AM +0400, Alexander Shiyan wrote:
+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;
+}
This machinery seems pretty much identical to the FIQ code for i.MX - I know you don't think anything can be shared but can you please be more explicit about why? The contents of the FIQ are different but the start and stop mechanics don't look so different (and given that the general idea of what they do is very similar this isn't surprising to me).
+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;
+}
Why is it required? It seems like what actually happens here is that the FIQ handles underflow by playing zero bytes but that's not quite the same thing as what silence does - we can still underflow after sending silence, this is about sending an explicit amount of silence.
participants (2)
-
Alexander Shiyan
-
Mark Brown