From c087a94bea49acf34d651f7308506fe462a937b3 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 25 Oct 2024 16:05:07 +0100 Subject: ASoC: Rename "sh" to "renesas" Rename the "sh" folder to "renesas" to better reflect the Renesas-specific drivers. Signed-off-by: Lad Prabhakar Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20241025150511.722040-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/Kconfig | 2 +- sound/soc/Makefile | 2 +- sound/soc/renesas/Kconfig | 76 ++ sound/soc/renesas/Makefile | 28 + sound/soc/renesas/dma-sh7760.c | 334 ++++++ sound/soc/renesas/fsi.c | 2119 ++++++++++++++++++++++++++++++++++++++ sound/soc/renesas/hac.c | 344 +++++++ sound/soc/renesas/migor.c | 205 ++++ sound/soc/renesas/rcar/Makefile | 3 + sound/soc/renesas/rcar/adg.c | 775 ++++++++++++++ sound/soc/renesas/rcar/cmd.c | 191 ++++ sound/soc/renesas/rcar/core.c | 2112 +++++++++++++++++++++++++++++++++++++ sound/soc/renesas/rcar/ctu.c | 389 +++++++ sound/soc/renesas/rcar/debugfs.c | 96 ++ sound/soc/renesas/rcar/dma.c | 885 ++++++++++++++++ sound/soc/renesas/rcar/dvc.c | 392 +++++++ sound/soc/renesas/rcar/gen.c | 495 +++++++++ sound/soc/renesas/rcar/mix.c | 356 +++++++ sound/soc/renesas/rcar/rsnd.h | 896 ++++++++++++++++ sound/soc/renesas/rcar/src.c | 732 +++++++++++++ sound/soc/renesas/rcar/ssi.c | 1260 +++++++++++++++++++++++ sound/soc/renesas/rcar/ssiu.c | 609 +++++++++++ sound/soc/renesas/rz-ssi.c | 1212 ++++++++++++++++++++++ sound/soc/renesas/sh7760-ac97.c | 72 ++ sound/soc/renesas/siu.h | 180 ++++ sound/soc/renesas/siu_dai.c | 800 ++++++++++++++ sound/soc/renesas/siu_pcm.c | 553 ++++++++++ sound/soc/renesas/ssi.c | 403 ++++++++ sound/soc/sh/Kconfig | 76 -- sound/soc/sh/Makefile | 28 - sound/soc/sh/dma-sh7760.c | 334 ------ sound/soc/sh/fsi.c | 2119 -------------------------------------- sound/soc/sh/hac.c | 344 ------- sound/soc/sh/migor.c | 205 ---- sound/soc/sh/rcar/Makefile | 3 - sound/soc/sh/rcar/adg.c | 775 -------------- sound/soc/sh/rcar/cmd.c | 191 ---- sound/soc/sh/rcar/core.c | 2112 ------------------------------------- sound/soc/sh/rcar/ctu.c | 389 ------- sound/soc/sh/rcar/debugfs.c | 96 -- sound/soc/sh/rcar/dma.c | 885 ---------------- sound/soc/sh/rcar/dvc.c | 392 ------- sound/soc/sh/rcar/gen.c | 495 --------- sound/soc/sh/rcar/mix.c | 356 ------- sound/soc/sh/rcar/rsnd.h | 896 ---------------- sound/soc/sh/rcar/src.c | 732 ------------- sound/soc/sh/rcar/ssi.c | 1260 ----------------------- sound/soc/sh/rcar/ssiu.c | 609 ----------- sound/soc/sh/rz-ssi.c | 1212 ---------------------- sound/soc/sh/sh7760-ac97.c | 72 -- sound/soc/sh/siu.h | 180 ---- sound/soc/sh/siu_dai.c | 800 -------------- sound/soc/sh/siu_pcm.c | 553 ---------- sound/soc/sh/ssi.c | 403 -------- 54 files changed, 15519 insertions(+), 15519 deletions(-) create mode 100644 sound/soc/renesas/Kconfig create mode 100644 sound/soc/renesas/Makefile create mode 100644 sound/soc/renesas/dma-sh7760.c create mode 100644 sound/soc/renesas/fsi.c create mode 100644 sound/soc/renesas/hac.c create mode 100644 sound/soc/renesas/migor.c create mode 100644 sound/soc/renesas/rcar/Makefile create mode 100644 sound/soc/renesas/rcar/adg.c create mode 100644 sound/soc/renesas/rcar/cmd.c create mode 100644 sound/soc/renesas/rcar/core.c create mode 100644 sound/soc/renesas/rcar/ctu.c create mode 100644 sound/soc/renesas/rcar/debugfs.c create mode 100644 sound/soc/renesas/rcar/dma.c create mode 100644 sound/soc/renesas/rcar/dvc.c create mode 100644 sound/soc/renesas/rcar/gen.c create mode 100644 sound/soc/renesas/rcar/mix.c create mode 100644 sound/soc/renesas/rcar/rsnd.h create mode 100644 sound/soc/renesas/rcar/src.c create mode 100644 sound/soc/renesas/rcar/ssi.c create mode 100644 sound/soc/renesas/rcar/ssiu.c create mode 100644 sound/soc/renesas/rz-ssi.c create mode 100644 sound/soc/renesas/sh7760-ac97.c create mode 100644 sound/soc/renesas/siu.h create mode 100644 sound/soc/renesas/siu_dai.c create mode 100644 sound/soc/renesas/siu_pcm.c create mode 100644 sound/soc/renesas/ssi.c delete mode 100644 sound/soc/sh/Kconfig delete mode 100644 sound/soc/sh/Makefile delete mode 100644 sound/soc/sh/dma-sh7760.c delete mode 100644 sound/soc/sh/fsi.c delete mode 100644 sound/soc/sh/hac.c delete mode 100644 sound/soc/sh/migor.c delete mode 100644 sound/soc/sh/rcar/Makefile delete mode 100644 sound/soc/sh/rcar/adg.c delete mode 100644 sound/soc/sh/rcar/cmd.c delete mode 100644 sound/soc/sh/rcar/core.c delete mode 100644 sound/soc/sh/rcar/ctu.c delete mode 100644 sound/soc/sh/rcar/debugfs.c delete mode 100644 sound/soc/sh/rcar/dma.c delete mode 100644 sound/soc/sh/rcar/dvc.c delete mode 100644 sound/soc/sh/rcar/gen.c delete mode 100644 sound/soc/sh/rcar/mix.c delete mode 100644 sound/soc/sh/rcar/rsnd.h delete mode 100644 sound/soc/sh/rcar/src.c delete mode 100644 sound/soc/sh/rcar/ssi.c delete mode 100644 sound/soc/sh/rcar/ssiu.c delete mode 100644 sound/soc/sh/rz-ssi.c delete mode 100644 sound/soc/sh/sh7760-ac97.c delete mode 100644 sound/soc/sh/siu.h delete mode 100644 sound/soc/sh/siu_dai.c delete mode 100644 sound/soc/sh/siu_pcm.c delete mode 100644 sound/soc/sh/ssi.c diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 8e01b421fe8d..5efba76abb31 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -106,10 +106,10 @@ source "sound/soc/meson/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/qcom/Kconfig" +source "sound/soc/renesas/Kconfig" source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/sdca/Kconfig" -source "sound/soc/sh/Kconfig" source "sound/soc/sof/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sprd/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 5307b0b62a93..08baaa11d813 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -59,10 +59,10 @@ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += kirkwood/ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += qcom/ +obj-$(CONFIG_SND_SOC) += renesas/ obj-$(CONFIG_SND_SOC) += rockchip/ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sdca/ -obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sof/ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += sprd/ diff --git a/sound/soc/renesas/Kconfig b/sound/soc/renesas/Kconfig new file mode 100644 index 000000000000..426632996a0a --- /dev/null +++ b/sound/soc/renesas/Kconfig @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: GPL-2.0 +menu "SoC Audio support for Renesas SoCs" + depends on SUPERH || ARCH_RENESAS || COMPILE_TEST + +config SND_SOC_PCM_SH7760 + tristate "SoC Audio support for Renesas SH7760" + depends on CPU_SUBTYPE_SH7760 && SH_DMABRG + help + Enable this option for SH7760 AC97/I2S audio support. + + +## +## Audio unit modules +## + +config SND_SOC_SH4_HAC + tristate + select AC97_BUS + select SND_SOC_AC97_BUS + +config SND_SOC_SH4_SSI + tristate + +config SND_SOC_SH4_FSI + tristate "SH4 FSI support" + depends on SUPERH || COMMON_CLK + select SND_SIMPLE_CARD + help + This option enables FSI sound support + +config SND_SOC_SH4_SIU + tristate + depends on ARCH_SHMOBILE && HAVE_CLK + depends on DMADEVICES + select DMA_ENGINE + select SH_DMAE + select FW_LOADER + +config SND_SOC_RCAR + tristate "R-Car series SRU/SCU/SSIU/SSI support" + depends on COMMON_CLK + depends on OF + select SND_SIMPLE_CARD_UTILS + select SND_DMAENGINE_PCM + select REGMAP_MMIO + help + This option enables R-Car SRU/SCU/SSIU/SSI sound support + +config SND_SOC_RZ + tristate "RZ/G2L series SSIF-2 support" + depends on ARCH_RZG2L || COMPILE_TEST + help + This option enables RZ/G2L SSIF-2 sound support. + +## +## Boards +## + +config SND_SH7760_AC97 + tristate "SH7760 AC97 sound support" + depends on CPU_SUBTYPE_SH7760 && SND_SOC_PCM_SH7760 + select SND_SOC_SH4_HAC + select SND_SOC_AC97_CODEC + help + This option enables generic sound support for the first + AC97 unit of the SH7760. + +config SND_SIU_MIGOR + tristate "SIU sound support on Migo-R" + depends on SH_MIGOR && I2C + select SND_SOC_SH4_SIU + select SND_SOC_WM8978 + help + This option enables sound support for the SH7722 Migo-R board + +endmenu diff --git a/sound/soc/renesas/Makefile b/sound/soc/renesas/Makefile new file mode 100644 index 000000000000..f0e19cbd1581 --- /dev/null +++ b/sound/soc/renesas/Makefile @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0 +## DMA engines +snd-soc-dma-sh7760-y := dma-sh7760.o +obj-$(CONFIG_SND_SOC_PCM_SH7760) += snd-soc-dma-sh7760.o + +## audio units found on some SH-4 +snd-soc-hac-y := hac.o +snd-soc-ssi-y := ssi.o +snd-soc-fsi-y := fsi.o +snd-soc-siu-y := siu_pcm.o siu_dai.o +obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o +obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o +obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o +obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o + +## audio units for R-Car +obj-$(CONFIG_SND_SOC_RCAR) += rcar/ + +## boards +snd-soc-sh7760-ac97-y := sh7760-ac97.o +snd-soc-migor-y := migor.o + +obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o +obj-$(CONFIG_SND_SIU_MIGOR) += snd-soc-migor.o + +# RZ/G2L +snd-soc-rz-ssi-y := rz-ssi.o +obj-$(CONFIG_SND_SOC_RZ) += snd-soc-rz-ssi.o diff --git a/sound/soc/renesas/dma-sh7760.c b/sound/soc/renesas/dma-sh7760.c new file mode 100644 index 000000000000..c53539482c20 --- /dev/null +++ b/sound/soc/renesas/dma-sh7760.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// SH7760 ("camelot") DMABRG audio DMA unit support +// +// Copyright (C) 2007 Manuel Lauss +// +// The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which +// trigger an interrupt when one half of the programmed transfer size +// has been xmitted. +// +// FIXME: little-endian only for now + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* registers and bits */ +#define BRGATXSAR 0x00 +#define BRGARXDAR 0x04 +#define BRGATXTCR 0x08 +#define BRGARXTCR 0x0C +#define BRGACR 0x10 +#define BRGATXTCNT 0x14 +#define BRGARXTCNT 0x18 + +#define ACR_RAR (1 << 18) +#define ACR_RDS (1 << 17) +#define ACR_RDE (1 << 16) +#define ACR_TAR (1 << 2) +#define ACR_TDS (1 << 1) +#define ACR_TDE (1 << 0) + +/* receiver/transmitter data alignment */ +#define ACR_RAM_NONE (0 << 24) +#define ACR_RAM_4BYTE (1 << 24) +#define ACR_RAM_2WORD (2 << 24) +#define ACR_TAM_NONE (0 << 8) +#define ACR_TAM_4BYTE (1 << 8) +#define ACR_TAM_2WORD (2 << 8) + + +struct camelot_pcm { + unsigned long mmio; /* DMABRG audio channel control reg MMIO */ + unsigned int txid; /* ID of first DMABRG IRQ for this unit */ + + struct snd_pcm_substream *tx_ss; + unsigned long tx_period_size; + unsigned int tx_period; + + struct snd_pcm_substream *rx_ss; + unsigned long rx_period_size; + unsigned int rx_period; + +} cam_pcm_data[2] = { + { + .mmio = 0xFE3C0040, + .txid = DMABRGIRQ_A0TXF, + }, + { + .mmio = 0xFE3C0060, + .txid = DMABRGIRQ_A1TXF, + }, +}; + +#define BRGREG(x) (*(unsigned long *)(cam->mmio + (x))) + +/* + * set a minimum of 16kb per period, to avoid interrupt-"storm" and + * resulting skipping. In general, the bigger the minimum size, the + * better for overall system performance. (The SH7760 is a puny CPU + * with a slow SDRAM interface and poor internal bus bandwidth, + * *especially* when the LCDC is active). The minimum for the DMAC + * is 8 bytes; 16kbytes are enough to get skip-free playback of a + * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain + * reasonable responsiveness in MPlayer. + */ +#define DMABRG_PERIOD_MIN 16 * 1024 +#define DMABRG_PERIOD_MAX 0x03fffffc +#define DMABRG_PREALLOC_BUFFER 32 * 1024 +#define DMABRG_PREALLOC_BUFFER_MAX 32 * 1024 + +static const struct snd_pcm_hardware camelot_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH), + .buffer_bytes_max = DMABRG_PERIOD_MAX, + .period_bytes_min = DMABRG_PERIOD_MIN, + .period_bytes_max = DMABRG_PERIOD_MAX / 2, + .periods_min = 2, + .periods_max = 2, + .fifo_size = 128, +}; + +static void camelot_txdma(void *data) +{ + struct camelot_pcm *cam = data; + cam->tx_period ^= 1; + snd_pcm_period_elapsed(cam->tx_ss); +} + +static void camelot_rxdma(void *data) +{ + struct camelot_pcm *cam = data; + cam->rx_period ^= 1; + snd_pcm_period_elapsed(cam->rx_ss); +} + +static int camelot_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; + int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; + int ret, dmairq; + + snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware); + + /* DMABRG buffer half/full events */ + dmairq = (recv) ? cam->txid + 2 : cam->txid; + if (recv) { + cam->rx_ss = substream; + ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam); + if (unlikely(ret)) { + pr_debug("audio unit %d irqs already taken!\n", + snd_soc_rtd_to_cpu(rtd, 0)->id); + return -EBUSY; + } + (void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam); + } else { + cam->tx_ss = substream; + ret = dmabrg_request_irq(dmairq, camelot_txdma, cam); + if (unlikely(ret)) { + pr_debug("audio unit %d irqs already taken!\n", + snd_soc_rtd_to_cpu(rtd, 0)->id); + return -EBUSY; + } + (void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam); + } + return 0; +} + +static int camelot_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; + int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; + int dmairq; + + dmairq = (recv) ? cam->txid + 2 : cam->txid; + + if (recv) + cam->rx_ss = NULL; + else + cam->tx_ss = NULL; + + dmabrg_free_irq(dmairq + 1); + dmabrg_free_irq(dmairq); + + return 0; +} + +static int camelot_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; + int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; + + if (recv) { + cam->rx_period_size = params_period_bytes(hw_params); + cam->rx_period = 0; + } else { + cam->tx_period_size = params_period_bytes(hw_params); + cam->tx_period = 0; + } + return 0; +} + +static int camelot_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; + + pr_debug("PCM data: addr %pad len %zu\n", &runtime->dma_addr, + runtime->dma_bytes); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area; + BRGREG(BRGATXTCR) = runtime->dma_bytes; + } else { + BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area; + BRGREG(BRGARXTCR) = runtime->dma_bytes; + } + + return 0; +} + +static inline void dmabrg_play_dma_start(struct camelot_pcm *cam) +{ + unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); + /* start DMABRG engine: XFER start, auto-addr-reload */ + BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD; +} + +static inline void dmabrg_play_dma_stop(struct camelot_pcm *cam) +{ + unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); + /* forcibly terminate data transmission */ + BRGREG(BRGACR) = acr | ACR_TDS; +} + +static inline void dmabrg_rec_dma_start(struct camelot_pcm *cam) +{ + unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); + /* start DMABRG engine: recv start, auto-reload */ + BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD; +} + +static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam) +{ + unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); + /* forcibly terminate data receiver */ + BRGREG(BRGACR) = acr | ACR_RDS; +} + +static int camelot_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; + int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (recv) + dmabrg_rec_dma_start(cam); + else + dmabrg_play_dma_start(cam); + break; + case SNDRV_PCM_TRIGGER_STOP: + if (recv) + dmabrg_rec_dma_stop(cam); + else + dmabrg_play_dma_stop(cam); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t camelot_pos(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; + int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; + unsigned long pos; + + /* cannot use the DMABRG pointer register: under load, by the + * time ALSA comes around to read the register, it is already + * far ahead (or worse, already done with the fragment) of the + * position at the time the IRQ was triggered, which results in + * fast-playback sound in my test application (ScummVM) + */ + if (recv) + pos = cam->rx_period ? cam->rx_period_size : 0; + else + pos = cam->tx_period ? cam->tx_period_size : 0; + + return bytes_to_frames(runtime, pos); +} + +static int camelot_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + + /* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel + * in MMAP mode (i.e. aplay -M) + */ + snd_pcm_set_managed_buffer_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + NULL, + DMABRG_PREALLOC_BUFFER, DMABRG_PREALLOC_BUFFER_MAX); + + return 0; +} + +static const struct snd_soc_component_driver sh7760_soc_component = { + .open = camelot_pcm_open, + .close = camelot_pcm_close, + .hw_params = camelot_hw_params, + .prepare = camelot_prepare, + .trigger = camelot_trigger, + .pointer = camelot_pos, + .pcm_construct = camelot_pcm_new, +}; + +static int sh7760_soc_platform_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, &sh7760_soc_component, + NULL, 0); +} + +static struct platform_driver sh7760_pcm_driver = { + .driver = { + .name = "sh7760-pcm-audio", + }, + + .probe = sh7760_soc_platform_probe, +}; + +module_platform_driver(sh7760_pcm_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver"); +MODULE_AUTHOR("Manuel Lauss "); diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c new file mode 100644 index 000000000000..221ce91f1950 --- /dev/null +++ b/sound/soc/renesas/fsi.c @@ -0,0 +1,2119 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Fifo-attached Serial Interface (FSI) support for SH7724 +// +// Copyright (C) 2009 Renesas Solutions Corp. +// Kuninori Morimoto +// +// Based on ssi.c +// Copyright (c) 2007 Manuel Lauss + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PortA/PortB register */ +#define REG_DO_FMT 0x0000 +#define REG_DOFF_CTL 0x0004 +#define REG_DOFF_ST 0x0008 +#define REG_DI_FMT 0x000C +#define REG_DIFF_CTL 0x0010 +#define REG_DIFF_ST 0x0014 +#define REG_CKG1 0x0018 +#define REG_CKG2 0x001C +#define REG_DIDT 0x0020 +#define REG_DODT 0x0024 +#define REG_MUTE_ST 0x0028 +#define REG_OUT_DMAC 0x002C +#define REG_OUT_SEL 0x0030 +#define REG_IN_DMAC 0x0038 + +/* master register */ +#define MST_CLK_RST 0x0210 +#define MST_SOFT_RST 0x0214 +#define MST_FIFO_SZ 0x0218 + +/* core register (depend on FSI version) */ +#define A_MST_CTLR 0x0180 +#define B_MST_CTLR 0x01A0 +#define CPU_INT_ST 0x01F4 +#define CPU_IEMSK 0x01F8 +#define CPU_IMSK 0x01FC +#define INT_ST 0x0200 +#define IEMSK 0x0204 +#define IMSK 0x0208 + +/* DO_FMT */ +/* DI_FMT */ +#define CR_BWS_MASK (0x3 << 20) /* FSI2 */ +#define CR_BWS_24 (0x0 << 20) /* FSI2 */ +#define CR_BWS_16 (0x1 << 20) /* FSI2 */ +#define CR_BWS_20 (0x2 << 20) /* FSI2 */ + +#define CR_DTMD_PCM (0x0 << 8) /* FSI2 */ +#define CR_DTMD_SPDIF_PCM (0x1 << 8) /* FSI2 */ +#define CR_DTMD_SPDIF_STREAM (0x2 << 8) /* FSI2 */ + +#define CR_MONO (0x0 << 4) +#define CR_MONO_D (0x1 << 4) +#define CR_PCM (0x2 << 4) +#define CR_I2S (0x3 << 4) +#define CR_TDM (0x4 << 4) +#define CR_TDM_D (0x5 << 4) + +/* OUT_DMAC */ +/* IN_DMAC */ +#define VDMD_MASK (0x3 << 4) +#define VDMD_FRONT (0x0 << 4) /* Package in front */ +#define VDMD_BACK (0x1 << 4) /* Package in back */ +#define VDMD_STREAM (0x2 << 4) /* Stream mode(16bit * 2) */ + +#define DMA_ON (0x1 << 0) + +/* DOFF_CTL */ +/* DIFF_CTL */ +#define IRQ_HALF 0x00100000 +#define FIFO_CLR 0x00000001 + +/* DOFF_ST */ +#define ERR_OVER 0x00000010 +#define ERR_UNDER 0x00000001 +#define ST_ERR (ERR_OVER | ERR_UNDER) + +/* CKG1 */ +#define ACKMD_MASK 0x00007000 +#define BPFMD_MASK 0x00000700 +#define DIMD (1 << 4) +#define DOMD (1 << 0) + +/* A/B MST_CTLR */ +#define BP (1 << 4) /* Fix the signal of Biphase output */ +#define SE (1 << 0) /* Fix the master clock */ + +/* CLK_RST */ +#define CRB (1 << 4) +#define CRA (1 << 0) + +/* IO SHIFT / MACRO */ +#define BI_SHIFT 12 +#define BO_SHIFT 8 +#define AI_SHIFT 4 +#define AO_SHIFT 0 +#define AB_IO(param, shift) (param << shift) + +/* SOFT_RST */ +#define PBSR (1 << 12) /* Port B Software Reset */ +#define PASR (1 << 8) /* Port A Software Reset */ +#define IR (1 << 4) /* Interrupt Reset */ +#define FSISR (1 << 0) /* Software Reset */ + +/* OUT_SEL (FSI2) */ +#define DMMD (1 << 4) /* SPDIF output timing 0: Biphase only */ + /* 1: Biphase and serial */ + +/* FIFO_SZ */ +#define FIFO_SZ_MASK 0x7 + +#define FSI_RATES SNDRV_PCM_RATE_8000_96000 + +#define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) + +/* + * bus options + * + * 0x000000BA + * + * A : sample widtht 16bit setting + * B : sample widtht 24bit setting + */ + +#define SHIFT_16DATA 0 +#define SHIFT_24DATA 4 + +#define PACKAGE_24BITBUS_BACK 0 +#define PACKAGE_24BITBUS_FRONT 1 +#define PACKAGE_16BITBUS_STREAM 2 + +#define BUSOP_SET(s, a) ((a) << SHIFT_ ## s ## DATA) +#define BUSOP_GET(s, a) (((a) >> SHIFT_ ## s ## DATA) & 0xF) + +/* + * FSI driver use below type name for variable + * + * xxx_num : number of data + * xxx_pos : position of data + * xxx_capa : capacity of data + */ + +/* + * period/frame/sample image + * + * ex) PCM (2ch) + * + * period pos period pos + * [n] [n + 1] + * |<-------------------- period--------------------->| + * ==|============================================ ... =|== + * | | + * ||<----- frame ----->|<------ frame ----->| ... | + * |+--------------------+--------------------+- ... | + * ||[ sample ][ sample ]|[ sample ][ sample ]| ... | + * |+--------------------+--------------------+- ... | + * ==|============================================ ... =|== + */ + +/* + * FSI FIFO image + * + * | | + * | | + * | [ sample ] | + * | [ sample ] | + * | [ sample ] | + * | [ sample ] | + * --> go to codecs + */ + +/* + * FSI clock + * + * FSIxCLK [CPG] (ick) -------> | + * |-> FSI_DIV (div)-> FSI2 + * FSIxCK [external] (xck) ---> | + */ + +/* + * struct + */ + +struct fsi_stream_handler; +struct fsi_stream { + + /* + * these are initialized by fsi_stream_init() + */ + struct snd_pcm_substream *substream; + int fifo_sample_capa; /* sample capacity of FSI FIFO */ + int buff_sample_capa; /* sample capacity of ALSA buffer */ + int buff_sample_pos; /* sample position of ALSA buffer */ + int period_samples; /* sample number / 1 period */ + int period_pos; /* current period position */ + int sample_width; /* sample width */ + int uerr_num; + int oerr_num; + + /* + * bus options + */ + u32 bus_option; + + /* + * these are initialized by fsi_handler_init() + */ + struct fsi_stream_handler *handler; + struct fsi_priv *priv; + + /* + * these are for DMAEngine + */ + struct dma_chan *chan; + int dma_id; +}; + +struct fsi_clk { + /* see [FSI clock] */ + struct clk *own; + struct clk *xck; + struct clk *ick; + struct clk *div; + int (*set_rate)(struct device *dev, + struct fsi_priv *fsi); + + unsigned long rate; + unsigned int count; +}; + +struct fsi_priv { + void __iomem *base; + phys_addr_t phys; + struct fsi_master *master; + + struct fsi_stream playback; + struct fsi_stream capture; + + struct fsi_clk clock; + + u32 fmt; + + int chan_num:16; + unsigned int clk_master:1; + unsigned int clk_cpg:1; + unsigned int spdif:1; + unsigned int enable_stream:1; + unsigned int bit_clk_inv:1; + unsigned int lr_clk_inv:1; +}; + +struct fsi_stream_handler { + int (*init)(struct fsi_priv *fsi, struct fsi_stream *io); + int (*quit)(struct fsi_priv *fsi, struct fsi_stream *io); + int (*probe)(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev); + int (*transfer)(struct fsi_priv *fsi, struct fsi_stream *io); + int (*remove)(struct fsi_priv *fsi, struct fsi_stream *io); + int (*start_stop)(struct fsi_priv *fsi, struct fsi_stream *io, + int enable); +}; +#define fsi_stream_handler_call(io, func, args...) \ + (!(io) ? -ENODEV : \ + !((io)->handler->func) ? 0 : \ + (io)->handler->func(args)) + +struct fsi_core { + int ver; + + u32 int_st; + u32 iemsk; + u32 imsk; + u32 a_mclk; + u32 b_mclk; +}; + +struct fsi_master { + void __iomem *base; + struct fsi_priv fsia; + struct fsi_priv fsib; + const struct fsi_core *core; + spinlock_t lock; +}; + +static inline int fsi_stream_is_play(struct fsi_priv *fsi, + struct fsi_stream *io) +{ + return &fsi->playback == io; +} + + +/* + * basic read write function + */ + +static void __fsi_reg_write(u32 __iomem *reg, u32 data) +{ + /* valid data area is 24bit */ + data &= 0x00ffffff; + + __raw_writel(data, reg); +} + +static u32 __fsi_reg_read(u32 __iomem *reg) +{ + return __raw_readl(reg); +} + +static void __fsi_reg_mask_set(u32 __iomem *reg, u32 mask, u32 data) +{ + u32 val = __fsi_reg_read(reg); + + val &= ~mask; + val |= data & mask; + + __fsi_reg_write(reg, val); +} + +#define fsi_reg_write(p, r, d)\ + __fsi_reg_write((p->base + REG_##r), d) + +#define fsi_reg_read(p, r)\ + __fsi_reg_read((p->base + REG_##r)) + +#define fsi_reg_mask_set(p, r, m, d)\ + __fsi_reg_mask_set((p->base + REG_##r), m, d) + +#define fsi_master_read(p, r) _fsi_master_read(p, MST_##r) +#define fsi_core_read(p, r) _fsi_master_read(p, p->core->r) +static u32 _fsi_master_read(struct fsi_master *master, u32 reg) +{ + u32 ret; + unsigned long flags; + + spin_lock_irqsave(&master->lock, flags); + ret = __fsi_reg_read(master->base + reg); + spin_unlock_irqrestore(&master->lock, flags); + + return ret; +} + +#define fsi_master_mask_set(p, r, m, d) _fsi_master_mask_set(p, MST_##r, m, d) +#define fsi_core_mask_set(p, r, m, d) _fsi_master_mask_set(p, p->core->r, m, d) +static void _fsi_master_mask_set(struct fsi_master *master, + u32 reg, u32 mask, u32 data) +{ + unsigned long flags; + + spin_lock_irqsave(&master->lock, flags); + __fsi_reg_mask_set(master->base + reg, mask, data); + spin_unlock_irqrestore(&master->lock, flags); +} + +/* + * basic function + */ +static int fsi_version(struct fsi_master *master) +{ + return master->core->ver; +} + +static struct fsi_master *fsi_get_master(struct fsi_priv *fsi) +{ + return fsi->master; +} + +static int fsi_is_clk_master(struct fsi_priv *fsi) +{ + return fsi->clk_master; +} + +static int fsi_is_port_a(struct fsi_priv *fsi) +{ + return fsi->master->base == fsi->base; +} + +static int fsi_is_spdif(struct fsi_priv *fsi) +{ + return fsi->spdif; +} + +static int fsi_is_enable_stream(struct fsi_priv *fsi) +{ + return fsi->enable_stream; +} + +static int fsi_is_play(struct snd_pcm_substream *substream) +{ + return substream->stream == SNDRV_PCM_STREAM_PLAYBACK; +} + +static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + + return snd_soc_rtd_to_cpu(rtd, 0); +} + +static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai) +{ + struct fsi_master *master = snd_soc_dai_get_drvdata(dai); + + if (dai->id == 0) + return &master->fsia; + else + return &master->fsib; +} + +static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) +{ + return fsi_get_priv_frm_dai(fsi_get_dai(substream)); +} + +static u32 fsi_get_port_shift(struct fsi_priv *fsi, struct fsi_stream *io) +{ + int is_play = fsi_stream_is_play(fsi, io); + int is_porta = fsi_is_port_a(fsi); + u32 shift; + + if (is_porta) + shift = is_play ? AO_SHIFT : AI_SHIFT; + else + shift = is_play ? BO_SHIFT : BI_SHIFT; + + return shift; +} + +static int fsi_frame2sample(struct fsi_priv *fsi, int frames) +{ + return frames * fsi->chan_num; +} + +static int fsi_sample2frame(struct fsi_priv *fsi, int samples) +{ + return samples / fsi->chan_num; +} + +static int fsi_get_current_fifo_samples(struct fsi_priv *fsi, + struct fsi_stream *io) +{ + int is_play = fsi_stream_is_play(fsi, io); + u32 status; + int frames; + + status = is_play ? + fsi_reg_read(fsi, DOFF_ST) : + fsi_reg_read(fsi, DIFF_ST); + + frames = 0x1ff & (status >> 8); + + return fsi_frame2sample(fsi, frames); +} + +static void fsi_count_fifo_err(struct fsi_priv *fsi) +{ + u32 ostatus = fsi_reg_read(fsi, DOFF_ST); + u32 istatus = fsi_reg_read(fsi, DIFF_ST); + + if (ostatus & ERR_OVER) + fsi->playback.oerr_num++; + + if (ostatus & ERR_UNDER) + fsi->playback.uerr_num++; + + if (istatus & ERR_OVER) + fsi->capture.oerr_num++; + + if (istatus & ERR_UNDER) + fsi->capture.uerr_num++; + + fsi_reg_write(fsi, DOFF_ST, 0); + fsi_reg_write(fsi, DIFF_ST, 0); +} + +/* + * fsi_stream_xx() function + */ +static inline struct fsi_stream *fsi_stream_get(struct fsi_priv *fsi, + struct snd_pcm_substream *substream) +{ + return fsi_is_play(substream) ? &fsi->playback : &fsi->capture; +} + +static int fsi_stream_is_working(struct fsi_priv *fsi, + struct fsi_stream *io) +{ + struct fsi_master *master = fsi_get_master(fsi); + unsigned long flags; + int ret; + + spin_lock_irqsave(&master->lock, flags); + ret = !!(io->substream && io->substream->runtime); + spin_unlock_irqrestore(&master->lock, flags); + + return ret; +} + +static struct fsi_priv *fsi_stream_to_priv(struct fsi_stream *io) +{ + return io->priv; +} + +static void fsi_stream_init(struct fsi_priv *fsi, + struct fsi_stream *io, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsi_master *master = fsi_get_master(fsi); + unsigned long flags; + + spin_lock_irqsave(&master->lock, flags); + io->substream = substream; + io->buff_sample_capa = fsi_frame2sample(fsi, runtime->buffer_size); + io->buff_sample_pos = 0; + io->period_samples = fsi_frame2sample(fsi, runtime->period_size); + io->period_pos = 0; + io->sample_width = samples_to_bytes(runtime, 1); + io->bus_option = 0; + io->oerr_num = -1; /* ignore 1st err */ + io->uerr_num = -1; /* ignore 1st err */ + fsi_stream_handler_call(io, init, fsi, io); + spin_unlock_irqrestore(&master->lock, flags); +} + +static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io) +{ + struct snd_soc_dai *dai = fsi_get_dai(io->substream); + struct fsi_master *master = fsi_get_master(fsi); + unsigned long flags; + + spin_lock_irqsave(&master->lock, flags); + + if (io->oerr_num > 0) + dev_err(dai->dev, "over_run = %d\n", io->oerr_num); + + if (io->uerr_num > 0) + dev_err(dai->dev, "under_run = %d\n", io->uerr_num); + + fsi_stream_handler_call(io, quit, fsi, io); + io->substream = NULL; + io->buff_sample_capa = 0; + io->buff_sample_pos = 0; + io->period_samples = 0; + io->period_pos = 0; + io->sample_width = 0; + io->bus_option = 0; + io->oerr_num = 0; + io->uerr_num = 0; + spin_unlock_irqrestore(&master->lock, flags); +} + +static int fsi_stream_transfer(struct fsi_stream *io) +{ + struct fsi_priv *fsi = fsi_stream_to_priv(io); + if (!fsi) + return -EIO; + + return fsi_stream_handler_call(io, transfer, fsi, io); +} + +#define fsi_stream_start(fsi, io)\ + fsi_stream_handler_call(io, start_stop, fsi, io, 1) + +#define fsi_stream_stop(fsi, io)\ + fsi_stream_handler_call(io, start_stop, fsi, io, 0) + +static int fsi_stream_probe(struct fsi_priv *fsi, struct device *dev) +{ + struct fsi_stream *io; + int ret1, ret2; + + io = &fsi->playback; + ret1 = fsi_stream_handler_call(io, probe, fsi, io, dev); + + io = &fsi->capture; + ret2 = fsi_stream_handler_call(io, probe, fsi, io, dev); + + if (ret1 < 0) + return ret1; + if (ret2 < 0) + return ret2; + + return 0; +} + +static int fsi_stream_remove(struct fsi_priv *fsi) +{ + struct fsi_stream *io; + int ret1, ret2; + + io = &fsi->playback; + ret1 = fsi_stream_handler_call(io, remove, fsi, io); + + io = &fsi->capture; + ret2 = fsi_stream_handler_call(io, remove, fsi, io); + + if (ret1 < 0) + return ret1; + if (ret2 < 0) + return ret2; + + return 0; +} + +/* + * format/bus/dma setting + */ +static void fsi_format_bus_setup(struct fsi_priv *fsi, struct fsi_stream *io, + u32 bus, struct device *dev) +{ + struct fsi_master *master = fsi_get_master(fsi); + int is_play = fsi_stream_is_play(fsi, io); + u32 fmt = fsi->fmt; + + if (fsi_version(master) >= 2) { + u32 dma = 0; + + /* + * FSI2 needs DMA/Bus setting + */ + switch (bus) { + case PACKAGE_24BITBUS_FRONT: + fmt |= CR_BWS_24; + dma |= VDMD_FRONT; + dev_dbg(dev, "24bit bus / package in front\n"); + break; + case PACKAGE_16BITBUS_STREAM: + fmt |= CR_BWS_16; + dma |= VDMD_STREAM; + dev_dbg(dev, "16bit bus / stream mode\n"); + break; + case PACKAGE_24BITBUS_BACK: + default: + fmt |= CR_BWS_24; + dma |= VDMD_BACK; + dev_dbg(dev, "24bit bus / package in back\n"); + break; + } + + if (is_play) + fsi_reg_write(fsi, OUT_DMAC, dma); + else + fsi_reg_write(fsi, IN_DMAC, dma); + } + + if (is_play) + fsi_reg_write(fsi, DO_FMT, fmt); + else + fsi_reg_write(fsi, DI_FMT, fmt); +} + +/* + * irq function + */ + +static void fsi_irq_enable(struct fsi_priv *fsi, struct fsi_stream *io) +{ + u32 data = AB_IO(1, fsi_get_port_shift(fsi, io)); + struct fsi_master *master = fsi_get_master(fsi); + + fsi_core_mask_set(master, imsk, data, data); + fsi_core_mask_set(master, iemsk, data, data); +} + +static void fsi_irq_disable(struct fsi_priv *fsi, struct fsi_stream *io) +{ + u32 data = AB_IO(1, fsi_get_port_shift(fsi, io)); + struct fsi_master *master = fsi_get_master(fsi); + + fsi_core_mask_set(master, imsk, data, 0); + fsi_core_mask_set(master, iemsk, data, 0); +} + +static u32 fsi_irq_get_status(struct fsi_master *master) +{ + return fsi_core_read(master, int_st); +} + +static void fsi_irq_clear_status(struct fsi_priv *fsi) +{ + u32 data = 0; + struct fsi_master *master = fsi_get_master(fsi); + + data |= AB_IO(1, fsi_get_port_shift(fsi, &fsi->playback)); + data |= AB_IO(1, fsi_get_port_shift(fsi, &fsi->capture)); + + /* clear interrupt factor */ + fsi_core_mask_set(master, int_st, data, 0); +} + +/* + * SPDIF master clock function + * + * These functions are used later FSI2 + */ +static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) +{ + struct fsi_master *master = fsi_get_master(fsi); + u32 mask, val; + + mask = BP | SE; + val = enable ? mask : 0; + + fsi_is_port_a(fsi) ? + fsi_core_mask_set(master, a_mclk, mask, val) : + fsi_core_mask_set(master, b_mclk, mask, val); +} + +/* + * clock function + */ +static int fsi_clk_init(struct device *dev, + struct fsi_priv *fsi, + int xck, + int ick, + int div, + int (*set_rate)(struct device *dev, + struct fsi_priv *fsi)) +{ + struct fsi_clk *clock = &fsi->clock; + int is_porta = fsi_is_port_a(fsi); + + clock->xck = NULL; + clock->ick = NULL; + clock->div = NULL; + clock->rate = 0; + clock->count = 0; + clock->set_rate = set_rate; + + clock->own = devm_clk_get(dev, NULL); + if (IS_ERR(clock->own)) + return -EINVAL; + + /* external clock */ + if (xck) { + clock->xck = devm_clk_get(dev, is_porta ? "xcka" : "xckb"); + if (IS_ERR(clock->xck)) { + dev_err(dev, "can't get xck clock\n"); + return -EINVAL; + } + if (clock->xck == clock->own) { + dev_err(dev, "cpu doesn't support xck clock\n"); + return -EINVAL; + } + } + + /* FSIACLK/FSIBCLK */ + if (ick) { + clock->ick = devm_clk_get(dev, is_porta ? "icka" : "ickb"); + if (IS_ERR(clock->ick)) { + dev_err(dev, "can't get ick clock\n"); + return -EINVAL; + } + if (clock->ick == clock->own) { + dev_err(dev, "cpu doesn't support ick clock\n"); + return -EINVAL; + } + } + + /* FSI-DIV */ + if (div) { + clock->div = devm_clk_get(dev, is_porta ? "diva" : "divb"); + if (IS_ERR(clock->div)) { + dev_err(dev, "can't get div clock\n"); + return -EINVAL; + } + if (clock->div == clock->own) { + dev_err(dev, "cpu doesn't support div clock\n"); + return -EINVAL; + } + } + + return 0; +} + +#define fsi_clk_invalid(fsi) fsi_clk_valid(fsi, 0) +static void fsi_clk_valid(struct fsi_priv *fsi, unsigned long rate) +{ + fsi->clock.rate = rate; +} + +static int fsi_clk_is_valid(struct fsi_priv *fsi) +{ + return fsi->clock.set_rate && + fsi->clock.rate; +} + +static int fsi_clk_enable(struct device *dev, + struct fsi_priv *fsi) +{ + struct fsi_clk *clock = &fsi->clock; + int ret = -EINVAL; + + if (!fsi_clk_is_valid(fsi)) + return ret; + + if (0 == clock->count) { + ret = clock->set_rate(dev, fsi); + if (ret < 0) { + fsi_clk_invalid(fsi); + return ret; + } + + ret = clk_enable(clock->xck); + if (ret) + goto err; + ret = clk_enable(clock->ick); + if (ret) + goto disable_xck; + ret = clk_enable(clock->div); + if (ret) + goto disable_ick; + + clock->count++; + } + + return ret; + +disable_ick: + clk_disable(clock->ick); +disable_xck: + clk_disable(clock->xck); +err: + return ret; +} + +static int fsi_clk_disable(struct device *dev, + struct fsi_priv *fsi) +{ + struct fsi_clk *clock = &fsi->clock; + + if (!fsi_clk_is_valid(fsi)) + return -EINVAL; + + if (1 == clock->count--) { + clk_disable(clock->xck); + clk_disable(clock->ick); + clk_disable(clock->div); + } + + return 0; +} + +static int fsi_clk_set_ackbpf(struct device *dev, + struct fsi_priv *fsi, + int ackmd, int bpfmd) +{ + u32 data = 0; + + /* check ackmd/bpfmd relationship */ + if (bpfmd > ackmd) { + dev_err(dev, "unsupported rate (%d/%d)\n", ackmd, bpfmd); + return -EINVAL; + } + + /* ACKMD */ + switch (ackmd) { + case 512: + data |= (0x0 << 12); + break; + case 256: + data |= (0x1 << 12); + break; + case 128: + data |= (0x2 << 12); + break; + case 64: + data |= (0x3 << 12); + break; + case 32: + data |= (0x4 << 12); + break; + default: + dev_err(dev, "unsupported ackmd (%d)\n", ackmd); + return -EINVAL; + } + + /* BPFMD */ + switch (bpfmd) { + case 32: + data |= (0x0 << 8); + break; + case 64: + data |= (0x1 << 8); + break; + case 128: + data |= (0x2 << 8); + break; + case 256: + data |= (0x3 << 8); + break; + case 512: + data |= (0x4 << 8); + break; + case 16: + data |= (0x7 << 8); + break; + default: + dev_err(dev, "unsupported bpfmd (%d)\n", bpfmd); + return -EINVAL; + } + + dev_dbg(dev, "ACKMD/BPFMD = %d/%d\n", ackmd, bpfmd); + + fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); + udelay(10); + + return 0; +} + +static int fsi_clk_set_rate_external(struct device *dev, + struct fsi_priv *fsi) +{ + struct clk *xck = fsi->clock.xck; + struct clk *ick = fsi->clock.ick; + unsigned long rate = fsi->clock.rate; + unsigned long xrate; + int ackmd, bpfmd; + int ret = 0; + + /* check clock rate */ + xrate = clk_get_rate(xck); + if (xrate % rate) { + dev_err(dev, "unsupported clock rate\n"); + return -EINVAL; + } + + clk_set_parent(ick, xck); + clk_set_rate(ick, xrate); + + bpfmd = fsi->chan_num * 32; + ackmd = xrate / rate; + + dev_dbg(dev, "external/rate = %ld/%ld\n", xrate, rate); + + ret = fsi_clk_set_ackbpf(dev, fsi, ackmd, bpfmd); + if (ret < 0) + dev_err(dev, "%s failed", __func__); + + return ret; +} + +static int fsi_clk_set_rate_cpg(struct device *dev, + struct fsi_priv *fsi) +{ + struct clk *ick = fsi->clock.ick; + struct clk *div = fsi->clock.div; + unsigned long rate = fsi->clock.rate; + unsigned long target = 0; /* 12288000 or 11289600 */ + unsigned long actual, cout; + unsigned long diff, min; + unsigned long best_cout, best_act; + int adj; + int ackmd, bpfmd; + int ret = -EINVAL; + + if (!(12288000 % rate)) + target = 12288000; + if (!(11289600 % rate)) + target = 11289600; + if (!target) { + dev_err(dev, "unsupported rate\n"); + return ret; + } + + bpfmd = fsi->chan_num * 32; + ackmd = target / rate; + ret = fsi_clk_set_ackbpf(dev, fsi, ackmd, bpfmd); + if (ret < 0) { + dev_err(dev, "%s failed", __func__); + return ret; + } + + /* + * The clock flow is + * + * [CPG] = cout => [FSI_DIV] = audio => [FSI] => [codec] + * + * But, it needs to find best match of CPG and FSI_DIV + * combination, since it is difficult to generate correct + * frequency of audio clock from ick clock only. + * Because ick is created from its parent clock. + * + * target = rate x [512/256/128/64]fs + * cout = round(target x adjustment) + * actual = cout / adjustment (by FSI-DIV) ~= target + * audio = actual + */ + min = ~0; + best_cout = 0; + best_act = 0; + for (adj = 1; adj < 0xffff; adj++) { + + cout = target * adj; + if (cout > 100000000) /* max clock = 100MHz */ + break; + + /* cout/actual audio clock */ + cout = clk_round_rate(ick, cout); + actual = cout / adj; + + /* find best frequency */ + diff = abs(actual - target); + if (diff < min) { + min = diff; + best_cout = cout; + best_act = actual; + } + } + + ret = clk_set_rate(ick, best_cout); + if (ret < 0) { + dev_err(dev, "ick clock failed\n"); + return -EIO; + } + + ret = clk_set_rate(div, clk_round_rate(div, best_act)); + if (ret < 0) { + dev_err(dev, "div clock failed\n"); + return -EIO; + } + + dev_dbg(dev, "ick/div = %ld/%ld\n", + clk_get_rate(ick), clk_get_rate(div)); + + return ret; +} + +static void fsi_pointer_update(struct fsi_stream *io, int size) +{ + io->buff_sample_pos += size; + + if (io->buff_sample_pos >= + io->period_samples * (io->period_pos + 1)) { + struct snd_pcm_substream *substream = io->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + + io->period_pos++; + + if (io->period_pos >= runtime->periods) { + io->buff_sample_pos = 0; + io->period_pos = 0; + } + + snd_pcm_period_elapsed(substream); + } +} + +/* + * pio data transfer handler + */ +static void fsi_pio_push16(struct fsi_priv *fsi, u8 *_buf, int samples) +{ + int i; + + if (fsi_is_enable_stream(fsi)) { + /* + * stream mode + * see + * fsi_pio_push_init() + */ + u32 *buf = (u32 *)_buf; + + for (i = 0; i < samples / 2; i++) + fsi_reg_write(fsi, DODT, buf[i]); + } else { + /* normal mode */ + u16 *buf = (u16 *)_buf; + + for (i = 0; i < samples; i++) + fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8)); + } +} + +static void fsi_pio_pop16(struct fsi_priv *fsi, u8 *_buf, int samples) +{ + u16 *buf = (u16 *)_buf; + int i; + + for (i = 0; i < samples; i++) + *(buf + i) = (u16)(fsi_reg_read(fsi, DIDT) >> 8); +} + +static void fsi_pio_push32(struct fsi_priv *fsi, u8 *_buf, int samples) +{ + u32 *buf = (u32 *)_buf; + int i; + + for (i = 0; i < samples; i++) + fsi_reg_write(fsi, DODT, *(buf + i)); +} + +static void fsi_pio_pop32(struct fsi_priv *fsi, u8 *_buf, int samples) +{ + u32 *buf = (u32 *)_buf; + int i; + + for (i = 0; i < samples; i++) + *(buf + i) = fsi_reg_read(fsi, DIDT); +} + +static u8 *fsi_pio_get_area(struct fsi_priv *fsi, struct fsi_stream *io) +{ + struct snd_pcm_runtime *runtime = io->substream->runtime; + + return runtime->dma_area + + samples_to_bytes(runtime, io->buff_sample_pos); +} + +static int fsi_pio_transfer(struct fsi_priv *fsi, struct fsi_stream *io, + void (*run16)(struct fsi_priv *fsi, u8 *buf, int samples), + void (*run32)(struct fsi_priv *fsi, u8 *buf, int samples), + int samples) +{ + u8 *buf; + + if (!fsi_stream_is_working(fsi, io)) + return -EINVAL; + + buf = fsi_pio_get_area(fsi, io); + + switch (io->sample_width) { + case 2: + run16(fsi, buf, samples); + break; + case 4: + run32(fsi, buf, samples); + break; + default: + return -EINVAL; + } + + fsi_pointer_update(io, samples); + + return 0; +} + +static int fsi_pio_pop(struct fsi_priv *fsi, struct fsi_stream *io) +{ + int sample_residues; /* samples in FSI fifo */ + int sample_space; /* ALSA free samples space */ + int samples; + + sample_residues = fsi_get_current_fifo_samples(fsi, io); + sample_space = io->buff_sample_capa - io->buff_sample_pos; + + samples = min(sample_residues, sample_space); + + return fsi_pio_transfer(fsi, io, + fsi_pio_pop16, + fsi_pio_pop32, + samples); +} + +static int fsi_pio_push(struct fsi_priv *fsi, struct fsi_stream *io) +{ + int sample_residues; /* ALSA residue samples */ + int sample_space; /* FSI fifo free samples space */ + int samples; + + sample_residues = io->buff_sample_capa - io->buff_sample_pos; + sample_space = io->fifo_sample_capa - + fsi_get_current_fifo_samples(fsi, io); + + samples = min(sample_residues, sample_space); + + return fsi_pio_transfer(fsi, io, + fsi_pio_push16, + fsi_pio_push32, + samples); +} + +static int fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, + int enable) +{ + struct fsi_master *master = fsi_get_master(fsi); + u32 clk = fsi_is_port_a(fsi) ? CRA : CRB; + + if (enable) + fsi_irq_enable(fsi, io); + else + fsi_irq_disable(fsi, io); + + if (fsi_is_clk_master(fsi)) + fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); + + return 0; +} + +static int fsi_pio_push_init(struct fsi_priv *fsi, struct fsi_stream *io) +{ + /* + * we can use 16bit stream mode + * when "playback" and "16bit data" + * and platform allows "stream mode" + * see + * fsi_pio_push16() + */ + if (fsi_is_enable_stream(fsi)) + io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | + BUSOP_SET(16, PACKAGE_16BITBUS_STREAM); + else + io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | + BUSOP_SET(16, PACKAGE_24BITBUS_BACK); + return 0; +} + +static int fsi_pio_pop_init(struct fsi_priv *fsi, struct fsi_stream *io) +{ + /* + * always 24bit bus, package back when "capture" + */ + io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | + BUSOP_SET(16, PACKAGE_24BITBUS_BACK); + return 0; +} + +static struct fsi_stream_handler fsi_pio_push_handler = { + .init = fsi_pio_push_init, + .transfer = fsi_pio_push, + .start_stop = fsi_pio_start_stop, +}; + +static struct fsi_stream_handler fsi_pio_pop_handler = { + .init = fsi_pio_pop_init, + .transfer = fsi_pio_pop, + .start_stop = fsi_pio_start_stop, +}; + +static irqreturn_t fsi_interrupt(int irq, void *data) +{ + struct fsi_master *master = data; + u32 int_st = fsi_irq_get_status(master); + + /* clear irq status */ + fsi_master_mask_set(master, SOFT_RST, IR, 0); + fsi_master_mask_set(master, SOFT_RST, IR, IR); + + if (int_st & AB_IO(1, AO_SHIFT)) + fsi_stream_transfer(&master->fsia.playback); + if (int_st & AB_IO(1, BO_SHIFT)) + fsi_stream_transfer(&master->fsib.playback); + if (int_st & AB_IO(1, AI_SHIFT)) + fsi_stream_transfer(&master->fsia.capture); + if (int_st & AB_IO(1, BI_SHIFT)) + fsi_stream_transfer(&master->fsib.capture); + + fsi_count_fifo_err(&master->fsia); + fsi_count_fifo_err(&master->fsib); + + fsi_irq_clear_status(&master->fsia); + fsi_irq_clear_status(&master->fsib); + + return IRQ_HANDLED; +} + +/* + * dma data transfer handler + */ +static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io) +{ + /* + * 24bit data : 24bit bus / package in back + * 16bit data : 16bit bus / stream mode + */ + io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | + BUSOP_SET(16, PACKAGE_16BITBUS_STREAM); + + return 0; +} + +static void fsi_dma_complete(void *data) +{ + struct fsi_stream *io = (struct fsi_stream *)data; + struct fsi_priv *fsi = fsi_stream_to_priv(io); + + fsi_pointer_update(io, io->period_samples); + + fsi_count_fifo_err(fsi); +} + +static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) +{ + struct snd_soc_dai *dai = fsi_get_dai(io->substream); + struct snd_pcm_substream *substream = io->substream; + struct dma_async_tx_descriptor *desc; + int is_play = fsi_stream_is_play(fsi, io); + enum dma_transfer_direction dir; + int ret = -EIO; + + if (is_play) + dir = DMA_MEM_TO_DEV; + else + dir = DMA_DEV_TO_MEM; + + desc = dmaengine_prep_dma_cyclic(io->chan, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), + dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(dai->dev, "dmaengine_prep_dma_cyclic() fail\n"); + goto fsi_dma_transfer_err; + } + + desc->callback = fsi_dma_complete; + desc->callback_param = io; + + if (dmaengine_submit(desc) < 0) { + dev_err(dai->dev, "tx_submit() fail\n"); + goto fsi_dma_transfer_err; + } + + dma_async_issue_pending(io->chan); + + /* + * FIXME + * + * In DMAEngine case, codec and FSI cannot be started simultaneously + * since FSI is using the scheduler work queue. + * Therefore, in capture case, probably FSI FIFO will have got + * overflow error in this point. + * in that case, DMA cannot start transfer until error was cleared. + */ + if (!is_play) { + if (ERR_OVER & fsi_reg_read(fsi, DIFF_ST)) { + fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR); + fsi_reg_write(fsi, DIFF_ST, 0); + } + } + + ret = 0; + +fsi_dma_transfer_err: + return ret; +} + +static int fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, + int start) +{ + struct fsi_master *master = fsi_get_master(fsi); + u32 clk = fsi_is_port_a(fsi) ? CRA : CRB; + u32 enable = start ? DMA_ON : 0; + + fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable); + + dmaengine_terminate_all(io->chan); + + if (fsi_is_clk_master(fsi)) + fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); + + return 0; +} + +static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev) +{ + int is_play = fsi_stream_is_play(fsi, io); + +#ifdef CONFIG_SUPERH + dma_cap_mask_t mask; + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + io->chan = dma_request_channel(mask, shdma_chan_filter, + (void *)io->dma_id); +#else + io->chan = dma_request_chan(dev, is_play ? "tx" : "rx"); + if (IS_ERR(io->chan)) + io->chan = NULL; +#endif + if (io->chan) { + struct dma_slave_config cfg = {}; + int ret; + + if (is_play) { + cfg.dst_addr = fsi->phys + REG_DODT; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.direction = DMA_MEM_TO_DEV; + } else { + cfg.src_addr = fsi->phys + REG_DIDT; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.direction = DMA_DEV_TO_MEM; + } + + ret = dmaengine_slave_config(io->chan, &cfg); + if (ret < 0) { + dma_release_channel(io->chan); + io->chan = NULL; + } + } + + if (!io->chan) { + + /* switch to PIO handler */ + if (is_play) + fsi->playback.handler = &fsi_pio_push_handler; + else + fsi->capture.handler = &fsi_pio_pop_handler; + + dev_info(dev, "switch handler (dma => pio)\n"); + + /* probe again */ + return fsi_stream_probe(fsi, dev); + } + + return 0; +} + +static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io) +{ + fsi_stream_stop(fsi, io); + + if (io->chan) + dma_release_channel(io->chan); + + io->chan = NULL; + return 0; +} + +static struct fsi_stream_handler fsi_dma_push_handler = { + .init = fsi_dma_init, + .probe = fsi_dma_probe, + .transfer = fsi_dma_transfer, + .remove = fsi_dma_remove, + .start_stop = fsi_dma_push_start_stop, +}; + +/* + * dai ops + */ +static void fsi_fifo_init(struct fsi_priv *fsi, + struct fsi_stream *io, + struct device *dev) +{ + struct fsi_master *master = fsi_get_master(fsi); + int is_play = fsi_stream_is_play(fsi, io); + u32 shift, i; + int frame_capa; + + /* get on-chip RAM capacity */ + shift = fsi_master_read(master, FIFO_SZ); + shift >>= fsi_get_port_shift(fsi, io); + shift &= FIFO_SZ_MASK; + frame_capa = 256 << shift; + dev_dbg(dev, "fifo = %d words\n", frame_capa); + + /* + * The maximum number of sample data varies depending + * on the number of channels selected for the format. + * + * FIFOs are used in 4-channel units in 3-channel mode + * and in 8-channel units in 5- to 7-channel mode + * meaning that more FIFOs than the required size of DPRAM + * are used. + * + * ex) if 256 words of DP-RAM is connected + * 1 channel: 256 (256 x 1 = 256) + * 2 channels: 128 (128 x 2 = 256) + * 3 channels: 64 ( 64 x 3 = 192) + * 4 channels: 64 ( 64 x 4 = 256) + * 5 channels: 32 ( 32 x 5 = 160) + * 6 channels: 32 ( 32 x 6 = 192) + * 7 channels: 32 ( 32 x 7 = 224) + * 8 channels: 32 ( 32 x 8 = 256) + */ + for (i = 1; i < fsi->chan_num; i <<= 1) + frame_capa >>= 1; + dev_dbg(dev, "%d channel %d store\n", + fsi->chan_num, frame_capa); + + io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa); + + /* + * set interrupt generation factor + * clear FIFO + */ + if (is_play) { + fsi_reg_write(fsi, DOFF_CTL, IRQ_HALF); + fsi_reg_mask_set(fsi, DOFF_CTL, FIFO_CLR, FIFO_CLR); + } else { + fsi_reg_write(fsi, DIFF_CTL, IRQ_HALF); + fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR); + } +} + +static int fsi_hw_startup(struct fsi_priv *fsi, + struct fsi_stream *io, + struct device *dev) +{ + u32 data = 0; + + /* clock setting */ + if (fsi_is_clk_master(fsi)) + data = DIMD | DOMD; + + fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); + + /* clock inversion (CKG2) */ + data = 0; + if (fsi->bit_clk_inv) + data |= (1 << 0); + if (fsi->lr_clk_inv) + data |= (1 << 4); + if (fsi_is_clk_master(fsi)) + data <<= 8; + fsi_reg_write(fsi, CKG2, data); + + /* spdif ? */ + if (fsi_is_spdif(fsi)) { + fsi_spdif_clk_ctrl(fsi, 1); + fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + } + + /* + * get bus settings + */ + data = 0; + switch (io->sample_width) { + case 2: + data = BUSOP_GET(16, io->bus_option); + break; + case 4: + data = BUSOP_GET(24, io->bus_option); + break; + } + fsi_format_bus_setup(fsi, io, data, dev); + + /* irq clear */ + fsi_irq_disable(fsi, io); + fsi_irq_clear_status(fsi); + + /* fifo init */ + fsi_fifo_init(fsi, io, dev); + + /* start master clock */ + if (fsi_is_clk_master(fsi)) + return fsi_clk_enable(dev, fsi); + + return 0; +} + +static int fsi_hw_shutdown(struct fsi_priv *fsi, + struct device *dev) +{ + /* stop master clock */ + if (fsi_is_clk_master(fsi)) + return fsi_clk_disable(dev, fsi); + + return 0; +} + +static int fsi_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct fsi_priv *fsi = fsi_get_priv(substream); + + fsi_clk_invalid(fsi); + + return 0; +} + +static void fsi_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct fsi_priv *fsi = fsi_get_priv(substream); + + fsi_clk_invalid(fsi); +} + +static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct fsi_priv *fsi = fsi_get_priv(substream); + struct fsi_stream *io = fsi_stream_get(fsi, substream); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + fsi_stream_init(fsi, io, substream); + if (!ret) + ret = fsi_hw_startup(fsi, io, dai->dev); + if (!ret) + ret = fsi_stream_start(fsi, io); + if (!ret) + ret = fsi_stream_transfer(io); + break; + case SNDRV_PCM_TRIGGER_STOP: + if (!ret) + ret = fsi_hw_shutdown(fsi, dai->dev); + fsi_stream_stop(fsi, io); + fsi_stream_quit(fsi, io); + break; + } + + return ret; +} + +static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) +{ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + fsi->fmt = CR_I2S; + fsi->chan_num = 2; + break; + case SND_SOC_DAIFMT_LEFT_J: + fsi->fmt = CR_PCM; + fsi->chan_num = 2; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fsi_set_fmt_spdif(struct fsi_priv *fsi) +{ + struct fsi_master *master = fsi_get_master(fsi); + + if (fsi_version(master) < 2) + return -EINVAL; + + fsi->fmt = CR_DTMD_SPDIF_PCM | CR_PCM; + fsi->chan_num = 2; + + return 0; +} + +static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); + int ret; + + /* set clock master audio interface */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: + break; + case SND_SOC_DAIFMT_BP_FP: + fsi->clk_master = 1; /* cpu is master */ + break; + default: + return -EINVAL; + } + + /* set clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + fsi->bit_clk_inv = 0; + fsi->lr_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + fsi->bit_clk_inv = 1; + fsi->lr_clk_inv = 0; + break; + case SND_SOC_DAIFMT_IB_IF: + fsi->bit_clk_inv = 1; + fsi->lr_clk_inv = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + default: + fsi->bit_clk_inv = 0; + fsi->lr_clk_inv = 0; + break; + } + + if (fsi_is_clk_master(fsi)) { + if (fsi->clk_cpg) + fsi_clk_init(dai->dev, fsi, 0, 1, 1, + fsi_clk_set_rate_cpg); + else + fsi_clk_init(dai->dev, fsi, 1, 1, 0, + fsi_clk_set_rate_external); + } + + /* set format */ + if (fsi_is_spdif(fsi)) + ret = fsi_set_fmt_spdif(fsi); + else + ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + + return ret; +} + +static int fsi_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct fsi_priv *fsi = fsi_get_priv(substream); + + if (fsi_is_clk_master(fsi)) + fsi_clk_valid(fsi, params_rate(params)); + + return 0; +} + +/* + * Select below from Sound Card, not auto + * SND_SOC_DAIFMT_CBC_CFC + * SND_SOC_DAIFMT_CBP_CFP + */ +static const u64 fsi_dai_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF; + +static const struct snd_soc_dai_ops fsi_dai_ops = { + .startup = fsi_dai_startup, + .shutdown = fsi_dai_shutdown, + .trigger = fsi_dai_trigger, + .set_fmt = fsi_dai_set_fmt, + .hw_params = fsi_dai_hw_params, + .auto_selectable_formats = &fsi_dai_formats, + .num_auto_selectable_formats = 1, +}; + +/* + * pcm ops + */ + +static const struct snd_pcm_hardware fsi_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 32, + .fifo_size = 256, +}; + +static int fsi_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret = 0; + + snd_soc_set_runtime_hwparams(substream, &fsi_pcm_hardware); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + + return ret; +} + +static snd_pcm_uframes_t fsi_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct fsi_priv *fsi = fsi_get_priv(substream); + struct fsi_stream *io = fsi_stream_get(fsi, substream); + + return fsi_sample2frame(fsi, io->buff_sample_pos); +} + +/* + * snd_soc_component + */ + +#define PREALLOC_BUFFER (32 * 1024) +#define PREALLOC_BUFFER_MAX (32 * 1024) + +static int fsi_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + snd_pcm_set_managed_buffer_all( + rtd->pcm, + SNDRV_DMA_TYPE_DEV, + rtd->card->snd_card->dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); + return 0; +} + +/* + * alsa struct + */ + +static struct snd_soc_dai_driver fsi_soc_dai[] = { + { + .name = "fsia-dai", + .playback = { + .rates = FSI_RATES, + .formats = FSI_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .rates = FSI_RATES, + .formats = FSI_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &fsi_dai_ops, + }, + { + .name = "fsib-dai", + .playback = { + .rates = FSI_RATES, + .formats = FSI_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .rates = FSI_RATES, + .formats = FSI_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &fsi_dai_ops, + }, +}; + +static const struct snd_soc_component_driver fsi_soc_component = { + .name = "fsi", + .open = fsi_pcm_open, + .pointer = fsi_pointer, + .pcm_construct = fsi_pcm_new, +}; + +/* + * platform function + */ +static void fsi_of_parse(char *name, + struct device_node *np, + struct sh_fsi_port_info *info, + struct device *dev) +{ + int i; + char prop[128]; + unsigned long flags = 0; + struct { + char *name; + unsigned int val; + } of_parse_property[] = { + { "spdif-connection", SH_FSI_FMT_SPDIF }, + { "stream-mode-support", SH_FSI_ENABLE_STREAM_MODE }, + { "use-internal-clock", SH_FSI_CLK_CPG }, + }; + + for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) { + sprintf(prop, "%s,%s", name, of_parse_property[i].name); + if (of_property_present(np, prop)) + flags |= of_parse_property[i].val; + } + info->flags = flags; + + dev_dbg(dev, "%s flags : %lx\n", name, info->flags); +} + +static void fsi_port_info_init(struct fsi_priv *fsi, + struct sh_fsi_port_info *info) +{ + if (info->flags & SH_FSI_FMT_SPDIF) + fsi->spdif = 1; + + if (info->flags & SH_FSI_CLK_CPG) + fsi->clk_cpg = 1; + + if (info->flags & SH_FSI_ENABLE_STREAM_MODE) + fsi->enable_stream = 1; +} + +static void fsi_handler_init(struct fsi_priv *fsi, + struct sh_fsi_port_info *info) +{ + fsi->playback.handler = &fsi_pio_push_handler; /* default PIO */ + fsi->playback.priv = fsi; + fsi->capture.handler = &fsi_pio_pop_handler; /* default PIO */ + fsi->capture.priv = fsi; + + if (info->tx_id) { + fsi->playback.dma_id = info->tx_id; + fsi->playback.handler = &fsi_dma_push_handler; + } +} + +static const struct fsi_core fsi1_core = { + .ver = 1, + + /* Interrupt */ + .int_st = INT_ST, + .iemsk = IEMSK, + .imsk = IMSK, +}; + +static const struct fsi_core fsi2_core = { + .ver = 2, + + /* Interrupt */ + .int_st = CPU_INT_ST, + .iemsk = CPU_IEMSK, + .imsk = CPU_IMSK, + .a_mclk = A_MST_CTLR, + .b_mclk = B_MST_CTLR, +}; + +static const struct of_device_id fsi_of_match[] = { + { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, + { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, + {}, +}; +MODULE_DEVICE_TABLE(of, fsi_of_match); + +static const struct platform_device_id fsi_id_table[] = { + { "sh_fsi", (kernel_ulong_t)&fsi1_core }, + {}, +}; +MODULE_DEVICE_TABLE(platform, fsi_id_table); + +static int fsi_probe(struct platform_device *pdev) +{ + struct fsi_master *master; + struct device_node *np = pdev->dev.of_node; + struct sh_fsi_platform_info info; + const struct fsi_core *core; + struct fsi_priv *fsi; + struct resource *res; + unsigned int irq; + int ret; + + memset(&info, 0, sizeof(info)); + + core = NULL; + if (np) { + core = of_device_get_match_data(&pdev->dev); + fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); + fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); + } else { + const struct platform_device_id *id_entry = pdev->id_entry; + if (id_entry) + core = (struct fsi_core *)id_entry->driver_data; + + if (pdev->dev.platform_data) + memcpy(&info, pdev->dev.platform_data, sizeof(info)); + } + + if (!core) { + dev_err(&pdev->dev, "unknown fsi device\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!res || (int)irq <= 0) { + dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); + return -ENODEV; + } + + master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); + if (!master) + return -ENOMEM; + + master->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!master->base) { + dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n"); + return -ENXIO; + } + + /* master setting */ + master->core = core; + spin_lock_init(&master->lock); + + /* FSI A setting */ + fsi = &master->fsia; + fsi->base = master->base; + fsi->phys = res->start; + fsi->master = master; + fsi_port_info_init(fsi, &info.port_a); + fsi_handler_init(fsi, &info.port_a); + ret = fsi_stream_probe(fsi, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "FSIA stream probe failed\n"); + return ret; + } + + /* FSI B setting */ + fsi = &master->fsib; + fsi->base = master->base + 0x40; + fsi->phys = res->start + 0x40; + fsi->master = master; + fsi_port_info_init(fsi, &info.port_b); + fsi_handler_init(fsi, &info.port_b); + ret = fsi_stream_probe(fsi, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "FSIB stream probe failed\n"); + goto exit_fsia; + } + + pm_runtime_enable(&pdev->dev); + dev_set_drvdata(&pdev->dev, master); + + ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0, + dev_name(&pdev->dev), master); + if (ret) { + dev_err(&pdev->dev, "irq request err\n"); + goto exit_fsib; + } + + ret = devm_snd_soc_register_component(&pdev->dev, &fsi_soc_component, + fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "cannot snd component register\n"); + goto exit_fsib; + } + + return ret; + +exit_fsib: + pm_runtime_disable(&pdev->dev); + fsi_stream_remove(&master->fsib); +exit_fsia: + fsi_stream_remove(&master->fsia); + + return ret; +} + +static void fsi_remove(struct platform_device *pdev) +{ + struct fsi_master *master; + + master = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + + fsi_stream_remove(&master->fsia); + fsi_stream_remove(&master->fsib); +} + +static void __fsi_suspend(struct fsi_priv *fsi, + struct fsi_stream *io, + struct device *dev) +{ + if (!fsi_stream_is_working(fsi, io)) + return; + + fsi_stream_stop(fsi, io); + fsi_hw_shutdown(fsi, dev); +} + +static void __fsi_resume(struct fsi_priv *fsi, + struct fsi_stream *io, + struct device *dev) +{ + if (!fsi_stream_is_working(fsi, io)) + return; + + fsi_hw_startup(fsi, io, dev); + fsi_stream_start(fsi, io); +} + +static int fsi_suspend(struct device *dev) +{ + struct fsi_master *master = dev_get_drvdata(dev); + struct fsi_priv *fsia = &master->fsia; + struct fsi_priv *fsib = &master->fsib; + + __fsi_suspend(fsia, &fsia->playback, dev); + __fsi_suspend(fsia, &fsia->capture, dev); + + __fsi_suspend(fsib, &fsib->playback, dev); + __fsi_suspend(fsib, &fsib->capture, dev); + + return 0; +} + +static int fsi_resume(struct device *dev) +{ + struct fsi_master *master = dev_get_drvdata(dev); + struct fsi_priv *fsia = &master->fsia; + struct fsi_priv *fsib = &master->fsib; + + __fsi_resume(fsia, &fsia->playback, dev); + __fsi_resume(fsia, &fsia->capture, dev); + + __fsi_resume(fsib, &fsib->playback, dev); + __fsi_resume(fsib, &fsib->capture, dev); + + return 0; +} + +static const struct dev_pm_ops fsi_pm_ops = { + .suspend = fsi_suspend, + .resume = fsi_resume, +}; + +static struct platform_driver fsi_driver = { + .driver = { + .name = "fsi-pcm-audio", + .pm = &fsi_pm_ops, + .of_match_table = fsi_of_match, + }, + .probe = fsi_probe, + .remove = fsi_remove, + .id_table = fsi_id_table, +}; + +module_platform_driver(fsi_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SuperH onchip FSI audio driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_ALIAS("platform:fsi-pcm-audio"); diff --git a/sound/soc/renesas/hac.c b/sound/soc/renesas/hac.c new file mode 100644 index 000000000000..db618c09d1e0 --- /dev/null +++ b/sound/soc/renesas/hac.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Hitachi Audio Controller (AC97) support for SH7760/SH7780 +// +// Copyright (c) 2007 Manuel Lauss +// +// dont forget to set IPSEL/OMSEL register bits (in your board code) to +// enable HAC output pins! + +/* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only + * the FIRST can be used since ASoC does not pass any information to the + * ac97_read/write() functions regarding WHICH unit to use. You'll have + * to edit the code a bit to use the other AC97 unit. --mlau + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* regs and bits */ +#define HACCR 0x08 +#define HACCSAR 0x20 +#define HACCSDR 0x24 +#define HACPCML 0x28 +#define HACPCMR 0x2C +#define HACTIER 0x50 +#define HACTSR 0x54 +#define HACRIER 0x58 +#define HACRSR 0x5C +#define HACACR 0x60 + +#define CR_CR (1 << 15) /* "codec-ready" indicator */ +#define CR_CDRT (1 << 11) /* cold reset */ +#define CR_WMRT (1 << 10) /* warm reset */ +#define CR_B9 (1 << 9) /* the mysterious "bit 9" */ +#define CR_ST (1 << 5) /* AC97 link start bit */ + +#define CSAR_RD (1 << 19) /* AC97 data read bit */ +#define CSAR_WR (0) + +#define TSR_CMDAMT (1 << 31) +#define TSR_CMDDMT (1 << 30) + +#define RSR_STARY (1 << 22) +#define RSR_STDRY (1 << 21) + +#define ACR_DMARX16 (1 << 30) +#define ACR_DMATX16 (1 << 29) +#define ACR_TX12ATOM (1 << 26) +#define ACR_DMARX20 ((1 << 24) | (1 << 22)) +#define ACR_DMATX20 ((1 << 23) | (1 << 21)) + +#define CSDR_SHIFT 4 +#define CSDR_MASK (0xffff << CSDR_SHIFT) +#define CSAR_SHIFT 12 +#define CSAR_MASK (0x7f << CSAR_SHIFT) + +#define AC97_WRITE_RETRY 1 +#define AC97_READ_RETRY 5 + +/* manual-suggested AC97 codec access timeouts (us) */ +#define TMO_E1 500 /* 21 < E1 < 1000 */ +#define TMO_E2 13 /* 13 < E2 */ +#define TMO_E3 21 /* 21 < E3 */ +#define TMO_E4 500 /* 21 < E4 < 1000 */ + +struct hac_priv { + unsigned long mmio; /* HAC base address */ +} hac_cpu_data[] = { +#if defined(CONFIG_CPU_SUBTYPE_SH7760) + { + .mmio = 0xFE240000, + }, + { + .mmio = 0xFE250000, + }, +#elif defined(CONFIG_CPU_SUBTYPE_SH7780) + { + .mmio = 0xFFE40000, + }, +#else +#error "Unsupported SuperH SoC" +#endif +}; + +#define HACREG(reg) (*(unsigned long *)(hac->mmio + (reg))) + +/* + * AC97 read/write flow as outlined in the SH7760 manual (pages 903-906) + */ +static int hac_get_codec_data(struct hac_priv *hac, unsigned short r, + unsigned short *v) +{ + unsigned int to1, to2, i; + unsigned short adr; + + for (i = AC97_READ_RETRY; i; i--) { + *v = 0; + /* wait for HAC to receive something from the codec */ + for (to1 = TMO_E4; + to1 && !(HACREG(HACRSR) & RSR_STARY); + --to1) + udelay(1); + for (to2 = TMO_E4; + to2 && !(HACREG(HACRSR) & RSR_STDRY); + --to2) + udelay(1); + + if (!to1 && !to2) + return 0; /* codec comm is down */ + + adr = ((HACREG(HACCSAR) & CSAR_MASK) >> CSAR_SHIFT); + *v = ((HACREG(HACCSDR) & CSDR_MASK) >> CSDR_SHIFT); + + HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); + + if (r == adr) + break; + + /* manual says: wait at least 21 usec before retrying */ + udelay(21); + } + HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); + return i; +} + +static unsigned short hac_read_codec_aux(struct hac_priv *hac, + unsigned short reg) +{ + unsigned short val; + unsigned int i, to; + + for (i = AC97_READ_RETRY; i; i--) { + /* send_read_request */ + local_irq_disable(); + HACREG(HACTSR) &= ~(TSR_CMDAMT); + HACREG(HACCSAR) = (reg << CSAR_SHIFT) | CSAR_RD; + local_irq_enable(); + + for (to = TMO_E3; + to && !(HACREG(HACTSR) & TSR_CMDAMT); + --to) + udelay(1); + + HACREG(HACTSR) &= ~TSR_CMDAMT; + val = 0; + if (hac_get_codec_data(hac, reg, &val) != 0) + break; + } + + return i ? val : ~0; +} + +static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + int unit_id = 0 /* ac97->private_data */; + struct hac_priv *hac = &hac_cpu_data[unit_id]; + unsigned int i, to; + /* write_codec_aux */ + for (i = AC97_WRITE_RETRY; i; i--) { + /* send_write_request */ + local_irq_disable(); + HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT); + HACREG(HACCSDR) = (val << CSDR_SHIFT); + HACREG(HACCSAR) = (reg << CSAR_SHIFT) & (~CSAR_RD); + local_irq_enable(); + + /* poll-wait for CMDAMT and CMDDMT */ + for (to = TMO_E1; + to && !(HACREG(HACTSR) & (TSR_CMDAMT|TSR_CMDDMT)); + --to) + udelay(1); + + HACREG(HACTSR) &= ~(TSR_CMDAMT | TSR_CMDDMT); + if (to) + break; + /* timeout, try again */ + } +} + +static unsigned short hac_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + int unit_id = 0 /* ac97->private_data */; + struct hac_priv *hac = &hac_cpu_data[unit_id]; + return hac_read_codec_aux(hac, reg); +} + +static void hac_ac97_warmrst(struct snd_ac97 *ac97) +{ + int unit_id = 0 /* ac97->private_data */; + struct hac_priv *hac = &hac_cpu_data[unit_id]; + unsigned int tmo; + + HACREG(HACCR) = CR_WMRT | CR_ST | CR_B9; + msleep(10); + HACREG(HACCR) = CR_ST | CR_B9; + for (tmo = 1000; (tmo > 0) && !(HACREG(HACCR) & CR_CR); tmo--) + udelay(1); + + if (!tmo) + printk(KERN_INFO "hac: reset: AC97 link down!\n"); + /* settings this bit lets us have a conversation with codec */ + HACREG(HACACR) |= ACR_TX12ATOM; +} + +static void hac_ac97_coldrst(struct snd_ac97 *ac97) +{ + int unit_id = 0 /* ac97->private_data */; + struct hac_priv *hac; + hac = &hac_cpu_data[unit_id]; + + HACREG(HACCR) = 0; + HACREG(HACCR) = CR_CDRT | CR_ST | CR_B9; + msleep(10); + hac_ac97_warmrst(ac97); +} + +static struct snd_ac97_bus_ops hac_ac97_ops = { + .read = hac_ac97_read, + .write = hac_ac97_write, + .reset = hac_ac97_coldrst, + .warm_reset = hac_ac97_warmrst, +}; + +static int hac_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hac_priv *hac = &hac_cpu_data[dai->id]; + int d = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + + switch (params->msbits) { + case 16: + HACREG(HACACR) |= d ? ACR_DMARX16 : ACR_DMATX16; + HACREG(HACACR) &= d ? ~ACR_DMARX20 : ~ACR_DMATX20; + break; + case 20: + HACREG(HACACR) &= d ? ~ACR_DMARX16 : ~ACR_DMATX16; + HACREG(HACACR) |= d ? ACR_DMARX20 : ACR_DMATX20; + break; + default: + pr_debug("hac: invalid depth %d bit\n", params->msbits); + return -EINVAL; + break; + } + + return 0; +} + +#define AC97_RATES \ + SNDRV_PCM_RATE_8000_192000 + +#define AC97_FMTS \ + SNDRV_PCM_FMTBIT_S16_LE + +static const struct snd_soc_dai_ops hac_dai_ops = { + .hw_params = hac_hw_params, +}; + +static struct snd_soc_dai_driver sh4_hac_dai[] = { +{ + .name = "hac-dai.0", + .playback = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &hac_dai_ops, +}, +#ifdef CONFIG_CPU_SUBTYPE_SH7760 +{ + .name = "hac-dai.1", + .id = 1, + .playback = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &hac_dai_ops, + +}, +#endif +}; + +static const struct snd_soc_component_driver sh4_hac_component = { + .name = "sh4-hac", + .legacy_dai_naming = 1, +}; + +static int hac_soc_platform_probe(struct platform_device *pdev) +{ + int ret; + + ret = snd_soc_set_ac97_ops(&hac_ac97_ops); + if (ret != 0) + return ret; + + return devm_snd_soc_register_component(&pdev->dev, &sh4_hac_component, + sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai)); +} + +static void hac_soc_platform_remove(struct platform_device *pdev) +{ + snd_soc_set_ac97_ops(NULL); +} + +static struct platform_driver hac_pcm_driver = { + .driver = { + .name = "hac-pcm-audio", + }, + + .probe = hac_soc_platform_probe, + .remove = hac_soc_platform_remove, +}; + +module_platform_driver(hac_pcm_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver"); +MODULE_AUTHOR("Manuel Lauss "); diff --git a/sound/soc/renesas/migor.c b/sound/soc/renesas/migor.c new file mode 100644 index 000000000000..5a0bc6edac0a --- /dev/null +++ b/sound/soc/renesas/migor.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC driver for Migo-R +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include "../codecs/wm8978.h" +#include "siu.h" + +/* Default 8000Hz sampling frequency */ +static unsigned long codec_freq = 8000 * 512; + +static unsigned int use_count; + +/* External clock, sourced from the codec at the SIUMCKB pin */ +static unsigned long siumckb_recalc(struct clk *clk) +{ + return codec_freq; +} + +static struct sh_clk_ops siumckb_clk_ops = { + .recalc = siumckb_recalc, +}; + +static struct clk siumckb_clk = { + .ops = &siumckb_clk_ops, + .rate = 0, /* initialised at run-time */ +}; + +static struct clk_lookup *siumckb_lookup; + +static int migor_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + int ret; + unsigned int rate = params_rate(params); + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 13000000, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_OPCLKRATE, rate * 512); + if (ret < 0) + return ret; + + codec_freq = rate * 512; + /* + * This propagates the parent frequency change to children and + * recalculates the frequency table + */ + clk_set_rate(&siumckb_clk, codec_freq); + dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq); + + ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), SIU_CLKB_EXT, + codec_freq / 2, SND_SOC_CLOCK_IN); + + if (!ret) + use_count++; + + return ret; +} + +static int migor_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + + if (use_count) { + use_count--; + + if (!use_count) + snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 0, + SND_SOC_CLOCK_IN); + } else { + dev_dbg(codec_dai->dev, "Unbalanced hw_free!\n"); + } + + return 0; +} + +static const struct snd_soc_ops migor_dai_ops = { + .hw_params = migor_hw_params, + .hw_free = migor_hw_free, +}; + +static const struct snd_soc_dapm_widget migor_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Onboard Microphone", NULL), + SND_SOC_DAPM_MIC("External Microphone", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Headphone output connected to LHP/RHP, enable OUT4 for VMID */ + { "Headphone", NULL, "OUT4 VMID" }, + { "OUT4 VMID", NULL, "LHP" }, + { "OUT4 VMID", NULL, "RHP" }, + + /* On-board microphone */ + { "RMICN", NULL, "Mic Bias" }, + { "RMICP", NULL, "Mic Bias" }, + { "Mic Bias", NULL, "Onboard Microphone" }, + + /* External microphone */ + { "LMICN", NULL, "Mic Bias" }, + { "LMICP", NULL, "Mic Bias" }, + { "Mic Bias", NULL, "External Microphone" }, +}; + +/* migor digital audio interface glue - connects codec <--> CPU */ +SND_SOC_DAILINK_DEFS(wm8978, + DAILINK_COMP_ARRAY(COMP_CPU("siu-pcm-audio")), + DAILINK_COMP_ARRAY(COMP_CODEC("wm8978.0-001a", "wm8978-hifi")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("siu-pcm-audio"))); + +static struct snd_soc_dai_link migor_dai = { + .name = "wm8978", + .stream_name = "WM8978", + .dai_fmt = SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &migor_dai_ops, + SND_SOC_DAILINK_REG(wm8978), +}; + +/* migor audio machine driver */ +static struct snd_soc_card snd_soc_migor = { + .name = "Migo-R", + .owner = THIS_MODULE, + .dai_link = &migor_dai, + .num_links = 1, + + .dapm_widgets = migor_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(migor_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static struct platform_device *migor_snd_device; + +static int __init migor_init(void) +{ + int ret; + + ret = clk_register(&siumckb_clk); + if (ret < 0) + return ret; + + siumckb_lookup = clkdev_create(&siumckb_clk, "siumckb_clk", NULL); + if (!siumckb_lookup) { + ret = -ENOMEM; + goto eclkdevalloc; + } + + /* Port number used on this machine: port B */ + migor_snd_device = platform_device_alloc("soc-audio", 1); + if (!migor_snd_device) { + ret = -ENOMEM; + goto epdevalloc; + } + + platform_set_drvdata(migor_snd_device, &snd_soc_migor); + + ret = platform_device_add(migor_snd_device); + if (ret) + goto epdevadd; + + return 0; + +epdevadd: + platform_device_put(migor_snd_device); +epdevalloc: + clkdev_drop(siumckb_lookup); +eclkdevalloc: + clk_unregister(&siumckb_clk); + return ret; +} + +static void __exit migor_exit(void) +{ + clkdev_drop(siumckb_lookup); + clk_unregister(&siumckb_clk); + platform_device_unregister(migor_snd_device); +} + +module_init(migor_init); +module_exit(migor_exit); + +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_DESCRIPTION("ALSA SoC Migor"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/renesas/rcar/Makefile b/sound/soc/renesas/rcar/Makefile new file mode 100644 index 000000000000..45eb875a912a --- /dev/null +++ b/sound/soc/renesas/rcar/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-soc-rcar-y := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o debugfs.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c new file mode 100644 index 000000000000..0f190abf00e7 --- /dev/null +++ b/sound/soc/renesas/rcar/adg.c @@ -0,0 +1,775 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Helper routines for R-Car sound ADG. +// +// Copyright (C) 2013 Kuninori Morimoto +#include +#include +#include "rsnd.h" + +#define CLKA 0 +#define CLKB 1 +#define CLKC 2 +#define CLKI 3 +#define CLKINMAX 4 + +#define CLKOUT 0 +#define CLKOUT1 1 +#define CLKOUT2 2 +#define CLKOUT3 3 +#define CLKOUTMAX 4 + +#define BRRx_MASK(x) (0x3FF & x) + +static struct rsnd_mod_ops adg_ops = { + .name = "adg", +}; + +#define ADG_HZ_441 0 +#define ADG_HZ_48 1 +#define ADG_HZ_SIZE 2 + +struct rsnd_adg { + struct clk *clkin[CLKINMAX]; + struct clk *clkout[CLKOUTMAX]; + struct clk *null_clk; + struct clk_onecell_data onecell; + struct rsnd_mod mod; + int clkin_rate[CLKINMAX]; + int clkin_size; + int clkout_size; + u32 ckr; + u32 brga; + u32 brgb; + + int brg_rate[ADG_HZ_SIZE]; /* BRGA / BRGB */ +}; + +#define for_each_rsnd_clkin(pos, adg, i) \ + for (i = 0; \ + (i < adg->clkin_size) && \ + ((pos) = adg->clkin[i]); \ + i++) +#define for_each_rsnd_clkout(pos, adg, i) \ + for (i = 0; \ + (i < adg->clkout_size) && \ + ((pos) = adg->clkout[i]); \ + i++) +#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) + +static const char * const clkin_name_gen4[] = { + [CLKA] = "clkin", +}; + +static const char * const clkin_name_gen2[] = { + [CLKA] = "clk_a", + [CLKB] = "clk_b", + [CLKC] = "clk_c", + [CLKI] = "clk_i", +}; + +static const char * const clkout_name_gen2[] = { + [CLKOUT] = "audio_clkout", + [CLKOUT1] = "audio_clkout1", + [CLKOUT2] = "audio_clkout2", + [CLKOUT3] = "audio_clkout3", +}; + +static u32 rsnd_adg_calculate_brgx(unsigned long div) +{ + int i; + + if (!div) + return 0; + + for (i = 3; i >= 0; i--) { + int ratio = 2 << (i * 2); + if (0 == (div % ratio)) + return (u32)((i << 8) | ((div / ratio) - 1)); + } + + return ~0; +} + +static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + int id = rsnd_mod_id(ssi_mod); + int ws = id; + + if (rsnd_ssi_is_pin_sharing(io)) { + switch (id) { + case 1: + case 2: + case 9: + ws = 0; + break; + case 4: + ws = 3; + break; + case 8: + ws = 7; + break; + } + } else { + /* + * SSI8 is not connected to ADG. + * Thus SSI9 is using ws = 8 + */ + if (id == 9) + ws = 8; + } + + return (0x6 + ws) << 8; +} + +static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + unsigned int target_rate, + unsigned int *target_val, + unsigned int *target_en) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int sel; + unsigned int val, en; + unsigned int min, diff; + unsigned int sel_rate[] = { + adg->clkin_rate[CLKA], /* 0000: CLKA */ + adg->clkin_rate[CLKB], /* 0001: CLKB */ + adg->clkin_rate[CLKC], /* 0010: CLKC */ + adg->brg_rate[ADG_HZ_441], /* 0011: BRGA */ + adg->brg_rate[ADG_HZ_48], /* 0100: BRGB */ + }; + + min = ~0; + val = 0; + en = 0; + for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { + int idx = 0; + int step = 2; + int div; + + if (!sel_rate[sel]) + continue; + + for (div = 2; div <= 98304; div += step) { + diff = abs(target_rate - sel_rate[sel] / div); + if (min > diff) { + val = (sel << 8) | idx; + min = diff; + en = 1 << (sel + 1); /* fixme */ + } + + /* + * step of 0_0000 / 0_0001 / 0_1101 + * are out of order + */ + if ((idx > 2) && (idx % 2)) + step *= 2; + if (idx == 0x1c) { + div += step; + step *= 2; + } + idx++; + } + } + + if (min == ~0) { + dev_err(dev, "no Input clock\n"); + return; + } + + *target_val = val; + if (target_en) + *target_en = en; +} + +static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate, + u32 *in, u32 *out, u32 *en) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + unsigned int target_rate; + u32 *target_val; + u32 _in; + u32 _out; + u32 _en; + + /* default = SSI WS */ + _in = + _out = rsnd_adg_ssi_ws_timing_gen2(io); + + target_rate = 0; + target_val = NULL; + _en = 0; + if (runtime->rate != in_rate) { + target_rate = out_rate; + target_val = &_out; + } else if (runtime->rate != out_rate) { + target_rate = in_rate; + target_val = &_in; + } + + if (target_rate) + __rsnd_adg_get_timesel_ratio(priv, io, + target_rate, + target_val, &_en); + + if (in) + *in = _in; + if (out) + *out = _out; + if (en) + *en = _en; +} + +int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + int id = rsnd_mod_id(cmd_mod); + int shift = (id % 2) ? 16 : 0; + u32 mask, val; + + rsnd_adg_get_timesel_ratio(priv, io, + rsnd_src_get_in_rate(priv, io), + rsnd_src_get_out_rate(priv, io), + NULL, &val, NULL); + + val = val << shift; + mask = 0x0f1f << shift; + + rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val); + + return 0; +} + +int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + u32 in, out; + u32 mask, en; + int id = rsnd_mod_id(src_mod); + int shift = (id % 2) ? 16 : 0; + + rsnd_mod_make_sure(src_mod, RSND_MOD_SRC); + + rsnd_adg_get_timesel_ratio(priv, io, + in_rate, out_rate, + &in, &out, &en); + + in = in << shift; + out = out << shift; + mask = 0x0f1f << shift; + + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL(id / 2), mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL(id / 2), mask, out); + + if (en) + rsnd_mod_bset(adg_mod, DIV_EN, en, en); + + return 0; +} + +static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + struct device *dev = rsnd_priv_to_dev(priv); + int id = rsnd_mod_id(ssi_mod); + int shift = (id % 4) * 8; + u32 mask = 0xFF << shift; + + rsnd_mod_make_sure(ssi_mod, RSND_MOD_SSI); + + val = val << shift; + + /* + * SSI 8 is not connected to ADG. + * it works with SSI 7 + */ + if (id == 8) + return; + + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL(id / 4), mask, val); + + dev_dbg(dev, "AUDIO_CLK_SEL is 0x%x\n", val); +} + +int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct clk *clk; + int i; + int sel_table[] = { + [CLKA] = 0x1, + [CLKB] = 0x2, + [CLKC] = 0x3, + [CLKI] = 0x0, + }; + + /* + * find suitable clock from + * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. + */ + for_each_rsnd_clkin(clk, adg, i) + if (rate == adg->clkin_rate[i]) + return sel_table[i]; + + /* + * find divided clock from BRGA/BRGB + */ + if (rate == adg->brg_rate[ADG_HZ_441]) + return 0x10; + + if (rate == adg->brg_rate[ADG_HZ_48]) + return 0x20; + + return -EIO; +} + +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod) +{ + rsnd_adg_set_ssi_clk(ssi_mod, 0); + + return 0; +} + +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + int data; + u32 ckr = 0; + + data = rsnd_adg_clk_query(priv, rate); + if (data < 0) + return data; + + rsnd_adg_set_ssi_clk(ssi_mod, data); + + if (0 == (rate % 8000)) + ckr = 0x80000000; /* BRGB output = 48kHz */ + + rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr); + + dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n", + (ckr) ? 'B' : 'A', + (ckr) ? adg->brg_rate[ADG_HZ_48] : + adg->brg_rate[ADG_HZ_441]); + + return 0; +} + +void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + struct clk *clk; + int i; + + if (enable) { + rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr); + rsnd_mod_write(adg_mod, BRRA, adg->brga); + rsnd_mod_write(adg_mod, BRRB, adg->brgb); + } + + for_each_rsnd_clkin(clk, adg, i) { + if (enable) { + clk_prepare_enable(clk); + + /* + * We shouldn't use clk_get_rate() under + * atomic context. Let's keep it when + * rsnd_adg_clk_enable() was called + */ + adg->clkin_rate[i] = clk_get_rate(clk); + } else { + clk_disable_unprepare(clk); + } + } +} + +static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv, + const char * const name, + const char *parent) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + + clk = clk_register_fixed_rate(dev, name, parent, 0, 0); + if (IS_ERR_OR_NULL(clk)) { + dev_err(dev, "create null clk error\n"); + return ERR_CAST(clk); + } + + return clk; +} + +static struct clk *rsnd_adg_null_clk_get(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = priv->adg; + + if (!adg->null_clk) { + static const char * const name = "rsnd_adg_null"; + + adg->null_clk = rsnd_adg_create_null_clk(priv, name, NULL); + } + + return adg->null_clk; +} + +static void rsnd_adg_null_clk_clean(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = priv->adg; + + if (adg->null_clk) + clk_unregister_fixed_rate(adg->null_clk); +} + +static int rsnd_adg_get_clkin(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = priv->adg; + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + const char * const *clkin_name; + int clkin_size; + int i; + + clkin_name = clkin_name_gen2; + clkin_size = ARRAY_SIZE(clkin_name_gen2); + if (rsnd_is_gen4(priv)) { + clkin_name = clkin_name_gen4; + clkin_size = ARRAY_SIZE(clkin_name_gen4); + } + + for (i = 0; i < clkin_size; i++) { + clk = devm_clk_get(dev, clkin_name[i]); + + if (IS_ERR_OR_NULL(clk)) + clk = rsnd_adg_null_clk_get(priv); + if (IS_ERR_OR_NULL(clk)) + goto err; + + adg->clkin[i] = clk; + } + + adg->clkin_size = clkin_size; + + return 0; + +err: + dev_err(dev, "adg clock IN get failed\n"); + + rsnd_adg_null_clk_clean(priv); + + return -EIO; +} + +static void rsnd_adg_unregister_clkout(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = priv->adg; + struct clk *clk; + int i; + + for_each_rsnd_clkout(clk, adg, i) + clk_unregister_fixed_rate(clk); +} + +static int rsnd_adg_get_clkout(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = priv->adg; + struct clk *clk; + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np = dev->of_node; + struct property *prop; + u32 ckr, brgx, brga, brgb; + u32 req_rate[ADG_HZ_SIZE] = {}; + uint32_t count = 0; + unsigned long req_Hz[ADG_HZ_SIZE]; + int clkout_size; + int i, req_size; + int approximate = 0; + const char *parent_clk_name = NULL; + const char * const *clkout_name; + int brg_table[] = { + [CLKA] = 0x0, + [CLKB] = 0x1, + [CLKC] = 0x4, + [CLKI] = 0x2, + }; + + ckr = 0; + brga = 0xff; /* default */ + brgb = 0xff; /* default */ + + /* + * ADG supports BRRA/BRRB output only + * this means all clkout0/1/2/3 will be same rate + */ + prop = of_find_property(np, "clock-frequency", NULL); + if (!prop) + goto rsnd_adg_get_clkout_end; + + req_size = prop->length / sizeof(u32); + if (req_size > ADG_HZ_SIZE) { + dev_err(dev, "too many clock-frequency\n"); + return -EINVAL; + } + + of_property_read_u32_array(np, "clock-frequency", req_rate, req_size); + req_Hz[ADG_HZ_48] = 0; + req_Hz[ADG_HZ_441] = 0; + for (i = 0; i < req_size; i++) { + if (0 == (req_rate[i] % 44100)) + req_Hz[ADG_HZ_441] = req_rate[i]; + if (0 == (req_rate[i] % 48000)) + req_Hz[ADG_HZ_48] = req_rate[i]; + } + + /* + * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC + * have 44.1kHz or 48kHz base clocks for now. + * + * SSI itself can divide parent clock by 1/1 - 1/16 + * see + * rsnd_adg_ssi_clk_try_start() + * rsnd_ssi_master_clk_start() + */ + + /* + * [APPROXIMATE] + * + * clk_i (internal clock) can't create accurate rate, it will be approximate rate. + * + * + * + * clk_i needs x2 of required maximum rate. + * see + * - Minimum division of BRRA/BRRB + * - rsnd_ssi_clk_query() + * + * Sample Settings for TDM 8ch, 32bit width + * + * 8(ch) x 32(bit) x 44100(Hz) x 2 = 22579200 + * 8(ch) x 32(bit) x 48000(Hz) x 2 = 24576000 + * + * clock-frequency = <22579200 24576000>; + */ + for_each_rsnd_clkin(clk, adg, i) { + u32 rate, div; + + rate = clk_get_rate(clk); + + if (0 == rate) /* not used */ + continue; + + /* BRGA */ + + if (i == CLKI) + /* see [APPROXIMATE] */ + rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_441]) * req_Hz[ADG_HZ_441]; + if (!adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441] && (0 == rate % 44100)) { + div = rate / req_Hz[ADG_HZ_441]; + brgx = rsnd_adg_calculate_brgx(div); + if (BRRx_MASK(brgx) == brgx) { + brga = brgx; + adg->brg_rate[ADG_HZ_441] = rate / div; + ckr |= brg_table[i] << 20; + if (req_Hz[ADG_HZ_441]) + parent_clk_name = __clk_get_name(clk); + if (i == CLKI) + approximate = 1; + } + } + + /* BRGB */ + + if (i == CLKI) + /* see [APPROXIMATE] */ + rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_48]) * req_Hz[ADG_HZ_48]; + if (!adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48] && (0 == rate % 48000)) { + div = rate / req_Hz[ADG_HZ_48]; + brgx = rsnd_adg_calculate_brgx(div); + if (BRRx_MASK(brgx) == brgx) { + brgb = brgx; + adg->brg_rate[ADG_HZ_48] = rate / div; + ckr |= brg_table[i] << 16; + if (req_Hz[ADG_HZ_48]) + parent_clk_name = __clk_get_name(clk); + if (i == CLKI) + approximate = 1; + } + } + } + + if (!(adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48]) && + !(adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441])) + goto rsnd_adg_get_clkout_end; + + if (approximate) + dev_info(dev, "It uses CLK_I as approximate rate"); + + clkout_name = clkout_name_gen2; + clkout_size = ARRAY_SIZE(clkout_name_gen2); + if (rsnd_is_gen4(priv)) + clkout_size = 1; /* reuse clkout_name_gen2[] */ + + /* + * ADG supports BRRA/BRRB output only. + * this means all clkout0/1/2/3 will be * same rate + */ + + of_property_read_u32(np, "#clock-cells", &count); + /* + * for clkout + */ + if (!count) { + clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT], + parent_clk_name, 0, req_rate[0]); + if (IS_ERR_OR_NULL(clk)) + goto err; + + adg->clkout[CLKOUT] = clk; + adg->clkout_size = 1; + of_clk_add_provider(np, of_clk_src_simple_get, clk); + } + /* + * for clkout0/1/2/3 + */ + else { + for (i = 0; i < clkout_size; i++) { + clk = clk_register_fixed_rate(dev, clkout_name[i], + parent_clk_name, 0, + req_rate[0]); + if (IS_ERR_OR_NULL(clk)) + goto err; + + adg->clkout[i] = clk; + } + adg->onecell.clks = adg->clkout; + adg->onecell.clk_num = clkout_size; + adg->clkout_size = clkout_size; + of_clk_add_provider(np, of_clk_src_onecell_get, + &adg->onecell); + } + +rsnd_adg_get_clkout_end: + adg->ckr = ckr; + adg->brga = brga; + adg->brgb = brgb; + + return 0; + +err: + dev_err(dev, "adg clock OUT get failed\n"); + + rsnd_adg_unregister_clkout(priv); + + return -EIO; +} + +#if defined(DEBUG) || defined(CONFIG_DEBUG_FS) +__printf(3, 4) +static void dbg_msg(struct device *dev, struct seq_file *m, + const char *fmt, ...) +{ + char msg[128]; + va_list args; + + va_start(args, fmt); + vsnprintf(msg, sizeof(msg), fmt, args); + va_end(args); + + if (m) + seq_puts(m, msg); + else + dev_dbg(dev, "%s", msg); +} + +void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + int i; + + for_each_rsnd_clkin(clk, adg, i) + dbg_msg(dev, m, "%-18s : %pa : %ld\n", + __clk_get_name(clk), clk, clk_get_rate(clk)); + + dbg_msg(dev, m, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", + adg->ckr, adg->brga, adg->brgb); + dbg_msg(dev, m, "BRGA (for 44100 base) = %d\n", adg->brg_rate[ADG_HZ_441]); + dbg_msg(dev, m, "BRGB (for 48000 base) = %d\n", adg->brg_rate[ADG_HZ_48]); + + /* + * Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start() + * by BRGCKR::BRGCKR_31 + */ + for_each_rsnd_clkout(clk, adg, i) + dbg_msg(dev, m, "%-18s : %pa : %ld\n", + __clk_get_name(clk), clk, clk_get_rate(clk)); +} +#else +#define rsnd_adg_clk_dbg_info(priv, m) +#endif + +int rsnd_adg_probe(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg; + struct device *dev = rsnd_priv_to_dev(priv); + int ret; + + adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); + if (!adg) + return -ENOMEM; + + ret = rsnd_mod_init(priv, &adg->mod, &adg_ops, + NULL, 0, 0); + if (ret) + return ret; + + priv->adg = adg; + + ret = rsnd_adg_get_clkin(priv); + if (ret) + return ret; + + ret = rsnd_adg_get_clkout(priv); + if (ret) + return ret; + + rsnd_adg_clk_enable(priv); + rsnd_adg_clk_dbg_info(priv, NULL); + + return 0; +} + +void rsnd_adg_remove(struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np = dev->of_node; + + rsnd_adg_unregister_clkout(priv); + + of_clk_del_provider(np); + + rsnd_adg_clk_disable(priv); + + /* It should be called after rsnd_adg_clk_disable() */ + rsnd_adg_null_clk_clean(priv); +} diff --git a/sound/soc/renesas/rcar/cmd.c b/sound/soc/renesas/rcar/cmd.c new file mode 100644 index 000000000000..8d9a1e345a22 --- /dev/null +++ b/sound/soc/renesas/rcar/cmd.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car CMD support +// +// Copyright (C) 2015 Renesas Solutions Corp. +// Kuninori Morimoto + +#include "rsnd.h" + +struct rsnd_cmd { + struct rsnd_mod mod; +}; + +#define CMD_NAME "cmd" + +#define rsnd_cmd_nr(priv) ((priv)->cmd_nr) +#define for_each_rsnd_cmd(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_cmd_nr(priv)) && \ + ((pos) = (struct rsnd_cmd *)(priv)->cmd + i); \ + i++) + +static int rsnd_cmd_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); + struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); + struct device *dev = rsnd_priv_to_dev(priv); + u32 data; + static const u32 path[] = { + [1] = 1 << 0, + [5] = 1 << 8, + [6] = 1 << 12, + [9] = 1 << 15, + }; + + if (!mix && !dvc) + return 0; + + if (ARRAY_SIZE(path) < rsnd_mod_id(mod) + 1) + return -ENXIO; + + if (mix) { + struct rsnd_dai *rdai; + int i; + + /* + * it is assuming that integrater is well understanding about + * data path. Here doesn't check impossible connection, + * like src2 + src5 + */ + data = 0; + for_each_rsnd_dai(rdai, priv, i) { + struct rsnd_dai_stream *tio = &rdai->playback; + struct rsnd_mod *src = rsnd_io_to_mod_src(tio); + + if (mix == rsnd_io_to_mod_mix(tio)) + data |= path[rsnd_mod_id(src)]; + + tio = &rdai->capture; + src = rsnd_io_to_mod_src(tio); + if (mix == rsnd_io_to_mod_mix(tio)) + data |= path[rsnd_mod_id(src)]; + } + + } else { + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + + static const u8 cmd_case[] = { + [0] = 0x3, + [1] = 0x3, + [2] = 0x4, + [3] = 0x1, + [4] = 0x2, + [5] = 0x4, + [6] = 0x1, + [9] = 0x2, + }; + + if (unlikely(!src)) + return -EIO; + + data = path[rsnd_mod_id(src)] | + cmd_case[rsnd_mod_id(src)] << 16; + } + + dev_dbg(dev, "ctu/mix path = 0x%08x\n", data); + + rsnd_mod_write(mod, CMD_ROUTE_SLCT, data); + rsnd_mod_write(mod, CMD_BUSIF_MODE, rsnd_get_busif_shift(io, mod) | 1); + rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); + + rsnd_adg_set_cmd_timsel_gen2(mod, io); + + return 0; +} + +static int rsnd_cmd_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_write(mod, CMD_CTRL, 0x10); + + return 0; +} + +static int rsnd_cmd_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_write(mod, CMD_CTRL, 0); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void rsnd_cmd_debug_info(struct seq_file *m, + struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, + 0x180 + rsnd_mod_id_raw(mod) * 0x20, 0x30); +} +#define DEBUG_INFO .debug_info = rsnd_cmd_debug_info +#else +#define DEBUG_INFO +#endif + +static struct rsnd_mod_ops rsnd_cmd_ops = { + .name = CMD_NAME, + .init = rsnd_cmd_init, + .start = rsnd_cmd_start, + .stop = rsnd_cmd_stop, + .get_status = rsnd_mod_get_status, + DEBUG_INFO +}; + +static struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv))) + id = 0; + + return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id); +} +int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct rsnd_mod *mod = rsnd_cmd_mod_get(priv, id); + + return rsnd_dai_connect(mod, io, mod->type); +} + +int rsnd_cmd_probe(struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_cmd *cmd; + int i, nr; + + /* same number as DVC */ + nr = priv->dvc_nr; + if (!nr) + return 0; + + cmd = devm_kcalloc(dev, nr, sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + priv->cmd_nr = nr; + priv->cmd = cmd; + + for_each_rsnd_cmd(cmd, priv, i) { + int ret = rsnd_mod_init(priv, rsnd_mod_get(cmd), + &rsnd_cmd_ops, NULL, + RSND_MOD_CMD, i); + if (ret) + return ret; + } + + return 0; +} + +void rsnd_cmd_remove(struct rsnd_priv *priv) +{ + struct rsnd_cmd *cmd; + int i; + + for_each_rsnd_cmd(cmd, priv, i) { + rsnd_mod_quit(rsnd_mod_get(cmd)); + } +} diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c new file mode 100644 index 000000000000..c32e88d6a141 --- /dev/null +++ b/sound/soc/renesas/rcar/core.c @@ -0,0 +1,2112 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SRU/SCU/SSIU/SSI support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto +// +// Based on fsi.c +// Kuninori Morimoto + +/* + * Renesas R-Car sound device structure + * + * Gen1 + * + * SRU : Sound Routing Unit + * - SRC : Sampling Rate Converter + * - CMD + * - CTU : Channel Count Conversion Unit + * - MIX : Mixer + * - DVC : Digital Volume and Mute Function + * - SSI : Serial Sound Interface + * + * Gen2 + * + * SCU : Sampling Rate Converter Unit + * - SRC : Sampling Rate Converter + * - CMD + * - CTU : Channel Count Conversion Unit + * - MIX : Mixer + * - DVC : Digital Volume and Mute Function + * SSIU : Serial Sound Interface Unit + * - SSI : Serial Sound Interface + */ + +/* + * driver data Image + * + * rsnd_priv + * | + * | ** this depends on Gen1/Gen2 + * | + * +- gen + * | + * | ** these depend on data path + * | ** gen and platform data control it + * | + * +- rdai[0] + * | | sru ssiu ssi + * | +- playback -> [mod] -> [mod] -> [mod] -> ... + * | | + * | | sru ssiu ssi + * | +- capture -> [mod] -> [mod] -> [mod] -> ... + * | + * +- rdai[1] + * | | sru ssiu ssi + * | +- playback -> [mod] -> [mod] -> [mod] -> ... + * | | + * | | sru ssiu ssi + * | +- capture -> [mod] -> [mod] -> [mod] -> ... + * ... + * | + * | ** these control ssi + * | + * +- ssi + * | | + * | +- ssi[0] + * | +- ssi[1] + * | +- ssi[2] + * | ... + * | + * | ** these control src + * | + * +- src + * | + * +- src[0] + * +- src[1] + * +- src[2] + * ... + * + * + * for_each_rsnd_dai(xx, priv, xx) + * rdai[0] => rdai[1] => rdai[2] => ... + * + * for_each_rsnd_mod(xx, rdai, xx) + * [mod] => [mod] => [mod] => ... + * + * rsnd_dai_call(xxx, fn ) + * [mod]->fn() -> [mod]->fn() -> [mod]->fn()... + * + */ + +#include +#include +#include "rsnd.h" + +#define RSND_RATES SNDRV_PCM_RATE_8000_192000 +#define RSND_FMTS (SNDRV_PCM_FMTBIT_S8 |\ + SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct of_device_id rsnd_of_match[] = { + { .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 }, + { .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 }, + { .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN3 }, + { .compatible = "renesas,rcar_sound-gen4", .data = (void *)RSND_GEN4 }, + /* Special Handling */ + { .compatible = "renesas,rcar_sound-r8a77990", .data = (void *)(RSND_GEN3 | RSND_SOC_E) }, + {}, +}; +MODULE_DEVICE_TABLE(of, rsnd_of_match); + +/* + * rsnd_mod functions + */ +void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) +{ + if (mod->type != type) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_warn(dev, "%s is not your expected module\n", + rsnd_mod_name(mod)); + } +} + +struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + if (!mod || !mod->ops || !mod->ops->dma_req) + return NULL; + + return mod->ops->dma_req(io, mod); +} + +#define MOD_NAME_NUM 5 +#define MOD_NAME_SIZE 16 +char *rsnd_mod_name(struct rsnd_mod *mod) +{ + static char names[MOD_NAME_NUM][MOD_NAME_SIZE]; + static int num; + char *name = names[num]; + + num++; + if (num >= MOD_NAME_NUM) + num = 0; + + /* + * Let's use same char to avoid pointlessness memory + * Thus, rsnd_mod_name() should be used immediately + * Don't keep pointer + */ + if ((mod)->ops->id_sub) { + snprintf(name, MOD_NAME_SIZE, "%s[%d%d]", + mod->ops->name, + rsnd_mod_id(mod), + rsnd_mod_id_sub(mod)); + } else { + snprintf(name, MOD_NAME_SIZE, "%s[%d]", + mod->ops->name, + rsnd_mod_id(mod)); + } + + return name; +} + +u32 *rsnd_mod_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) +{ + return &mod->status; +} + +int rsnd_mod_id_raw(struct rsnd_mod *mod) +{ + return mod->id; +} + +int rsnd_mod_id(struct rsnd_mod *mod) +{ + if ((mod)->ops->id) + return (mod)->ops->id(mod); + + return rsnd_mod_id_raw(mod); +} + +int rsnd_mod_id_sub(struct rsnd_mod *mod) +{ + if ((mod)->ops->id_sub) + return (mod)->ops->id_sub(mod); + + return 0; +} + +int rsnd_mod_init(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_mod_ops *ops, + struct clk *clk, + enum rsnd_mod_type type, + int id) +{ + int ret = clk_prepare(clk); + + if (ret) + return ret; + + mod->id = id; + mod->ops = ops; + mod->type = type; + mod->clk = clk; + mod->priv = priv; + + return 0; +} + +void rsnd_mod_quit(struct rsnd_mod *mod) +{ + clk_unprepare(mod->clk); + mod->clk = NULL; +} + +void rsnd_mod_interrupt(struct rsnd_mod *mod, + void (*callback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io)) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dai *rdai; + int i; + + for_each_rsnd_dai(rdai, priv, i) { + struct rsnd_dai_stream *io = &rdai->playback; + + if (mod == io->mod[mod->type]) + callback(mod, io); + + io = &rdai->capture; + if (mod == io->mod[mod->type]) + callback(mod, io); + } +} + +int rsnd_io_is_working(struct rsnd_dai_stream *io) +{ + /* see rsnd_dai_stream_init/quit() */ + if (io->substream) + return snd_pcm_running(io->substream); + + return 0; +} + +int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + /* + * params will be added when refine + * see + * __rsnd_soc_hw_rule_rate() + * __rsnd_soc_hw_rule_channels() + */ + if (params) + return params_channels(params); + else if (runtime) + return runtime->channels; + return 0; +} + +int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params) +{ + int chan = rsnd_runtime_channel_original_with_params(io, params); + struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io); + + if (ctu_mod) { + u32 converted_chan = rsnd_io_converted_chan(io); + + /* + * !! Note !! + * + * converted_chan will be used for CTU, + * or TDM Split mode. + * User shouldn't use CTU with TDM Split mode. + */ + if (rsnd_runtime_is_tdm_split(io)) { + struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); + + dev_err(dev, "CTU and TDM Split should be used\n"); + } + + if (converted_chan) + return converted_chan; + } + + return chan; +} + +int rsnd_channel_normalization(int chan) +{ + if (WARN_ON((chan > 8) || (chan < 0))) + return 0; + + /* TDM Extend Mode needs 8ch */ + if (chan == 6) + chan = 8; + + return chan; +} + +int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + int chan = rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu_with_params(io, params) : + rsnd_runtime_channel_original_with_params(io, params); + + /* Use Multi SSI */ + if (rsnd_runtime_is_multi_ssi(io)) + chan /= rsnd_rdai_ssi_lane_get(rdai); + + return rsnd_channel_normalization(chan); +} + +int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + int lane = rsnd_rdai_ssi_lane_get(rdai); + int chan = rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io); + + return (chan > 2) && (lane > 1); +} + +int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io) +{ + return rsnd_runtime_channel_for_ssi(io) >= 6; +} + +int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io) +{ + return !!rsnd_flags_has(io, RSND_STREAM_TDM_SPLIT); +} + +/* + * ADINR function + */ +u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct device *dev = rsnd_priv_to_dev(priv); + + switch (snd_pcm_format_width(runtime->format)) { + case 8: + return 16 << 16; + case 16: + return 8 << 16; + case 24: + return 0 << 16; + } + + dev_warn(dev, "not supported sample bits\n"); + + return 0; +} + +/* + * DALIGN function + */ +u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) +{ + static const u32 dalign_values[8] = { + 0x76543210, 0x00000032, 0x00007654, 0x00000076, + 0xfedcba98, 0x000000ba, 0x0000fedc, 0x000000fe, + }; + int id = 0; + struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); + struct rsnd_mod *target; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 dalign; + + /* + * *Hardware* L/R and *Software* L/R are inverted for 16bit data. + * 31..16 15...0 + * HW: [L ch] [R ch] + * SW: [R ch] [L ch] + * We need to care about inversion timing to control + * Playback/Capture correctly. + * The point is [DVC] needs *Hardware* L/R, [MEM] needs *Software* L/R + * + * sL/R : software L/R + * hL/R : hardware L/R + * (*) : conversion timing + * + * Playback + * sL/R (*) hL/R hL/R hL/R hL/R hL/R + * [MEM] -> [SRC] -> [DVC] -> [CMD] -> [SSIU] -> [SSI] -> codec + * + * Capture + * hL/R hL/R hL/R hL/R hL/R (*) sL/R + * codec -> [SSI] -> [SSIU] -> [SRC] -> [DVC] -> [CMD] -> [MEM] + */ + if (rsnd_io_is_play(io)) { + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + + target = src ? src : ssiu; + } else { + struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io); + + target = cmd ? cmd : ssiu; + } + + if (mod == ssiu) + id = rsnd_mod_id_sub(mod); + + dalign = dalign_values[id]; + + if (mod == target && snd_pcm_format_width(runtime->format) == 16) { + /* Target mod needs inverted DALIGN when 16bit */ + dalign = (dalign & 0xf0f0f0f0) >> 4 | + (dalign & 0x0f0f0f0f) << 4; + } + + return dalign; +} + +u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod) +{ + static const enum rsnd_mod_type playback_mods[] = { + RSND_MOD_SRC, + RSND_MOD_CMD, + RSND_MOD_SSIU, + }; + static const enum rsnd_mod_type capture_mods[] = { + RSND_MOD_CMD, + RSND_MOD_SRC, + RSND_MOD_SSIU, + }; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_mod *tmod = NULL; + const enum rsnd_mod_type *mods = + rsnd_io_is_play(io) ? + playback_mods : capture_mods; + int i; + + /* + * This is needed for 24bit data + * We need to shift 8bit + * + * Linux 24bit data is located as 0x00****** + * HW 24bit data is located as 0x******00 + * + */ + if (snd_pcm_format_width(runtime->format) != 24) + return 0; + + for (i = 0; i < ARRAY_SIZE(playback_mods); i++) { + tmod = rsnd_io_to_mod(io, mods[i]); + if (tmod) + break; + } + + if (tmod != mod) + return 0; + + if (rsnd_io_is_play(io)) + return (0 << 20) | /* shift to Left */ + (8 << 16); /* 8bit */ + else + return (1 << 20) | /* shift to Right */ + (8 << 16); /* 8bit */ +} + +/* + * rsnd_dai functions + */ +struct rsnd_mod *rsnd_mod_next(int *iterator, + struct rsnd_dai_stream *io, + enum rsnd_mod_type *array, + int array_size) +{ + int max = array ? array_size : RSND_MOD_MAX; + + for (; *iterator < max; (*iterator)++) { + enum rsnd_mod_type type = (array) ? array[*iterator] : *iterator; + struct rsnd_mod *mod = rsnd_io_to_mod(io, type); + + if (mod) + return mod; + } + + return NULL; +} + +static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = { + { + /* CAPTURE */ + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + RSND_MOD_SSIU, + RSND_MOD_SSIM3, + RSND_MOD_SSIM2, + RSND_MOD_SSIM1, + RSND_MOD_SSIP, + RSND_MOD_SSI, + }, { + /* PLAYBACK */ + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_SSIM3, + RSND_MOD_SSIM2, + RSND_MOD_SSIM1, + RSND_MOD_SSIP, + RSND_MOD_SSI, + RSND_MOD_SSIU, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + }, +}; + +static int rsnd_status_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, enum rsnd_mod_type type, + int shift, int add, int timing) +{ + u32 *status = mod->ops->get_status(mod, io, type); + u32 mask = 0xF << shift; + u8 val = (*status >> shift) & 0xF; + u8 next_val = (val + add) & 0xF; + int func_call = (val == timing); + + /* no status update */ + if (add == 0 || shift == 28) + return 1; + + if (next_val == 0xF) /* underflow case */ + func_call = -1; + else + *status = (*status & ~mask) + (next_val << shift); + + return func_call; +} + +#define rsnd_dai_call(fn, io, param...) \ +({ \ + struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); \ + struct rsnd_mod *mod; \ + int is_play = rsnd_io_is_play(io); \ + int ret = 0, i; \ + enum rsnd_mod_type *types = rsnd_mod_sequence[is_play]; \ + for_each_rsnd_mod_arrays(i, mod, io, types, RSND_MOD_MAX) { \ + int tmp = 0; \ + int func_call = rsnd_status_update(io, mod, types[i], \ + __rsnd_mod_shift_##fn, \ + __rsnd_mod_add_##fn, \ + __rsnd_mod_call_##fn); \ + if (func_call > 0 && (mod)->ops->fn) \ + tmp = (mod)->ops->fn(mod, io, param); \ + if (unlikely(func_call < 0) || \ + unlikely(tmp && (tmp != -EPROBE_DEFER))) \ + dev_err(dev, "%s : %s error (%d, %d)\n", \ + rsnd_mod_name(mod), #fn, tmp, func_call);\ + ret |= tmp; \ + } \ + ret; \ +}) + +int rsnd_dai_connect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) +{ + struct rsnd_priv *priv; + struct device *dev; + + if (!mod) + return -EIO; + + if (io->mod[type] == mod) + return 0; + + if (io->mod[type]) + return -EINVAL; + + priv = rsnd_mod_to_priv(mod); + dev = rsnd_priv_to_dev(priv); + + io->mod[type] = mod; + + dev_dbg(dev, "%s is connected to io (%s)\n", + rsnd_mod_name(mod), + rsnd_io_is_play(io) ? "Playback" : "Capture"); + + return 0; +} + +static void rsnd_dai_disconnect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) +{ + io->mod[type] = NULL; +} + +int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai, + int max_channels) +{ + if (max_channels > 0) + rdai->max_channels = max_channels; + + return rdai->max_channels; +} + +int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai, + int ssi_lane) +{ + if (ssi_lane > 0) + rdai->ssi_lane = ssi_lane; + + return rdai->ssi_lane; +} + +int rsnd_rdai_width_ctrl(struct rsnd_dai *rdai, int width) +{ + if (width > 0) + rdai->chan_width = width; + + return rdai->chan_width; +} + +struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id) +{ + if ((id < 0) || (id >= rsnd_rdai_nr(priv))) + return NULL; + + return priv->rdai + id; +} + +static struct snd_soc_dai_driver +*rsnd_daidrv_get(struct rsnd_priv *priv, int id) +{ + if ((id < 0) || (id >= rsnd_rdai_nr(priv))) + return NULL; + + return priv->daidrv + id; +} + +#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai) +static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = rsnd_dai_to_priv(dai); + + return rsnd_rdai_get(priv, dai->id); +} + +static void rsnd_dai_stream_init(struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream) +{ + io->substream = substream; +} + +static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io) +{ + io->substream = NULL; +} + +static +struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + + return snd_soc_rtd_to_cpu(rtd, 0); +} + +static +struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai, + struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return &rdai->playback; + else + return &rdai->capture; +} + +static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = rsnd_dai_to_priv(dai); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + int ret; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + ret = rsnd_dai_call(init, io, priv); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_dai_call(start, io, priv); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_dai_call(irq, io, priv, 1); + if (ret < 0) + goto dai_trigger_end; + + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + ret = rsnd_dai_call(irq, io, priv, 0); + + ret |= rsnd_dai_call(stop, io, priv); + + ret |= rsnd_dai_call(quit, io, priv); + + break; + default: + ret = -EINVAL; + } + +dai_trigger_end: + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + + /* set clock master for audio interface */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: + rdai->clk_master = 0; + break; + case SND_SOC_DAIFMT_BP_FP: + rdai->clk_master = 1; /* cpu is master */ + break; + default: + return -EINVAL; + } + + /* set format */ + rdai->bit_clk_inv = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + rdai->sys_delay = 0; + rdai->data_alignment = 0; + rdai->frm_clk_inv = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_DSP_B: + rdai->sys_delay = 1; + rdai->data_alignment = 0; + rdai->frm_clk_inv = 1; + break; + case SND_SOC_DAIFMT_RIGHT_J: + rdai->sys_delay = 1; + rdai->data_alignment = 1; + rdai->frm_clk_inv = 1; + break; + case SND_SOC_DAIFMT_DSP_A: + rdai->sys_delay = 0; + rdai->data_alignment = 0; + rdai->frm_clk_inv = 1; + break; + } + + /* set clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + rdai->frm_clk_inv = !rdai->frm_clk_inv; + break; + case SND_SOC_DAIFMT_IB_NF: + rdai->bit_clk_inv = !rdai->bit_clk_inv; + break; + case SND_SOC_DAIFMT_IB_IF: + rdai->bit_clk_inv = !rdai->bit_clk_inv; + rdai->frm_clk_inv = !rdai->frm_clk_inv; + break; + case SND_SOC_DAIFMT_NB_NF: + default: + break; + } + + return 0; +} + +static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, + u32 tx_mask, u32 rx_mask, + int slots, int slot_width) +{ + struct rsnd_priv *priv = rsnd_dai_to_priv(dai); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct device *dev = rsnd_priv_to_dev(priv); + + switch (slot_width) { + case 16: + case 24: + case 32: + break; + default: + /* use default */ + /* + * Indicate warning if DT has "dai-tdm-slot-width" + * but the value was not expected. + */ + if (slot_width) + dev_warn(dev, "unsupported TDM slot width (%d), force to use default 32\n", + slot_width); + slot_width = 32; + } + + switch (slots) { + case 2: + /* TDM Split Mode */ + case 6: + case 8: + /* TDM Extend Mode */ + rsnd_rdai_channels_set(rdai, slots); + rsnd_rdai_ssi_lane_set(rdai, 1); + rsnd_rdai_width_set(rdai, slot_width); + break; + default: + dev_err(dev, "unsupported TDM slots (%d)\n", slots); + return -EINVAL; + } + + return 0; +} + +static unsigned int rsnd_soc_hw_channels_list[] = { + 2, 6, 8, +}; + +static unsigned int rsnd_soc_hw_rate_list[] = { + 8000, + 11025, + 16000, + 22050, + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 176400, + 192000, +}; + +static int rsnd_soc_hw_rule(struct rsnd_dai *rdai, + unsigned int *list, int list_num, + struct snd_interval *baseline, struct snd_interval *iv, + struct rsnd_dai_stream *io, char *unit) +{ + struct snd_interval p; + unsigned int rate; + int i; + + snd_interval_any(&p); + p.min = UINT_MAX; + p.max = 0; + + for (i = 0; i < list_num; i++) { + + if (!snd_interval_test(iv, list[i])) + continue; + + rate = rsnd_ssi_clk_query(rdai, + baseline->min, list[i], NULL); + if (rate > 0) { + p.min = min(p.min, list[i]); + p.max = max(p.max, list[i]); + } + + rate = rsnd_ssi_clk_query(rdai, + baseline->max, list[i], NULL); + if (rate > 0) { + p.min = min(p.min, list[i]); + p.max = max(p.max, list[i]); + } + } + + /* Indicate error once if it can't handle */ + if (!rsnd_flags_has(io, RSND_HW_RULE_ERR) && (p.min > p.max)) { + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_warn(dev, "It can't handle %d %s <-> %d %s\n", + baseline->min, unit, baseline->max, unit); + rsnd_flags_set(io, RSND_HW_RULE_ERR); + } + + return snd_interval_refine(iv, &p); +} + +static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval ic; + struct rsnd_dai_stream *io = rule->private; + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + + /* + * possible sampling rate limitation is same as + * 2ch if it supports multi ssi + * and same as 8ch if TDM 6ch (see rsnd_ssi_config_init()) + */ + ic = *ic_; + ic.min = + ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params); + + return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_rate_list, + ARRAY_SIZE(rsnd_soc_hw_rate_list), + &ic, ir, io, "ch"); +} + +static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval ic; + struct rsnd_dai_stream *io = rule->private; + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + + /* + * possible sampling rate limitation is same as + * 2ch if it supports multi ssi + * and same as 8ch if TDM 6ch (see rsnd_ssi_config_init()) + */ + ic = *ic_; + ic.min = + ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params); + + return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_channels_list, + ARRAY_SIZE(rsnd_soc_hw_channels_list), + ir, &ic, io, "Hz"); +} + +static const struct snd_pcm_hardware rsnd_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 32, + .fifo_size = 256, +}; + +static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + struct snd_pcm_hw_constraint_list *constraint = &rdai->constraint; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int max_channels = rsnd_rdai_channels_get(rdai); + int i; + + rsnd_flags_del(io, RSND_HW_RULE_ERR); + + rsnd_dai_stream_init(io, substream); + + /* + * Channel Limitation + * It depends on Platform design + */ + constraint->list = rsnd_soc_hw_channels_list; + constraint->count = 0; + constraint->mask = 0; + + for (i = 0; i < ARRAY_SIZE(rsnd_soc_hw_channels_list); i++) { + if (rsnd_soc_hw_channels_list[i] > max_channels) + break; + constraint->count = i + 1; + } + + snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, constraint); + + snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + + /* + * Sampling Rate / Channel Limitation + * It depends on Clock Master Mode + */ + if (rsnd_rdai_is_clk_master(rdai)) { + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + rsnd_soc_hw_rule_rate, + is_play ? &rdai->playback : &rdai->capture, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + rsnd_soc_hw_rule_channels, + is_play ? &rdai->playback : &rdai->capture, + SNDRV_PCM_HW_PARAM_RATE, -1); + } + + return 0; +} + +static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + + /* + * call rsnd_dai_call without spinlock + */ + rsnd_dai_call(cleanup, io, priv); + + rsnd_dai_stream_quit(io); +} + +static int rsnd_soc_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = rsnd_dai_to_priv(dai); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + + return rsnd_dai_call(prepare, io, priv); +} + +static const u64 rsnd_soc_dai_formats[] = { + /* + * 1st Priority + * + * Well tested formats. + * Select below from Sound Card, not auto + * SND_SOC_DAIFMT_CBC_CFC + * SND_SOC_DAIFMT_CBP_CFP + */ + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF, + /* + * 2nd Priority + * + * Supported, but not well tested + */ + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct device_node *dai_np) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *ssiu_np = rsnd_ssiu_of_node(priv); + struct device_node *np; + int is_play = rsnd_io_is_play(io); + int i; + + if (!ssiu_np) + return; + + /* + * This driver assumes that it is TDM Split mode + * if it includes ssiu node + */ + for (i = 0;; i++) { + struct device_node *node = is_play ? + of_parse_phandle(dai_np, "playback", i) : + of_parse_phandle(dai_np, "capture", i); + + if (!node) + break; + + for_each_child_of_node(ssiu_np, np) { + if (np == node) { + rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT); + dev_dbg(dev, "%s is part of TDM Split\n", io->name); + } + } + + of_node_put(node); + } + + of_node_put(ssiu_np); +} + +static void rsnd_parse_connect_simple(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct device_node *dai_np) +{ + if (!rsnd_io_to_mod_ssi(io)) + return; + + rsnd_parse_tdm_split_mode(priv, io, dai_np); +} + +static void rsnd_parse_connect_graph(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct device_node *endpoint) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *remote_node; + + if (!rsnd_io_to_mod_ssi(io)) + return; + + remote_node = of_graph_get_remote_port_parent(endpoint); + + /* HDMI0 */ + if (strstr(remote_node->full_name, "hdmi@fead0000")) { + rsnd_flags_set(io, RSND_STREAM_HDMI0); + dev_dbg(dev, "%s connected to HDMI0\n", io->name); + } + + /* HDMI1 */ + if (strstr(remote_node->full_name, "hdmi@feae0000")) { + rsnd_flags_set(io, RSND_STREAM_HDMI1); + dev_dbg(dev, "%s connected to HDMI1\n", io->name); + } + + rsnd_parse_tdm_split_mode(priv, io, endpoint); + + of_node_put(remote_node); +} + +void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name, + struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), + struct device_node *node, + struct device_node *playback, + struct device_node *capture) +{ + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np; + int i; + + if (!node) + return; + + i = 0; + for_each_child_of_node(node, np) { + struct rsnd_mod *mod; + + i = rsnd_node_fixed_index(dev, np, name, i); + if (i < 0) { + of_node_put(np); + break; + } + + mod = mod_get(priv, i); + + if (np == playback) + rsnd_dai_connect(mod, &rdai->playback, mod->type); + if (np == capture) + rsnd_dai_connect(mod, &rdai->capture, mod->type); + i++; + } + + of_node_put(node); +} + +int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx) +{ + char node_name[16]; + + /* + * rsnd is assuming each device nodes are sequential numbering, + * but some of them are not. + * This function adjusts index for it. + * + * ex) + * Normal case, special case + * ssi-0 + * ssi-1 + * ssi-2 + * ssi-3 ssi-3 + * ssi-4 ssi-4 + * ... + * + * assume Max 64 node + */ + for (; idx < 64; idx++) { + snprintf(node_name, sizeof(node_name), "%s-%d", name, idx); + + if (strncmp(node_name, of_node_full_name(node), sizeof(node_name)) == 0) + return idx; + } + + dev_err(dev, "strange node numbering (%s)", + of_node_full_name(node)); + return -EINVAL; +} + +int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np; + int i; + + i = 0; + for_each_child_of_node(node, np) { + i = rsnd_node_fixed_index(dev, np, name, i); + if (i < 0) { + of_node_put(np); + return 0; + } + i++; + } + + return i; +} + +static struct device_node* + rsnd_pick_endpoint_node_for_ports(struct device_node *e_ports, + struct device_node *e_port) +{ + if (of_node_name_eq(e_ports, "ports")) + return e_ports; + + if (of_node_name_eq(e_ports, "port")) + return e_port; + + return NULL; +} + +static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np = dev->of_node; + struct device_node *ports, *node; + int nr = 0; + int i = 0; + + *is_graph = 0; + + /* + * parse both previous dai (= rcar_sound,dai), and + * graph dai (= ports/port) + */ + + /* + * Simple-Card + */ + node = of_get_child_by_name(np, RSND_NODE_DAI); + if (!node) + goto audio_graph; + + of_node_put(node); + + for_each_child_of_node(np, node) { + if (!of_node_name_eq(node, RSND_NODE_DAI)) + continue; + + priv->component_dais[i] = of_get_child_count(node); + nr += priv->component_dais[i]; + i++; + if (i >= RSND_MAX_COMPONENT) { + dev_info(dev, "reach to max component\n"); + of_node_put(node); + break; + } + } + + return nr; + +audio_graph: + /* + * Audio-Graph-Card + */ + for_each_child_of_node(np, ports) { + node = rsnd_pick_endpoint_node_for_ports(ports, np); + if (!node) + continue; + priv->component_dais[i] = of_graph_get_endpoint_count(node); + nr += priv->component_dais[i]; + i++; + if (i >= RSND_MAX_COMPONENT) { + dev_info(dev, "reach to max component\n"); + of_node_put(ports); + break; + } + } + + *is_graph = 1; + + return nr; +} + + +#define PREALLOC_BUFFER (32 * 1024) +#define PREALLOC_BUFFER_MAX (32 * 1024) + +static int rsnd_preallocate_pages(struct snd_soc_pcm_runtime *rtd, + struct rsnd_dai_stream *io, + int stream) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_pcm_substream *substream; + + /* + * use Audio-DMAC dev if we can use IPMMU + * see + * rsnd_dmaen_attach() + */ + if (io->dmac_dev) + dev = io->dmac_dev; + + for (substream = rtd->pcm->streams[stream].substream; + substream; + substream = substream->next) { + snd_pcm_set_managed_buffer(substream, + SNDRV_DMA_TYPE_DEV, + dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); + } + + return 0; +} + +static int rsnd_soc_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + int ret; + + ret = rsnd_dai_call(pcm_new, &rdai->playback, rtd); + if (ret) + return ret; + + ret = rsnd_dai_call(pcm_new, &rdai->capture, rtd); + if (ret) + return ret; + + ret = rsnd_preallocate_pages(rtd, &rdai->playback, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + return ret; + + ret = rsnd_preallocate_pages(rtd, &rdai->capture, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + return ret; + + return 0; +} + +static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { + .pcm_new = rsnd_soc_dai_pcm_new, + .startup = rsnd_soc_dai_startup, + .shutdown = rsnd_soc_dai_shutdown, + .trigger = rsnd_soc_dai_trigger, + .set_fmt = rsnd_soc_dai_set_fmt, + .set_tdm_slot = rsnd_soc_set_dai_tdm_slot, + .prepare = rsnd_soc_dai_prepare, + .auto_selectable_formats = rsnd_soc_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(rsnd_soc_dai_formats), +}; + +static void __rsnd_dai_probe(struct rsnd_priv *priv, + struct device_node *dai_np, + struct device_node *node_np, + uint32_t node_arg, + int dai_i) +{ + struct rsnd_dai_stream *io_playback; + struct rsnd_dai_stream *io_capture; + struct snd_soc_dai_driver *drv; + struct rsnd_dai *rdai; + struct device *dev = rsnd_priv_to_dev(priv); + int playback_exist = 0, capture_exist = 0; + int io_i; + + rdai = rsnd_rdai_get(priv, dai_i); + drv = rsnd_daidrv_get(priv, dai_i); + io_playback = &rdai->playback; + io_capture = &rdai->capture; + + snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); + + /* for multi Component */ + rdai->dai_args.np = node_np; + rdai->dai_args.args_count = 1; + rdai->dai_args.args[0] = node_arg; + + rdai->priv = priv; + drv->name = rdai->name; + drv->ops = &rsnd_soc_dai_ops; + drv->id = dai_i; + drv->dai_args = &rdai->dai_args; + + io_playback->rdai = rdai; + io_capture->rdai = rdai; + rsnd_rdai_channels_set(rdai, 2); /* default 2ch */ + rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */ + rsnd_rdai_width_set(rdai, 32); /* default 32bit width */ + + for (io_i = 0;; io_i++) { + struct device_node *playback = of_parse_phandle(dai_np, "playback", io_i); + struct device_node *capture = of_parse_phandle(dai_np, "capture", io_i); + + if (!playback && !capture) + break; + + if (io_i == 0) { + /* check whether playback/capture property exists */ + if (playback) + playback_exist = 1; + if (capture) + capture_exist = 1; + } + + rsnd_parse_connect_ssi(rdai, playback, capture); + rsnd_parse_connect_ssiu(rdai, playback, capture); + rsnd_parse_connect_src(rdai, playback, capture); + rsnd_parse_connect_ctu(rdai, playback, capture); + rsnd_parse_connect_mix(rdai, playback, capture); + rsnd_parse_connect_dvc(rdai, playback, capture); + + of_node_put(playback); + of_node_put(capture); + } + + if (playback_exist) { + snprintf(io_playback->name, RSND_DAI_NAME_SIZE, "DAI%d Playback", dai_i); + drv->playback.rates = RSND_RATES; + drv->playback.formats = RSND_FMTS; + drv->playback.channels_min = 2; + drv->playback.channels_max = 8; + drv->playback.stream_name = io_playback->name; + } + if (capture_exist) { + snprintf(io_capture->name, RSND_DAI_NAME_SIZE, "DAI%d Capture", dai_i); + drv->capture.rates = RSND_RATES; + drv->capture.formats = RSND_FMTS; + drv->capture.channels_min = 2; + drv->capture.channels_max = 8; + drv->capture.stream_name = io_capture->name; + } + + if (rsnd_ssi_is_pin_sharing(io_capture) || + rsnd_ssi_is_pin_sharing(io_playback)) { + /* should have symmetric_rate if pin sharing */ + drv->symmetric_rate = 1; + } + + dev_dbg(dev, "%s (%s/%s)\n", rdai->name, + rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", + rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); +} + +static int rsnd_dai_probe(struct rsnd_priv *priv) +{ + struct snd_soc_dai_driver *rdrv; + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np = dev->of_node; + struct rsnd_dai *rdai; + int nr = 0; + int is_graph; + int dai_i; + + nr = rsnd_dai_of_node(priv, &is_graph); + if (!nr) + return -EINVAL; + + rdrv = devm_kcalloc(dev, nr, sizeof(*rdrv), GFP_KERNEL); + rdai = devm_kcalloc(dev, nr, sizeof(*rdai), GFP_KERNEL); + if (!rdrv || !rdai) + return -ENOMEM; + + priv->rdai_nr = nr; + priv->daidrv = rdrv; + priv->rdai = rdai; + + /* + * parse all dai + */ + dai_i = 0; + if (is_graph) { + struct device_node *dai_np_port; + struct device_node *ports; + struct device_node *dai_np; + + for_each_child_of_node(np, ports) { + dai_np_port = rsnd_pick_endpoint_node_for_ports(ports, np); + if (!dai_np_port) + continue; + + for_each_endpoint_of_node(dai_np_port, dai_np) { + __rsnd_dai_probe(priv, dai_np, dai_np, 0, dai_i); + if (!rsnd_is_gen1(priv) && !rsnd_is_gen2(priv)) { + rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); + rsnd_parse_connect_graph(priv, &rdai->capture, dai_np); + } + dai_i++; + } + } + } else { + struct device_node *node; + struct device_node *dai_np; + + for_each_child_of_node(np, node) { + if (!of_node_name_eq(node, RSND_NODE_DAI)) + continue; + + for_each_child_of_node(node, dai_np) { + __rsnd_dai_probe(priv, dai_np, np, dai_i, dai_i); + if (!rsnd_is_gen1(priv) && !rsnd_is_gen2(priv)) { + rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_simple(priv, &rdai->playback, dai_np); + rsnd_parse_connect_simple(priv, &rdai->capture, dai_np); + } + dai_i++; + } + } + } + + return 0; +} + +/* + * pcm ops + */ +static int rsnd_hw_update(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + struct rsnd_priv *priv = rsnd_io_to_priv(io); + unsigned long flags; + int ret; + + spin_lock_irqsave(&priv->lock, flags); + if (hw_params) + ret = rsnd_dai_call(hw_params, io, substream, hw_params); + else + ret = rsnd_dai_call(hw_free, io, substream); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static int rsnd_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); + + /* + * rsnd assumes that it might be used under DPCM if user want to use + * channel / rate convert. Then, rsnd should be FE. + * And then, this function will be called *after* BE settings. + * this means, each BE already has fixuped hw_params. + * see + * dpcm_fe_dai_hw_params() + * dpcm_be_dai_hw_params() + */ + io->converted_rate = 0; + io->converted_chan = 0; + if (fe->dai_link->dynamic) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_soc_dpcm *dpcm; + int stream = substream->stream; + + for_each_dpcm_be(fe, stream, dpcm) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_hw_params *be_params = &be->dpcm[stream].hw_params; + + if (params_channels(hw_params) != params_channels(be_params)) + io->converted_chan = params_channels(be_params); + if (params_rate(hw_params) != params_rate(be_params)) + io->converted_rate = params_rate(be_params); + } + if (io->converted_chan) + dev_dbg(dev, "convert channels = %d\n", io->converted_chan); + if (io->converted_rate) { + /* + * SRC supports convert rates from params_rate(hw_params)/k_down + * to params_rate(hw_params)*k_up, where k_up is always 6, and + * k_down depends on number of channels and SRC unit. + * So all SRC units can upsample audio up to 6 times regardless + * its number of channels. And all SRC units can downsample + * 2 channel audio up to 6 times too. + */ + int k_up = 6; + int k_down = 6; + int channel; + struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); + + dev_dbg(dev, "convert rate = %d\n", io->converted_rate); + + channel = io->converted_chan ? io->converted_chan : + params_channels(hw_params); + + switch (rsnd_mod_id(src_mod)) { + /* + * SRC0 can downsample 4, 6 and 8 channel audio up to 4 times. + * SRC1, SRC3 and SRC4 can downsample 4 channel audio + * up to 4 times. + * SRC1, SRC3 and SRC4 can downsample 6 and 8 channel audio + * no more than twice. + */ + case 1: + case 3: + case 4: + if (channel > 4) { + k_down = 2; + break; + } + fallthrough; + case 0: + if (channel > 2) + k_down = 4; + break; + + /* Other SRC units do not support more than 2 channels */ + default: + if (channel > 2) + return -EINVAL; + } + + if (params_rate(hw_params) > io->converted_rate * k_down) { + hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->min = + io->converted_rate * k_down; + hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->max = + io->converted_rate * k_down; + hw_params->cmask |= SNDRV_PCM_HW_PARAM_RATE; + } else if (params_rate(hw_params) * k_up < io->converted_rate) { + hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->min = + DIV_ROUND_UP(io->converted_rate, k_up); + hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->max = + DIV_ROUND_UP(io->converted_rate, k_up); + hw_params->cmask |= SNDRV_PCM_HW_PARAM_RATE; + } + + /* + * TBD: Max SRC input and output rates also depend on number + * of channels and SRC unit: + * SRC1, SRC3 and SRC4 do not support more than 128kHz + * for 6 channel and 96kHz for 8 channel audio. + * Perhaps this function should return EINVAL if the input or + * the output rate exceeds the limitation. + */ + } + } + + return rsnd_hw_update(substream, hw_params); +} + +static int rsnd_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return rsnd_hw_update(substream, NULL); +} + +static snd_pcm_uframes_t rsnd_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + snd_pcm_uframes_t pointer = 0; + + rsnd_dai_call(pointer, io, &pointer); + + return pointer; +} + +/* + * snd_kcontrol + */ +static int rsnd_kctrl_info(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_info *uinfo) +{ + struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl); + + if (cfg->texts) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = cfg->size; + uinfo->value.enumerated.items = cfg->max; + if (uinfo->value.enumerated.item >= cfg->max) + uinfo->value.enumerated.item = cfg->max - 1; + strscpy(uinfo->value.enumerated.name, + cfg->texts[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)); + } else { + uinfo->count = cfg->size; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = cfg->max; + uinfo->type = (cfg->max == 1) ? + SNDRV_CTL_ELEM_TYPE_BOOLEAN : + SNDRV_CTL_ELEM_TYPE_INTEGER; + } + + return 0; +} + +static int rsnd_kctrl_get(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *uc) +{ + struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl); + int i; + + for (i = 0; i < cfg->size; i++) + if (cfg->texts) + uc->value.enumerated.item[i] = cfg->val[i]; + else + uc->value.integer.value[i] = cfg->val[i]; + + return 0; +} + +static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *uc) +{ + struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl); + int i, change = 0; + + if (!cfg->accept(cfg->io)) + return 0; + + for (i = 0; i < cfg->size; i++) { + if (cfg->texts) { + change |= (uc->value.enumerated.item[i] != cfg->val[i]); + cfg->val[i] = uc->value.enumerated.item[i]; + } else { + change |= (uc->value.integer.value[i] != cfg->val[i]); + cfg->val[i] = uc->value.integer.value[i]; + } + } + + if (change && cfg->update) + cfg->update(cfg->io, cfg->mod); + + return change; +} + +int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io) +{ + return 1; +} + +int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + + if (!runtime) { + dev_warn(dev, "Can't update kctrl when idle\n"); + return 0; + } + + return 1; +} + +struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg) +{ + cfg->cfg.val = cfg->val; + + return &cfg->cfg; +} + +struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg) +{ + cfg->cfg.val = &cfg->val; + + return &cfg->cfg; +} + +const char * const volume_ramp_rate[] = { + "128 dB/1 step", /* 00000 */ + "64 dB/1 step", /* 00001 */ + "32 dB/1 step", /* 00010 */ + "16 dB/1 step", /* 00011 */ + "8 dB/1 step", /* 00100 */ + "4 dB/1 step", /* 00101 */ + "2 dB/1 step", /* 00110 */ + "1 dB/1 step", /* 00111 */ + "0.5 dB/1 step", /* 01000 */ + "0.25 dB/1 step", /* 01001 */ + "0.125 dB/1 step", /* 01010 = VOLUME_RAMP_MAX_MIX */ + "0.125 dB/2 steps", /* 01011 */ + "0.125 dB/4 steps", /* 01100 */ + "0.125 dB/8 steps", /* 01101 */ + "0.125 dB/16 steps", /* 01110 */ + "0.125 dB/32 steps", /* 01111 */ + "0.125 dB/64 steps", /* 10000 */ + "0.125 dB/128 steps", /* 10001 */ + "0.125 dB/256 steps", /* 10010 */ + "0.125 dB/512 steps", /* 10011 */ + "0.125 dB/1024 steps", /* 10100 */ + "0.125 dB/2048 steps", /* 10101 */ + "0.125 dB/4096 steps", /* 10110 */ + "0.125 dB/8192 steps", /* 10111 = VOLUME_RAMP_MAX_DVC */ +}; + +int rsnd_kctrl_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + int (*accept)(struct rsnd_dai_stream *io), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), + struct rsnd_kctrl_cfg *cfg, + const char * const *texts, + int size, + u32 max) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_kcontrol *kctrl; + struct snd_kcontrol_new knew = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = name, + .info = rsnd_kctrl_info, + .index = rtd->num, + .get = rsnd_kctrl_get, + .put = rsnd_kctrl_put, + }; + int ret; + + /* + * 1) Avoid duplicate register for DVC with MIX case + * 2) Allow duplicate register for MIX + * 3) re-register if card was rebinded + */ + list_for_each_entry(kctrl, &card->controls, list) { + struct rsnd_kctrl_cfg *c = kctrl->private_data; + + if (c == cfg) + return 0; + } + + if (size > RSND_MAX_CHANNELS) + return -EINVAL; + + kctrl = snd_ctl_new1(&knew, cfg); + if (!kctrl) + return -ENOMEM; + + ret = snd_ctl_add(card, kctrl); + if (ret < 0) + return ret; + + cfg->texts = texts; + cfg->max = max; + cfg->size = size; + cfg->accept = accept; + cfg->update = update; + cfg->card = card; + cfg->kctrl = kctrl; + cfg->io = io; + cfg->mod = mod; + + return 0; +} + +/* + * snd_soc_component + */ +static const struct snd_soc_component_driver rsnd_soc_component = { + .name = "rsnd", + .probe = rsnd_debugfs_probe, + .hw_params = rsnd_hw_params, + .hw_free = rsnd_hw_free, + .pointer = rsnd_pointer, + .legacy_dai_naming = 1, +}; + +static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_dai_call(probe, io, priv); + if (ret == -EAGAIN) { + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *mod; + int i; + + /* + * Fallback to PIO mode + */ + + /* + * call "remove" for SSI/SRC/DVC + * SSI will be switch to PIO mode if it was DMA mode + * see + * rsnd_dma_init() + * rsnd_ssi_fallback() + */ + rsnd_dai_call(remove, io, priv); + + /* + * remove all mod from io + * and, re connect ssi + */ + for_each_rsnd_mod(i, mod, io) + rsnd_dai_disconnect(mod, io, i); + rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI); + + /* + * fallback + */ + rsnd_dai_call(fallback, io, priv); + + /* + * retry to "probe". + * DAI has SSI which is PIO mode only now. + */ + ret = rsnd_dai_call(probe, io, priv); + } + + return ret; +} + +/* + * rsnd probe + */ +static int rsnd_probe(struct platform_device *pdev) +{ + struct rsnd_priv *priv; + struct device *dev = &pdev->dev; + struct rsnd_dai *rdai; + int (*probe_func[])(struct rsnd_priv *priv) = { + rsnd_gen_probe, + rsnd_dma_probe, + rsnd_ssi_probe, + rsnd_ssiu_probe, + rsnd_src_probe, + rsnd_ctu_probe, + rsnd_mix_probe, + rsnd_dvc_probe, + rsnd_cmd_probe, + rsnd_adg_probe, + rsnd_dai_probe, + }; + int ret, i; + int ci; + + /* + * init priv data + */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENODEV; + + priv->pdev = pdev; + priv->flags = (unsigned long)of_device_get_match_data(dev); + spin_lock_init(&priv->lock); + + /* + * init each module + */ + for (i = 0; i < ARRAY_SIZE(probe_func); i++) { + ret = probe_func[i](priv); + if (ret) + return ret; + } + + for_each_rsnd_dai(rdai, priv, i) { + ret = rsnd_rdai_continuance_probe(priv, &rdai->playback); + if (ret) + goto exit_snd_probe; + + ret = rsnd_rdai_continuance_probe(priv, &rdai->capture); + if (ret) + goto exit_snd_probe; + } + + dev_set_drvdata(dev, priv); + + /* + * asoc register + */ + ci = 0; + for (i = 0; priv->component_dais[i] > 0; i++) { + int nr = priv->component_dais[i]; + + ret = devm_snd_soc_register_component(dev, &rsnd_soc_component, + priv->daidrv + ci, nr); + if (ret < 0) { + dev_err(dev, "cannot snd component register\n"); + goto exit_snd_probe; + } + + ci += nr; + } + + pm_runtime_enable(dev); + + dev_info(dev, "probed\n"); + return ret; + +exit_snd_probe: + for_each_rsnd_dai(rdai, priv, i) { + rsnd_dai_call(remove, &rdai->playback, priv); + rsnd_dai_call(remove, &rdai->capture, priv); + } + + /* + * adg is very special mod which can't use rsnd_dai_call(remove), + * and it registers ADG clock on probe. + * It should be unregister if probe failed. + * Mainly it is assuming -EPROBE_DEFER case + */ + rsnd_adg_remove(priv); + + return ret; +} + +static void rsnd_remove(struct platform_device *pdev) +{ + struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); + struct rsnd_dai *rdai; + void (*remove_func[])(struct rsnd_priv *priv) = { + rsnd_ssi_remove, + rsnd_ssiu_remove, + rsnd_src_remove, + rsnd_ctu_remove, + rsnd_mix_remove, + rsnd_dvc_remove, + rsnd_cmd_remove, + rsnd_adg_remove, + }; + int i; + + pm_runtime_disable(&pdev->dev); + + for_each_rsnd_dai(rdai, priv, i) { + int ret; + + ret = rsnd_dai_call(remove, &rdai->playback, priv); + if (ret) + dev_warn(&pdev->dev, "Failed to remove playback dai #%d\n", i); + + ret = rsnd_dai_call(remove, &rdai->capture, priv); + if (ret) + dev_warn(&pdev->dev, "Failed to remove capture dai #%d\n", i); + } + + for (i = 0; i < ARRAY_SIZE(remove_func); i++) + remove_func[i](priv); +} + +static int __maybe_unused rsnd_suspend(struct device *dev) +{ + struct rsnd_priv *priv = dev_get_drvdata(dev); + + rsnd_adg_clk_disable(priv); + + return 0; +} + +static int __maybe_unused rsnd_resume(struct device *dev) +{ + struct rsnd_priv *priv = dev_get_drvdata(dev); + + rsnd_adg_clk_enable(priv); + + return 0; +} + +static const struct dev_pm_ops rsnd_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rsnd_suspend, rsnd_resume) +}; + +static struct platform_driver rsnd_driver = { + .driver = { + .name = "rcar_sound", + .pm = &rsnd_pm_ops, + .of_match_table = rsnd_of_match, + }, + .probe = rsnd_probe, + .remove = rsnd_remove, +}; +module_platform_driver(rsnd_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas R-Car audio driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_ALIAS("platform:rcar-pcm-audio"); diff --git a/sound/soc/renesas/rcar/ctu.c b/sound/soc/renesas/rcar/ctu.c new file mode 100644 index 000000000000..a26ec7b780cd --- /dev/null +++ b/sound/soc/renesas/rcar/ctu.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ctu.c +// +// Copyright (c) 2015 Kuninori Morimoto + +#include "rsnd.h" + +#define CTU_NAME_SIZE 16 +#define CTU_NAME "ctu" + +/* + * User needs to setup CTU by amixer, and its settings are + * based on below registers + * + * CTUn_CPMDR : amixser set "CTU Pass" + * CTUn_SV0xR : amixser set "CTU SV0" + * CTUn_SV1xR : amixser set "CTU SV1" + * CTUn_SV2xR : amixser set "CTU SV2" + * CTUn_SV3xR : amixser set "CTU SV3" + * + * [CTU Pass] + * 0000: default + * 0001: Connect input data of channel 0 + * 0010: Connect input data of channel 1 + * 0011: Connect input data of channel 2 + * 0100: Connect input data of channel 3 + * 0101: Connect input data of channel 4 + * 0110: Connect input data of channel 5 + * 0111: Connect input data of channel 6 + * 1000: Connect input data of channel 7 + * 1001: Connect calculated data by scale values of matrix row 0 + * 1010: Connect calculated data by scale values of matrix row 1 + * 1011: Connect calculated data by scale values of matrix row 2 + * 1100: Connect calculated data by scale values of matrix row 3 + * + * [CTU SVx] + * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07] + * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17] + * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27] + * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37] + * [Output4] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output5] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output6] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output7] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * + * [SVxx] + * Plus Minus + * value time dB value time dB + * ----------------------------------------------------------------------- + * H'7F_FFFF 2 6 H'80_0000 2 6 + * ... + * H'40_0000 1 0 H'C0_0000 1 0 + * ... + * H'00_0001 2.38 x 10^-7 -132 + * H'00_0000 0 Mute H'FF_FFFF 2.38 x 10^-7 -132 + * + * + * Ex) Input ch -> Output ch + * 1ch -> 0ch + * 0ch -> 1ch + * + * amixer set "CTU Reset" on + * amixer set "CTU Pass" 9,10 + * amixer set "CTU SV0" 0,4194304 + * amixer set "CTU SV1" 4194304,0 + * or + * amixer set "CTU Reset" on + * amixer set "CTU Pass" 2,1 + */ + +struct rsnd_ctu { + struct rsnd_mod mod; + struct rsnd_kctrl_cfg_m pass; + struct rsnd_kctrl_cfg_m sv[4]; + struct rsnd_kctrl_cfg_s reset; + int channels; + u32 flags; +}; + +#define KCTRL_INITIALIZED (1 << 0) + +#define rsnd_ctu_nr(priv) ((priv)->ctu_nr) +#define for_each_rsnd_ctu(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_ctu_nr(priv)) && \ + ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ + i++) + +#define rsnd_mod_to_ctu(_mod) \ + container_of((_mod), struct rsnd_ctu, mod) + +#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id) + +static void rsnd_ctu_activation(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, CTU_SWRSR, 0); + rsnd_mod_write(mod, CTU_SWRSR, 1); +} + +static void rsnd_ctu_halt(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, CTU_CTUIR, 1); + rsnd_mod_write(mod, CTU_SWRSR, 0); +} + +static int rsnd_ctu_probe_(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + return rsnd_cmd_attach(io, rsnd_mod_id(mod)); +} + +static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + u32 cpmdr = 0; + u32 scmdr = 0; + int i, j; + + for (i = 0; i < RSND_MAX_CHANNELS; i++) { + u32 val = rsnd_kctrl_valm(ctu->pass, i); + + cpmdr |= val << (28 - (i * 4)); + + if ((val > 0x8) && (scmdr < (val - 0x8))) + scmdr = val - 0x8; + } + + rsnd_mod_write(mod, CTU_CTUIR, 1); + + rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io)); + + rsnd_mod_write(mod, CTU_CPMDR, cpmdr); + + rsnd_mod_write(mod, CTU_SCMDR, scmdr); + + for (i = 0; i < 4; i++) { + + if (i >= scmdr) + break; + + for (j = 0; j < RSND_MAX_CHANNELS; j++) + rsnd_mod_write(mod, CTU_SVxxR(i, j), rsnd_kctrl_valm(ctu->sv[i], j)); + } + + rsnd_mod_write(mod, CTU_CTUIR, 0); +} + +static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + int i; + + if (!rsnd_kctrl_vals(ctu->reset)) + return; + + for (i = 0; i < RSND_MAX_CHANNELS; i++) { + rsnd_kctrl_valm(ctu->pass, i) = 0; + rsnd_kctrl_valm(ctu->sv[0], i) = 0; + rsnd_kctrl_valm(ctu->sv[1], i) = 0; + rsnd_kctrl_valm(ctu->sv[2], i) = 0; + rsnd_kctrl_valm(ctu->sv[3], i) = 0; + } + rsnd_kctrl_vals(ctu->reset) = 0; +} + +static int rsnd_ctu_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + int ret; + + ret = rsnd_mod_power_on(mod); + if (ret < 0) + return ret; + + rsnd_ctu_activation(mod); + + rsnd_ctu_value_init(io, mod); + + return 0; +} + +static int rsnd_ctu_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_ctu_halt(mod); + + rsnd_mod_power_off(mod); + + return 0; +} + +static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + int ret; + + if (rsnd_flags_has(ctu, KCTRL_INITIALIZED)) + return 0; + + /* CTU Pass */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", + rsnd_kctrl_accept_anytime, + NULL, + &ctu->pass, RSND_MAX_CHANNELS, + 0xC); + if (ret < 0) + return ret; + + /* ROW0 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", + rsnd_kctrl_accept_anytime, + NULL, + &ctu->sv[0], RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW1 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", + rsnd_kctrl_accept_anytime, + NULL, + &ctu->sv[1], RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW2 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", + rsnd_kctrl_accept_anytime, + NULL, + &ctu->sv[2], RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW3 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", + rsnd_kctrl_accept_anytime, + NULL, + &ctu->sv[3], RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* Reset */ + ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset", + rsnd_kctrl_accept_anytime, + rsnd_ctu_value_reset, + &ctu->reset, 1); + + rsnd_flags_set(ctu, KCTRL_INITIALIZED); + + return ret; +} + +static int rsnd_ctu_id(struct rsnd_mod *mod) +{ + /* + * ctu00: -> 0, ctu01: -> 0, ctu02: -> 0, ctu03: -> 0 + * ctu10: -> 1, ctu11: -> 1, ctu12: -> 1, ctu13: -> 1 + */ + return mod->id / 4; +} + +static int rsnd_ctu_id_sub(struct rsnd_mod *mod) +{ + /* + * ctu00: -> 0, ctu01: -> 1, ctu02: -> 2, ctu03: -> 3 + * ctu10: -> 0, ctu11: -> 1, ctu12: -> 2, ctu13: -> 3 + */ + return mod->id % 4; +} + +#ifdef CONFIG_DEBUG_FS +static void rsnd_ctu_debug_info(struct seq_file *m, + struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, + 0x500 + rsnd_mod_id_raw(mod) * 0x100, 0x100); +} +#define DEBUG_INFO .debug_info = rsnd_ctu_debug_info +#else +#define DEBUG_INFO +#endif + +static struct rsnd_mod_ops rsnd_ctu_ops = { + .name = CTU_NAME, + .probe = rsnd_ctu_probe_, + .init = rsnd_ctu_init, + .quit = rsnd_ctu_quit, + .pcm_new = rsnd_ctu_pcm_new, + .get_status = rsnd_mod_get_status, + .id = rsnd_ctu_id, + .id_sub = rsnd_ctu_id_sub, + .id_cmd = rsnd_mod_id_raw, + DEBUG_INFO +}; + +struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv))) + id = 0; + + return rsnd_mod_get(rsnd_ctu_get(priv, id)); +} + +int rsnd_ctu_probe(struct rsnd_priv *priv) +{ + struct device_node *node; + struct device_node *np; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ctu *ctu; + struct clk *clk; + char name[CTU_NAME_SIZE]; + int i, nr, ret; + + node = rsnd_ctu_of_node(priv); + if (!node) + return 0; /* not used is not error */ + + nr = of_get_child_count(node); + if (!nr) { + ret = -EINVAL; + goto rsnd_ctu_probe_done; + } + + ctu = devm_kcalloc(dev, nr, sizeof(*ctu), GFP_KERNEL); + if (!ctu) { + ret = -ENOMEM; + goto rsnd_ctu_probe_done; + } + + priv->ctu_nr = nr; + priv->ctu = ctu; + + i = 0; + ret = 0; + for_each_child_of_node(node, np) { + ctu = rsnd_ctu_get(priv, i); + + /* + * CTU00, CTU01, CTU02, CTU03 => CTU0 + * CTU10, CTU11, CTU12, CTU13 => CTU1 + */ + snprintf(name, CTU_NAME_SIZE, "%s.%d", + CTU_NAME, i / 4); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + of_node_put(np); + goto rsnd_ctu_probe_done; + } + + ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, + clk, RSND_MOD_CTU, i); + if (ret) { + of_node_put(np); + goto rsnd_ctu_probe_done; + } + + i++; + } + + +rsnd_ctu_probe_done: + of_node_put(node); + + return ret; +} + +void rsnd_ctu_remove(struct rsnd_priv *priv) +{ + struct rsnd_ctu *ctu; + int i; + + for_each_rsnd_ctu(ctu, priv, i) { + rsnd_mod_quit(rsnd_mod_get(ctu)); + } +} diff --git a/sound/soc/renesas/rcar/debugfs.c b/sound/soc/renesas/rcar/debugfs.c new file mode 100644 index 000000000000..26d3b310b9db --- /dev/null +++ b/sound/soc/renesas/rcar/debugfs.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// // Renesas R-Car debugfs support +// +// Copyright (c) 2021 Kuninori Morimoto +// +// > mount -t debugfs none /sys/kernel/debug +// > cd /sys/kernel/debug/asoc/rcar-sound/ec500000.sound/rdai{N}/ +// > cat playback/xxx +// > cat capture/xxx +// +#ifdef CONFIG_DEBUG_FS + +#include +#include "rsnd.h" + +static int rsnd_debugfs_show(struct seq_file *m, void *v) +{ + struct rsnd_dai_stream *io = m->private; + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + int i; + + /* adg is out of mods */ + rsnd_adg_clk_dbg_info(priv, m); + + for_each_rsnd_mod(i, mod, io) { + u32 *status = mod->ops->get_status(mod, io, mod->type); + + seq_printf(m, "name: %s\n", rsnd_mod_name(mod)); + seq_printf(m, "status: %08x\n", *status); + + if (mod->ops->debug_info) + mod->ops->debug_info(m, io, mod); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(rsnd_debugfs); + +void rsnd_debugfs_reg_show(struct seq_file *m, phys_addr_t _addr, + void __iomem *base, int offset, int size) +{ + int i, j; + + for (i = 0; i < size; i += 0x10) { + phys_addr_t addr = _addr + offset + i; + + seq_printf(m, "%pa:", &addr); + for (j = 0; j < 0x10; j += 0x4) + seq_printf(m, " %08x", __raw_readl(base + offset + i + j)); + seq_puts(m, "\n"); + } +} + +void rsnd_debugfs_mod_reg_show(struct seq_file *m, struct rsnd_mod *mod, + int reg_id, int offset, int size) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + + rsnd_debugfs_reg_show(m, + rsnd_gen_get_phy_addr(priv, reg_id), + rsnd_gen_get_base_addr(priv, reg_id), + offset, size); +} + +int rsnd_debugfs_probe(struct snd_soc_component *component) +{ + struct rsnd_priv *priv = dev_get_drvdata(component->dev); + struct rsnd_dai *rdai; + struct dentry *dir; + char name[64]; + int i; + + /* Gen1 is not supported */ + if (rsnd_is_gen1(priv)) + return 0; + + for_each_rsnd_dai(rdai, priv, i) { + /* + * created debugfs will be automatically + * removed, nothing to do for _remove. + * see + * soc_cleanup_component_debugfs() + */ + snprintf(name, sizeof(name), "rdai%d", i); + dir = debugfs_create_dir(name, component->debugfs_root); + + debugfs_create_file("playback", 0444, dir, &rdai->playback, &rsnd_debugfs_fops); + debugfs_create_file("capture", 0444, dir, &rdai->capture, &rsnd_debugfs_fops); + } + + return 0; +} + +#endif /* CONFIG_DEBUG_FS */ diff --git a/sound/soc/renesas/rcar/dma.c b/sound/soc/renesas/rcar/dma.c new file mode 100644 index 000000000000..2342bbb6fe92 --- /dev/null +++ b/sound/soc/renesas/rcar/dma.c @@ -0,0 +1,885 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car Audio DMAC support +// +// Copyright (C) 2015 Renesas Electronics Corp. +// Copyright (c) 2015 Kuninori Morimoto + +#include +#include +#include +#include "rsnd.h" + +/* + * Audio DMAC peri peri register + */ +#define PDMASAR 0x00 +#define PDMADAR 0x04 +#define PDMACHCR 0x0c + +/* PDMACHCR */ +#define PDMACHCR_DE (1 << 0) + + +struct rsnd_dmaen { + struct dma_chan *chan; +}; + +struct rsnd_dmapp { + int dmapp_id; + u32 chcr; +}; + +struct rsnd_dma { + struct rsnd_mod mod; + struct rsnd_mod *mod_from; + struct rsnd_mod *mod_to; + dma_addr_t src_addr; + dma_addr_t dst_addr; + union { + struct rsnd_dmaen en; + struct rsnd_dmapp pp; + } dma; +}; + +struct rsnd_dma_ctrl { + void __iomem *ppbase; + phys_addr_t ppres; + int dmaen_num; + int dmapp_num; +}; + +#define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) +#define rsnd_mod_to_dma(_mod) container_of((_mod), struct rsnd_dma, mod) +#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) +#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) + +/* for DEBUG */ +static struct rsnd_mod_ops mem_ops = { + .name = "mem", +}; + +static struct rsnd_mod mem = { +}; + +/* + * Audio DMAC + */ +static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, + struct rsnd_mod *mod_from, + struct rsnd_mod *mod_to) +{ + if ((!mod_from && !mod_to) || + (mod_from && mod_to)) + return NULL; + + if (mod_from) + return rsnd_mod_dma_req(io, mod_from); + else + return rsnd_mod_dma_req(io, mod_to); +} + +static int rsnd_dmaen_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + return snd_dmaengine_pcm_trigger(io->substream, SNDRV_PCM_TRIGGER_STOP); +} + +static int rsnd_dmaen_cleanup(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + + /* + * DMAEngine release uses mutex lock. + * Thus, it shouldn't be called under spinlock. + * Let's call it under prepare + */ + if (dmaen->chan) + snd_dmaengine_pcm_close_release_chan(io->substream); + + dmaen->chan = NULL; + + return 0; +} + +static int rsnd_dmaen_prepare(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + struct device *dev = rsnd_priv_to_dev(priv); + + /* maybe suspended */ + if (dmaen->chan) + return 0; + + /* + * DMAEngine request uses mutex lock. + * Thus, it shouldn't be called under spinlock. + * Let's call it under prepare + */ + dmaen->chan = rsnd_dmaen_request_channel(io, + dma->mod_from, + dma->mod_to); + if (IS_ERR_OR_NULL(dmaen->chan)) { + dmaen->chan = NULL; + dev_err(dev, "can't get dma channel\n"); + return -EIO; + } + + return snd_dmaengine_pcm_open(io->substream, dmaen->chan); +} + +static int rsnd_dmaen_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + struct device *dev = rsnd_priv_to_dev(priv); + struct dma_slave_config cfg = {}; + enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + int ret; + + /* + * in case of monaural data writing or reading through Audio-DMAC + * data is always in Left Justified format, so both src and dst + * DMA Bus width need to be set equal to physical data width. + */ + if (rsnd_runtime_channel_original(io) == 1) { + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + int bits = snd_pcm_format_physical_width(runtime->format); + + switch (bits) { + case 8: + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 16: + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 32: + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + dev_err(dev, "invalid format width %d\n", bits); + return -EINVAL; + } + } + + cfg.direction = snd_pcm_substream_to_dma_direction(io->substream); + cfg.src_addr = dma->src_addr; + cfg.dst_addr = dma->dst_addr; + cfg.src_addr_width = buswidth; + cfg.dst_addr_width = buswidth; + + dev_dbg(dev, "%s %pad -> %pad\n", + rsnd_mod_name(mod), + &cfg.src_addr, &cfg.dst_addr); + + ret = dmaengine_slave_config(dmaen->chan, &cfg); + if (ret < 0) + return ret; + + return snd_dmaengine_pcm_trigger(io->substream, SNDRV_PCM_TRIGGER_START); +} + +struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name, + struct rsnd_mod *mod, char *x) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct dma_chan *chan = NULL; + struct device_node *np; + int i = 0; + + for_each_child_of_node(of_node, np) { + i = rsnd_node_fixed_index(dev, np, name, i); + if (i < 0) { + chan = NULL; + of_node_put(np); + break; + } + + if (i == rsnd_mod_id_raw(mod) && (!chan)) + chan = of_dma_request_slave_channel(np, x); + i++; + } + + /* It should call of_node_put(), since, it is rsnd_xxx_of_node() */ + of_node_put(of_node); + + return chan; +} + +static int rsnd_dmaen_attach(struct rsnd_dai_stream *io, + struct rsnd_dma *dma, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct dma_chan *chan; + + /* try to get DMAEngine channel */ + chan = rsnd_dmaen_request_channel(io, mod_from, mod_to); + if (IS_ERR_OR_NULL(chan)) { + /* Let's follow when -EPROBE_DEFER case */ + if (PTR_ERR(chan) == -EPROBE_DEFER) + return PTR_ERR(chan); + + /* + * DMA failed. try to PIO mode + * see + * rsnd_ssi_fallback() + * rsnd_rdai_continuance_probe() + */ + return -EAGAIN; + } + + /* + * use it for IPMMU if needed + * see + * rsnd_preallocate_pages() + */ + io->dmac_dev = chan->device->dev; + + dma_release_channel(chan); + + dmac->dmaen_num++; + + return 0; +} + +static int rsnd_dmaen_pointer(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + snd_pcm_uframes_t *pointer) +{ + *pointer = snd_dmaengine_pcm_pointer(io->substream); + + return 0; +} + +static struct rsnd_mod_ops rsnd_dmaen_ops = { + .name = "audmac", + .prepare = rsnd_dmaen_prepare, + .cleanup = rsnd_dmaen_cleanup, + .start = rsnd_dmaen_start, + .stop = rsnd_dmaen_stop, + .pointer = rsnd_dmaen_pointer, + .get_status = rsnd_mod_get_status, +}; + +/* + * Audio DMAC peri peri + */ +static const u8 gen2_id_table_ssiu[] = { + /* SSI00 ~ SSI07 */ + 0x00, 0x01, 0x02, 0x03, 0x39, 0x3a, 0x3b, 0x3c, + /* SSI10 ~ SSI17 */ + 0x04, 0x05, 0x06, 0x07, 0x3d, 0x3e, 0x3f, 0x40, + /* SSI20 ~ SSI27 */ + 0x08, 0x09, 0x0a, 0x0b, 0x41, 0x42, 0x43, 0x44, + /* SSI30 ~ SSI37 */ + 0x0c, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, + /* SSI40 ~ SSI47 */ + 0x0d, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, + /* SSI5 */ + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* SSI6 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* SSI7 */ + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* SSI8 */ + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* SSI90 ~ SSI97 */ + 0x12, 0x13, 0x14, 0x15, 0x53, 0x54, 0x55, 0x56, +}; +static const u8 gen2_id_table_scu[] = { + 0x2d, /* SCU_SRCI0 */ + 0x2e, /* SCU_SRCI1 */ + 0x2f, /* SCU_SRCI2 */ + 0x30, /* SCU_SRCI3 */ + 0x31, /* SCU_SRCI4 */ + 0x32, /* SCU_SRCI5 */ + 0x33, /* SCU_SRCI6 */ + 0x34, /* SCU_SRCI7 */ + 0x35, /* SCU_SRCI8 */ + 0x36, /* SCU_SRCI9 */ +}; +static const u8 gen2_id_table_cmd[] = { + 0x37, /* SCU_CMD0 */ + 0x38, /* SCU_CMD1 */ +}; + +static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); + const u8 *entry = NULL; + int id = 255; + int size = 0; + + if ((mod == ssi) || + (mod == ssiu)) { + int busif = rsnd_mod_id_sub(ssiu); + + entry = gen2_id_table_ssiu; + size = ARRAY_SIZE(gen2_id_table_ssiu); + id = (rsnd_mod_id(mod) * 8) + busif; + } else if (mod == src) { + entry = gen2_id_table_scu; + size = ARRAY_SIZE(gen2_id_table_scu); + id = rsnd_mod_id(mod); + } else if (mod == dvc) { + entry = gen2_id_table_cmd; + size = ARRAY_SIZE(gen2_id_table_cmd); + id = rsnd_mod_id(mod); + } + + if ((!entry) || (size <= id)) { + struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); + + dev_err(dev, "unknown connection (%s)\n", rsnd_mod_name(mod)); + + /* use non-prohibited SRS number as error */ + return 0x00; /* SSI00 */ + } + + return entry[id]; +} + +static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io, + struct rsnd_mod *mod_from, + struct rsnd_mod *mod_to) +{ + return (rsnd_dmapp_get_id(io, mod_from) << 24) + + (rsnd_dmapp_get_id(io, mod_to) << 16); +} + +#define rsnd_dmapp_addr(dmac, dma, reg) \ + (dmac->ppbase + 0x20 + reg + \ + (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id)) +static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) +{ + struct rsnd_mod *mod = rsnd_mod_get(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "w 0x%px : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data); + + iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg)); +} + +static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) +{ + struct rsnd_mod *mod = rsnd_mod_get(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + + return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); +} + +static void rsnd_dmapp_bset(struct rsnd_dma *dma, u32 data, u32 mask, u32 reg) +{ + struct rsnd_mod *mod = rsnd_mod_get(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + void __iomem *addr = rsnd_dmapp_addr(dmac, dma, reg); + u32 val = ioread32(addr); + + val &= ~mask; + val |= (data & mask); + + iowrite32(val, addr); +} + +static int rsnd_dmapp_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + int i; + + rsnd_dmapp_bset(dma, 0, PDMACHCR_DE, PDMACHCR); + + for (i = 0; i < 1024; i++) { + if (0 == (rsnd_dmapp_read(dma, PDMACHCR) & PDMACHCR_DE)) + return 0; + udelay(1); + } + + return -EIO; +} + +static int rsnd_dmapp_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); + + rsnd_dmapp_write(dma, dma->src_addr, PDMASAR); + rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR); + rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); + + return 0; +} + +static int rsnd_dmapp_attach(struct rsnd_dai_stream *io, + struct rsnd_dma *dma, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) +{ + struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct device *dev = rsnd_priv_to_dev(priv); + + dmapp->dmapp_id = dmac->dmapp_num; + dmapp->chcr = rsnd_dmapp_get_chcr(io, mod_from, mod_to) | PDMACHCR_DE; + + dmac->dmapp_num++; + + dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", + dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void rsnd_dmapp_debug_info(struct seq_file *m, + struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); + + rsnd_debugfs_reg_show(m, dmac->ppres, dmac->ppbase, + 0x20 + 0x10 * dmapp->dmapp_id, 0x10); +} +#define DEBUG_INFO .debug_info = rsnd_dmapp_debug_info +#else +#define DEBUG_INFO +#endif + +static struct rsnd_mod_ops rsnd_dmapp_ops = { + .name = "audmac-pp", + .start = rsnd_dmapp_start, + .stop = rsnd_dmapp_stop, + .quit = rsnd_dmapp_stop, + .get_status = rsnd_mod_get_status, + DEBUG_INFO +}; + +/* + * Common DMAC Interface + */ + +/* + * DMA read/write register offset + * + * RSND_xxx_I_N for Audio DMAC input + * RSND_xxx_O_N for Audio DMAC output + * RSND_xxx_I_P for Audio DMAC peri peri input + * RSND_xxx_O_P for Audio DMAC peri peri output + * + * ex) R-Car H2 case + * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out + * SSI : 0xec541000 / 0xec241008 / 0xec24100c + * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 + * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 + * CMD : 0xec500000 / / 0xec008000 0xec308000 + */ +#define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) +#define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) + +#define RDMA_SSIU_I_N(addr, i, j) (addr ##_reg - 0x00441000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4))) +#define RDMA_SSIU_O_N(addr, i, j) RDMA_SSIU_I_N(addr, i, j) + +#define RDMA_SSIU_I_P(addr, i, j) (addr ##_reg - 0x00141000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4))) +#define RDMA_SSIU_O_P(addr, i, j) RDMA_SSIU_I_P(addr, i, j) + +#define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) +#define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) + +#define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) +#define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) + +#define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) +#define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) + +static dma_addr_t +rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + int is_play, int is_from) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_BASE_SSI); + phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_BASE_SCU); + int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod) || + !!(rsnd_io_to_mod_ssiu(io) == mod); + int use_src = !!rsnd_io_to_mod_src(io); + int use_cmd = !!rsnd_io_to_mod_dvc(io) || + !!rsnd_io_to_mod_mix(io) || + !!rsnd_io_to_mod_ctu(io); + int id = rsnd_mod_id(mod); + int busif = rsnd_mod_id_sub(rsnd_io_to_mod_ssiu(io)); + struct dma_addr { + dma_addr_t out_addr; + dma_addr_t in_addr; + } dma_addrs[3][2][3] = { + /* SRC */ + /* Capture */ + {{{ 0, 0 }, + { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, + { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, + /* Playback */ + {{ 0, 0, }, + { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, + { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } + }, + /* SSI */ + /* Capture */ + {{{ RDMA_SSI_O_N(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id, busif), 0 }, + { RDMA_SSIU_O_P(ssi, id, busif), 0 } }, + /* Playback */ + {{ 0, RDMA_SSI_I_N(ssi, id) }, + { 0, RDMA_SSIU_I_P(ssi, id, busif) }, + { 0, RDMA_SSIU_I_P(ssi, id, busif) } } + }, + /* SSIU */ + /* Capture */ + {{{ RDMA_SSIU_O_N(ssi, id, busif), 0 }, + { RDMA_SSIU_O_P(ssi, id, busif), 0 }, + { RDMA_SSIU_O_P(ssi, id, busif), 0 } }, + /* Playback */ + {{ 0, RDMA_SSIU_I_N(ssi, id, busif) }, + { 0, RDMA_SSIU_I_P(ssi, id, busif) }, + { 0, RDMA_SSIU_I_P(ssi, id, busif) } } }, + }; + + /* + * FIXME + * + * We can't support SSI9-4/5/6/7, because its address is + * out of calculation rule + */ + if ((id == 9) && (busif >= 4)) + dev_err(dev, "This driver doesn't support SSI%d-%d, so far", + id, busif); + + /* it shouldn't happen */ + if (use_cmd && !use_src) + dev_err(dev, "DVC is selected without SRC\n"); + + /* use SSIU or SSI ? */ + if (is_ssi && rsnd_ssi_use_busif(io)) + is_ssi++; + + return (is_from) ? + dma_addrs[is_ssi][is_play][use_src + use_cmd].out_addr : + dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr; +} + +/* + * Gen4 DMA read/write register offset + * + * ex) R-Car V4H case + * mod / SYS-DMAC in / SYS-DMAC out + * SSI_SDMC: 0xec400000 / 0xec400000 / 0xec400000 + */ +#define RDMA_SSI_SDMC(addr, i) (addr + (0x8000 * i)) +static dma_addr_t +rsnd_gen4_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + int is_play, int is_from) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + phys_addr_t addr = rsnd_gen_get_phy_addr(priv, RSND_BASE_SDMC); + int id = rsnd_mod_id(mod); + int busif = rsnd_mod_id_sub(mod); + + /* + * SSI0 only is supported + */ + if (id != 0) { + struct device *dev = rsnd_priv_to_dev(priv); + + dev_err(dev, "This driver doesn't support non SSI0"); + return -EINVAL; + } + + return RDMA_SSI_SDMC(addr, busif); +} + +static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + int is_play, int is_from) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + + if (!mod) + return 0; + + /* + * gen1 uses default DMA addr + */ + if (rsnd_is_gen1(priv)) + return 0; + else if (rsnd_is_gen4(priv)) + return rsnd_gen4_dma_addr(io, mod, is_play, is_from); + else + return rsnd_gen2_dma_addr(io, mod, is_play, is_from); +} + +#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */ +static void rsnd_dma_of_path(struct rsnd_mod *this, + struct rsnd_dai_stream *io, + int is_play, + struct rsnd_mod **mod_from, + struct rsnd_mod **mod_to) +{ + struct rsnd_mod *ssi; + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io); + struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); + struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); + struct rsnd_mod *mod[MOD_MAX]; + struct rsnd_mod *mod_start, *mod_end; + struct rsnd_priv *priv = rsnd_mod_to_priv(this); + struct device *dev = rsnd_priv_to_dev(priv); + int nr, i, idx; + + /* + * It should use "rcar_sound,ssiu" on DT. + * But, we need to keep compatibility for old version. + * + * If it has "rcar_sound.ssiu", it will be used. + * If not, "rcar_sound.ssi" will be used. + * see + * rsnd_ssiu_dma_req() + * rsnd_ssi_dma_req() + */ + if (rsnd_ssiu_of_node(priv)) { + struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); + + /* use SSIU */ + ssi = ssiu; + if (this == rsnd_io_to_mod_ssi(io)) + this = ssiu; + } else { + /* keep compatible, use SSI */ + ssi = rsnd_io_to_mod_ssi(io); + } + + if (!ssi) + return; + + nr = 0; + for (i = 0; i < MOD_MAX; i++) { + mod[i] = NULL; + nr += !!rsnd_io_to_mod(io, i); + } + + /* + * [S] -*-> [E] + * [S] -*-> SRC -o-> [E] + * [S] -*-> SRC -> DVC -o-> [E] + * [S] -*-> SRC -> CTU -> MIX -> DVC -o-> [E] + * + * playback [S] = mem + * [E] = SSI + * + * capture [S] = SSI + * [E] = mem + * + * -*-> Audio DMAC + * -o-> Audio DMAC peri peri + */ + mod_start = (is_play) ? NULL : ssi; + mod_end = (is_play) ? ssi : NULL; + + idx = 0; + mod[idx++] = mod_start; + for (i = 1; i < nr; i++) { + if (src) { + mod[idx++] = src; + src = NULL; + } else if (ctu) { + mod[idx++] = ctu; + ctu = NULL; + } else if (mix) { + mod[idx++] = mix; + mix = NULL; + } else if (dvc) { + mod[idx++] = dvc; + dvc = NULL; + } + } + mod[idx] = mod_end; + + /* + * | SSI | SRC | + * -------------+-----+-----+ + * is_play | o | * | + * !is_play | * | o | + */ + if ((this == ssi) == (is_play)) { + *mod_from = mod[idx - 1]; + *mod_to = mod[idx]; + } else { + *mod_from = mod[0]; + *mod_to = mod[1]; + } + + dev_dbg(dev, "module connection (this is %s)\n", rsnd_mod_name(this)); + for (i = 0; i <= idx; i++) { + dev_dbg(dev, " %s%s\n", + rsnd_mod_name(mod[i] ? mod[i] : &mem), + (mod[i] == *mod_from) ? " from" : + (mod[i] == *mod_to) ? " to" : ""); + } +} + +static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + struct rsnd_mod **dma_mod) +{ + struct rsnd_mod *mod_from = NULL; + struct rsnd_mod *mod_to = NULL; + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dma *dma; + struct rsnd_mod_ops *ops; + enum rsnd_mod_type type; + int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); + int is_play = rsnd_io_is_play(io); + int ret, dma_id; + + /* + * DMA failed. try to PIO mode + * see + * rsnd_ssi_fallback() + * rsnd_rdai_continuance_probe() + */ + if (!dmac) + return -EAGAIN; + + rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to); + + /* for Gen2 or later */ + if (mod_from && mod_to) { + ops = &rsnd_dmapp_ops; + attach = rsnd_dmapp_attach; + dma_id = dmac->dmapp_num; + type = RSND_MOD_AUDMAPP; + } else { + ops = &rsnd_dmaen_ops; + attach = rsnd_dmaen_attach; + dma_id = dmac->dmaen_num; + type = RSND_MOD_AUDMA; + } + + /* for Gen1, overwrite */ + if (rsnd_is_gen1(priv)) { + ops = &rsnd_dmaen_ops; + attach = rsnd_dmaen_attach; + dma_id = dmac->dmaen_num; + type = RSND_MOD_AUDMA; + } + + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + *dma_mod = rsnd_mod_get(dma); + + ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, + type, dma_id); + if (ret < 0) + return ret; + + dev_dbg(dev, "%s %s -> %s\n", + rsnd_mod_name(*dma_mod), + rsnd_mod_name(mod_from ? mod_from : &mem), + rsnd_mod_name(mod_to ? mod_to : &mem)); + + ret = attach(io, dma, mod_from, mod_to); + if (ret < 0) + return ret; + + dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); + dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); + dma->mod_from = mod_from; + dma->mod_to = mod_to; + + return 0; +} + +int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + struct rsnd_mod **dma_mod) +{ + if (!(*dma_mod)) { + int ret = rsnd_dma_alloc(io, mod, dma_mod); + + if (ret < 0) + return ret; + } + + return rsnd_dai_connect(*dma_mod, io, (*dma_mod)->type); +} + +int rsnd_dma_probe(struct rsnd_priv *priv) +{ + struct platform_device *pdev = rsnd_priv_to_pdev(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dma_ctrl *dmac; + struct resource *res; + + /* + * for Gen1 + */ + if (rsnd_is_gen1(priv)) + return 0; + + /* + * for Gen2 or later + */ + dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL); + if (!dmac) { + dev_err(dev, "dma allocate failed\n"); + return 0; /* it will be PIO mode */ + } + + /* for Gen4 doesn't have DMA-pp */ + if (rsnd_is_gen4(priv)) + goto audmapp_end; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp"); + if (!res) { + dev_err(dev, "lack of audmapp in DT\n"); + return 0; /* it will be PIO mode */ + } + + dmac->dmapp_num = 0; + dmac->ppres = res->start; + dmac->ppbase = devm_ioremap_resource(dev, res); + if (IS_ERR(dmac->ppbase)) + return PTR_ERR(dmac->ppbase); +audmapp_end: + priv->dma = dmac; + + /* dummy mem mod for debug */ + return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, 0, 0); +} diff --git a/sound/soc/renesas/rcar/dvc.c b/sound/soc/renesas/rcar/dvc.c new file mode 100644 index 000000000000..da91dd301aab --- /dev/null +++ b/sound/soc/renesas/rcar/dvc.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car DVC support +// +// Copyright (C) 2014 Renesas Solutions Corp. +// Kuninori Morimoto + +/* + * Playback Volume + * amixer set "DVC Out" 100% + * + * Capture Volume + * amixer set "DVC In" 100% + * + * Playback Mute + * amixer set "DVC Out Mute" on + * + * Capture Mute + * amixer set "DVC In Mute" on + * + * Volume Ramp + * amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps" + * amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps" + * amixer set "DVC Out Ramp" on + * aplay xxx.wav & + * amixer set "DVC Out" 80% // Volume Down + * amixer set "DVC Out" 100% // Volume Up + */ + +#include "rsnd.h" + +#define RSND_DVC_NAME_SIZE 16 + +#define DVC_NAME "dvc" + +struct rsnd_dvc { + struct rsnd_mod mod; + struct rsnd_kctrl_cfg_m volume; + struct rsnd_kctrl_cfg_m mute; + struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ + struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ + struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ +}; + +#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id) +#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) + +#define rsnd_mod_to_dvc(_mod) \ + container_of((_mod), struct rsnd_dvc, mod) + +#define for_each_rsnd_dvc(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_dvc_nr(priv)) && \ + ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ + i++) + +static void rsnd_dvc_activation(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, DVC_SWRSR, 0); + rsnd_mod_write(mod, DVC_SWRSR, 1); +} + +static void rsnd_dvc_halt(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, DVC_DVUIR, 1); + rsnd_mod_write(mod, DVC_SWRSR, 0); +} + +#define rsnd_dvc_get_vrpdr(dvc) (rsnd_kctrl_vals(dvc->rup) << 8 | \ + rsnd_kctrl_vals(dvc->rdown)) +#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (rsnd_kctrl_valm(dvc->volume, 0) >> 13)) + +static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + u32 val[RSND_MAX_CHANNELS]; + int i; + + /* Enable Ramp */ + if (rsnd_kctrl_vals(dvc->ren)) + for (i = 0; i < RSND_MAX_CHANNELS; i++) + val[i] = rsnd_kctrl_max(dvc->volume); + else + for (i = 0; i < RSND_MAX_CHANNELS; i++) + val[i] = rsnd_kctrl_valm(dvc->volume, i); + + /* Enable Digital Volume */ + for (i = 0; i < RSND_MAX_CHANNELS; i++) + rsnd_mod_write(mod, DVC_VOLxR(i), val[i]); +} + +static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + u32 adinr = 0; + u32 dvucr = 0; + u32 vrctr = 0; + u32 vrpdr = 0; + u32 vrdbr = 0; + + adinr = rsnd_get_adinr_bit(mod, io) | + rsnd_runtime_channel_after_ctu(io); + + /* Enable Digital Volume, Zero Cross Mute Mode */ + dvucr |= 0x101; + + /* Enable Ramp */ + if (rsnd_kctrl_vals(dvc->ren)) { + dvucr |= 0x10; + + /* + * FIXME !! + * use scale-downed Digital Volume + * as Volume Ramp + * 7F FFFF -> 3FF + */ + vrctr = 0xff; + vrpdr = rsnd_dvc_get_vrpdr(dvc); + vrdbr = rsnd_dvc_get_vrdbr(dvc); + } + + /* Initialize operation */ + rsnd_mod_write(mod, DVC_DVUIR, 1); + + /* General Information */ + rsnd_mod_write(mod, DVC_ADINR, adinr); + rsnd_mod_write(mod, DVC_DVUCR, dvucr); + + /* Volume Ramp Parameter */ + rsnd_mod_write(mod, DVC_VRCTR, vrctr); + rsnd_mod_write(mod, DVC_VRPDR, vrpdr); + rsnd_mod_write(mod, DVC_VRDBR, vrdbr); + + /* Digital Volume Function Parameter */ + rsnd_dvc_volume_parameter(io, mod); + + /* cancel operation */ + rsnd_mod_write(mod, DVC_DVUIR, 0); +} + +static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + u32 zcmcr = 0; + u32 vrpdr = 0; + u32 vrdbr = 0; + int i; + + for (i = 0; i < rsnd_kctrl_size(dvc->mute); i++) + zcmcr |= (!!rsnd_kctrl_valm(dvc->mute, i)) << i; + + if (rsnd_kctrl_vals(dvc->ren)) { + vrpdr = rsnd_dvc_get_vrpdr(dvc); + vrdbr = rsnd_dvc_get_vrdbr(dvc); + } + + /* Disable DVC Register access */ + rsnd_mod_write(mod, DVC_DVUER, 0); + + /* Zero Cross Mute Function */ + rsnd_mod_write(mod, DVC_ZCMCR, zcmcr); + + /* Volume Ramp Function */ + rsnd_mod_write(mod, DVC_VRPDR, vrpdr); + rsnd_mod_write(mod, DVC_VRDBR, vrdbr); + /* add DVC_VRWTR here */ + + /* Digital Volume Function Parameter */ + rsnd_dvc_volume_parameter(io, mod); + + /* Enable DVC Register access */ + rsnd_mod_write(mod, DVC_DVUER, 1); +} + +static int rsnd_dvc_probe_(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + return rsnd_cmd_attach(io, rsnd_mod_id(mod)); +} + +static int rsnd_dvc_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + int ret; + + ret = rsnd_mod_power_on(mod); + if (ret < 0) + return ret; + + rsnd_dvc_activation(mod); + + rsnd_dvc_volume_init(io, mod); + + rsnd_dvc_volume_update(io, mod); + + return 0; +} + +static int rsnd_dvc_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_dvc_halt(mod); + + rsnd_mod_power_off(mod); + + return 0; +} + +static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + int is_play = rsnd_io_is_play(io); + int channels = rsnd_rdai_channels_get(rdai); + int ret; + + /* Volume */ + ret = rsnd_kctrl_new_m(mod, io, rtd, + is_play ? + "DVC Out Playback Volume" : "DVC In Capture Volume", + rsnd_kctrl_accept_anytime, + rsnd_dvc_volume_update, + &dvc->volume, channels, + 0x00800000 - 1); + if (ret < 0) + return ret; + + /* Mute */ + ret = rsnd_kctrl_new_m(mod, io, rtd, + is_play ? + "DVC Out Mute Switch" : "DVC In Mute Switch", + rsnd_kctrl_accept_anytime, + rsnd_dvc_volume_update, + &dvc->mute, channels, + 1); + if (ret < 0) + return ret; + + /* Ramp */ + ret = rsnd_kctrl_new_s(mod, io, rtd, + is_play ? + "DVC Out Ramp Switch" : "DVC In Ramp Switch", + rsnd_kctrl_accept_anytime, + rsnd_dvc_volume_update, + &dvc->ren, 1); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_e(mod, io, rtd, + is_play ? + "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", + rsnd_kctrl_accept_anytime, + rsnd_dvc_volume_update, + &dvc->rup, + volume_ramp_rate, + VOLUME_RAMP_MAX_DVC); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_e(mod, io, rtd, + is_play ? + "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", + rsnd_kctrl_accept_anytime, + rsnd_dvc_volume_update, + &dvc->rdown, + volume_ramp_rate, + VOLUME_RAMP_MAX_DVC); + + if (ret < 0) + return ret; + + return 0; +} + +static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + + return rsnd_dma_request_channel(rsnd_dvc_of_node(priv), + DVC_NAME, mod, "tx"); +} + +#ifdef CONFIG_DEBUG_FS +static void rsnd_dvc_debug_info(struct seq_file *m, + struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, + 0xe00 + rsnd_mod_id(mod) * 0x100, 0x60); +} +#define DEBUG_INFO .debug_info = rsnd_dvc_debug_info +#else +#define DEBUG_INFO +#endif + +static struct rsnd_mod_ops rsnd_dvc_ops = { + .name = DVC_NAME, + .dma_req = rsnd_dvc_dma_req, + .probe = rsnd_dvc_probe_, + .init = rsnd_dvc_init, + .quit = rsnd_dvc_quit, + .pcm_new = rsnd_dvc_pcm_new, + .get_status = rsnd_mod_get_status, + DEBUG_INFO +}; + +struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) + id = 0; + + return rsnd_mod_get(rsnd_dvc_get(priv, id)); +} + +int rsnd_dvc_probe(struct rsnd_priv *priv) +{ + struct device_node *node; + struct device_node *np; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dvc *dvc; + struct clk *clk; + char name[RSND_DVC_NAME_SIZE]; + int i, nr, ret; + + node = rsnd_dvc_of_node(priv); + if (!node) + return 0; /* not used is not error */ + + nr = of_get_child_count(node); + if (!nr) { + ret = -EINVAL; + goto rsnd_dvc_probe_done; + } + + dvc = devm_kcalloc(dev, nr, sizeof(*dvc), GFP_KERNEL); + if (!dvc) { + ret = -ENOMEM; + goto rsnd_dvc_probe_done; + } + + priv->dvc_nr = nr; + priv->dvc = dvc; + + i = 0; + ret = 0; + for_each_child_of_node(node, np) { + dvc = rsnd_dvc_get(priv, i); + + snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d", + DVC_NAME, i); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + of_node_put(np); + goto rsnd_dvc_probe_done; + } + + ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, + clk, RSND_MOD_DVC, i); + if (ret) { + of_node_put(np); + goto rsnd_dvc_probe_done; + } + + i++; + } + +rsnd_dvc_probe_done: + of_node_put(node); + + return ret; +} + +void rsnd_dvc_remove(struct rsnd_priv *priv) +{ + struct rsnd_dvc *dvc; + int i; + + for_each_rsnd_dvc(dvc, priv, i) { + rsnd_mod_quit(rsnd_mod_get(dvc)); + } +} diff --git a/sound/soc/renesas/rcar/gen.c b/sound/soc/renesas/rcar/gen.c new file mode 100644 index 000000000000..d1f20cde66be --- /dev/null +++ b/sound/soc/renesas/rcar/gen.c @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car Gen1 SRU/SSI support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto + +/* + * #define DEBUG + * + * you can also add below in + * ${LINUX}/drivers/base/regmap/regmap.c + * for regmap debug + * + * #define LOG_DEVICE "xxxx.rcar_sound" + */ + +#include "rsnd.h" + +struct rsnd_gen { + struct rsnd_gen_ops *ops; + + /* RSND_BASE_MAX base */ + void __iomem *base[RSND_BASE_MAX]; + phys_addr_t res[RSND_BASE_MAX]; + struct regmap *regmap[RSND_BASE_MAX]; + + /* RSND_REG_MAX base */ + struct regmap_field *regs[REG_MAX]; + const char *reg_name[REG_MAX]; +}; + +#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) +#define rsnd_reg_name(gen, id) ((gen)->reg_name[id]) + +struct rsnd_regmap_field_conf { + int idx; + unsigned int reg_offset; + unsigned int id_offset; + const char *reg_name; +}; + +#define RSND_REG_SET(id, offset, _id_offset, n) \ +{ \ + .idx = id, \ + .reg_offset = offset, \ + .id_offset = _id_offset, \ + .reg_name = n, \ +} +/* single address mapping */ +#define RSND_GEN_S_REG(id, offset) \ + RSND_REG_SET(id, offset, 0, #id) + +/* multi address mapping */ +#define RSND_GEN_M_REG(id, offset, _id_offset) \ + RSND_REG_SET(id, offset, _id_offset, #id) + +/* + * basic function + */ +static int rsnd_is_accessible_reg(struct rsnd_priv *priv, + struct rsnd_gen *gen, enum rsnd_reg reg) +{ + if (!gen->regs[reg]) { + struct device *dev = rsnd_priv_to_dev(priv); + + dev_err(dev, "unsupported register access %x\n", reg); + return 0; + } + + return 1; +} + +static int rsnd_mod_id_cmd(struct rsnd_mod *mod) +{ + if (mod->ops->id_cmd) + return mod->ops->id_cmd(mod); + + return rsnd_mod_id(mod); +} + +u32 rsnd_mod_read(struct rsnd_mod *mod, enum rsnd_reg reg) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + u32 val; + + if (!rsnd_is_accessible_reg(priv, gen, reg)) + return 0; + + regmap_fields_read(gen->regs[reg], rsnd_mod_id_cmd(mod), &val); + + dev_dbg(dev, "r %s - %-18s (%4d) : %08x\n", + rsnd_mod_name(mod), + rsnd_reg_name(gen, reg), reg, val); + + return val; +} + +void rsnd_mod_write(struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + if (!rsnd_is_accessible_reg(priv, gen, reg)) + return; + + regmap_fields_force_write(gen->regs[reg], rsnd_mod_id_cmd(mod), data); + + dev_dbg(dev, "w %s - %-18s (%4d) : %08x\n", + rsnd_mod_name(mod), + rsnd_reg_name(gen, reg), reg, data); +} + +void rsnd_mod_bset(struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + if (!rsnd_is_accessible_reg(priv, gen, reg)) + return; + + regmap_fields_force_update_bits(gen->regs[reg], + rsnd_mod_id_cmd(mod), mask, data); + + dev_dbg(dev, "b %s - %-18s (%4d) : %08x/%08x\n", + rsnd_mod_name(mod), + rsnd_reg_name(gen, reg), reg, data, mask); + +} + +phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->res[reg_id]; +} + +#ifdef CONFIG_DEBUG_FS +void __iomem *rsnd_gen_get_base_addr(struct rsnd_priv *priv, int reg_id) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->base[reg_id]; +} +#endif + +#define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf) \ + _rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf)) +static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, + int id_size, + int reg_id, + const char *name, + const struct rsnd_regmap_field_conf *conf, + int conf_size) +{ + struct platform_device *pdev = rsnd_priv_to_pdev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct resource *res; + struct regmap_config regc; + struct regmap_field *regs; + struct regmap *regmap; + struct reg_field regf; + void __iomem *base; + int i; + + memset(®c, 0, sizeof(regc)); + regc.reg_bits = 32; + regc.val_bits = 32; + regc.reg_stride = 4; + regc.name = name; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) + return -ENODEV; + + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, ®c); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* RSND_BASE_MAX base */ + gen->base[reg_id] = base; + gen->regmap[reg_id] = regmap; + gen->res[reg_id] = res->start; + + for (i = 0; i < conf_size; i++) { + + regf.reg = conf[i].reg_offset; + regf.id_offset = conf[i].id_offset; + regf.lsb = 0; + regf.msb = 31; + regf.id_size = id_size; + + regs = devm_regmap_field_alloc(dev, regmap, regf); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + /* RSND_REG_MAX base */ + gen->regs[conf[i].idx] = regs; + gen->reg_name[conf[i].idx] = conf[i].reg_name; + } + + return 0; +} + +/* + * (A) : Gen4 is 0xa0c, but it is not used. + * see + * rsnd_ssiu_init() + */ +static const struct rsnd_regmap_field_conf conf_common_ssiu[] = { + RSND_GEN_S_REG(SSI_MODE0, 0x800), + RSND_GEN_S_REG(SSI_MODE1, 0x804), + RSND_GEN_S_REG(SSI_MODE2, 0x808), // (A) + RSND_GEN_S_REG(SSI_CONTROL, 0x810), + RSND_GEN_S_REG(SSI_SYS_STATUS0, 0x840), + RSND_GEN_S_REG(SSI_SYS_STATUS1, 0x844), + RSND_GEN_S_REG(SSI_SYS_STATUS2, 0x848), + RSND_GEN_S_REG(SSI_SYS_STATUS3, 0x84c), + RSND_GEN_S_REG(SSI_SYS_STATUS4, 0x880), + RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884), + RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888), + RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE0, 0x850), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE1, 0x854), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE2, 0x858), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE3, 0x85c), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE4, 0x890), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE5, 0x894), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE6, 0x898), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE7, 0x89c), + RSND_GEN_S_REG(HDMI0_SEL, 0x9e0), + RSND_GEN_S_REG(HDMI1_SEL, 0x9e4), + RSND_GEN_M_REG(SSI_BUSIF0_MODE, 0x0, 0x80), + RSND_GEN_M_REG(SSI_BUSIF0_ADINR, 0x4, 0x80), + RSND_GEN_M_REG(SSI_BUSIF0_DALIGN, 0x8, 0x80), + RSND_GEN_M_REG(SSI_BUSIF1_MODE, 0x20, 0x80), + RSND_GEN_M_REG(SSI_BUSIF1_ADINR, 0x24, 0x80), + RSND_GEN_M_REG(SSI_BUSIF1_DALIGN, 0x28, 0x80), + RSND_GEN_M_REG(SSI_BUSIF2_MODE, 0x40, 0x80), + RSND_GEN_M_REG(SSI_BUSIF2_ADINR, 0x44, 0x80), + RSND_GEN_M_REG(SSI_BUSIF2_DALIGN, 0x48, 0x80), + RSND_GEN_M_REG(SSI_BUSIF3_MODE, 0x60, 0x80), + RSND_GEN_M_REG(SSI_BUSIF3_ADINR, 0x64, 0x80), + RSND_GEN_M_REG(SSI_BUSIF3_DALIGN, 0x68, 0x80), + RSND_GEN_M_REG(SSI_BUSIF4_MODE, 0x500, 0x80), + RSND_GEN_M_REG(SSI_BUSIF4_ADINR, 0x504, 0x80), + RSND_GEN_M_REG(SSI_BUSIF4_DALIGN, 0x508, 0x80), + RSND_GEN_M_REG(SSI_BUSIF5_MODE, 0x520, 0x80), + RSND_GEN_M_REG(SSI_BUSIF5_ADINR, 0x524, 0x80), + RSND_GEN_M_REG(SSI_BUSIF5_DALIGN, 0x528, 0x80), + RSND_GEN_M_REG(SSI_BUSIF6_MODE, 0x540, 0x80), + RSND_GEN_M_REG(SSI_BUSIF6_ADINR, 0x544, 0x80), + RSND_GEN_M_REG(SSI_BUSIF6_DALIGN, 0x548, 0x80), + RSND_GEN_M_REG(SSI_BUSIF7_MODE, 0x560, 0x80), + RSND_GEN_M_REG(SSI_BUSIF7_ADINR, 0x564, 0x80), + RSND_GEN_M_REG(SSI_BUSIF7_DALIGN, 0x568, 0x80), + RSND_GEN_M_REG(SSI_MODE, 0xc, 0x80), + RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80), + RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80), + RSND_GEN_S_REG(SSI9_BUSIF0_MODE, 0x48c), + RSND_GEN_S_REG(SSI9_BUSIF0_ADINR, 0x484), + RSND_GEN_S_REG(SSI9_BUSIF0_DALIGN, 0x488), + RSND_GEN_S_REG(SSI9_BUSIF1_MODE, 0x4a0), + RSND_GEN_S_REG(SSI9_BUSIF1_ADINR, 0x4a4), + RSND_GEN_S_REG(SSI9_BUSIF1_DALIGN, 0x4a8), + RSND_GEN_S_REG(SSI9_BUSIF2_MODE, 0x4c0), + RSND_GEN_S_REG(SSI9_BUSIF2_ADINR, 0x4c4), + RSND_GEN_S_REG(SSI9_BUSIF2_DALIGN, 0x4c8), + RSND_GEN_S_REG(SSI9_BUSIF3_MODE, 0x4e0), + RSND_GEN_S_REG(SSI9_BUSIF3_ADINR, 0x4e4), + RSND_GEN_S_REG(SSI9_BUSIF3_DALIGN, 0x4e8), + RSND_GEN_S_REG(SSI9_BUSIF4_MODE, 0xd80), + RSND_GEN_S_REG(SSI9_BUSIF4_ADINR, 0xd84), + RSND_GEN_S_REG(SSI9_BUSIF4_DALIGN, 0xd88), + RSND_GEN_S_REG(SSI9_BUSIF5_MODE, 0xda0), + RSND_GEN_S_REG(SSI9_BUSIF5_ADINR, 0xda4), + RSND_GEN_S_REG(SSI9_BUSIF5_DALIGN, 0xda8), + RSND_GEN_S_REG(SSI9_BUSIF6_MODE, 0xdc0), + RSND_GEN_S_REG(SSI9_BUSIF6_ADINR, 0xdc4), + RSND_GEN_S_REG(SSI9_BUSIF6_DALIGN, 0xdc8), + RSND_GEN_S_REG(SSI9_BUSIF7_MODE, 0xde0), + RSND_GEN_S_REG(SSI9_BUSIF7_ADINR, 0xde4), + RSND_GEN_S_REG(SSI9_BUSIF7_DALIGN, 0xde8), +}; + +static const struct rsnd_regmap_field_conf conf_common_scu[] = { + RSND_GEN_M_REG(SRC_I_BUSIF_MODE, 0x0, 0x20), + RSND_GEN_M_REG(SRC_O_BUSIF_MODE, 0x4, 0x20), + RSND_GEN_M_REG(SRC_BUSIF_DALIGN, 0x8, 0x20), + RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), + RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), + RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), + RSND_GEN_M_REG(CMD_BUSIF_MODE, 0x184, 0x20), + RSND_GEN_M_REG(CMD_BUSIF_DALIGN, 0x188, 0x20), + RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20), + RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20), + RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), + RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc), + RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0), + RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4), + RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), + RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), + RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), + RSND_GEN_M_REG(SRC_IFSCR, 0x21c, 0x40), + RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40), + RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), + RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40), + RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40), + RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100), + RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100), + RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100), + RSND_GEN_M_REG(CTU_CPMDR, 0x510, 0x100), + RSND_GEN_M_REG(CTU_SCMDR, 0x514, 0x100), + RSND_GEN_M_REG(CTU_SV00R, 0x518, 0x100), + RSND_GEN_M_REG(CTU_SV01R, 0x51c, 0x100), + RSND_GEN_M_REG(CTU_SV02R, 0x520, 0x100), + RSND_GEN_M_REG(CTU_SV03R, 0x524, 0x100), + RSND_GEN_M_REG(CTU_SV04R, 0x528, 0x100), + RSND_GEN_M_REG(CTU_SV05R, 0x52c, 0x100), + RSND_GEN_M_REG(CTU_SV06R, 0x530, 0x100), + RSND_GEN_M_REG(CTU_SV07R, 0x534, 0x100), + RSND_GEN_M_REG(CTU_SV10R, 0x538, 0x100), + RSND_GEN_M_REG(CTU_SV11R, 0x53c, 0x100), + RSND_GEN_M_REG(CTU_SV12R, 0x540, 0x100), + RSND_GEN_M_REG(CTU_SV13R, 0x544, 0x100), + RSND_GEN_M_REG(CTU_SV14R, 0x548, 0x100), + RSND_GEN_M_REG(CTU_SV15R, 0x54c, 0x100), + RSND_GEN_M_REG(CTU_SV16R, 0x550, 0x100), + RSND_GEN_M_REG(CTU_SV17R, 0x554, 0x100), + RSND_GEN_M_REG(CTU_SV20R, 0x558, 0x100), + RSND_GEN_M_REG(CTU_SV21R, 0x55c, 0x100), + RSND_GEN_M_REG(CTU_SV22R, 0x560, 0x100), + RSND_GEN_M_REG(CTU_SV23R, 0x564, 0x100), + RSND_GEN_M_REG(CTU_SV24R, 0x568, 0x100), + RSND_GEN_M_REG(CTU_SV25R, 0x56c, 0x100), + RSND_GEN_M_REG(CTU_SV26R, 0x570, 0x100), + RSND_GEN_M_REG(CTU_SV27R, 0x574, 0x100), + RSND_GEN_M_REG(CTU_SV30R, 0x578, 0x100), + RSND_GEN_M_REG(CTU_SV31R, 0x57c, 0x100), + RSND_GEN_M_REG(CTU_SV32R, 0x580, 0x100), + RSND_GEN_M_REG(CTU_SV33R, 0x584, 0x100), + RSND_GEN_M_REG(CTU_SV34R, 0x588, 0x100), + RSND_GEN_M_REG(CTU_SV35R, 0x58c, 0x100), + RSND_GEN_M_REG(CTU_SV36R, 0x590, 0x100), + RSND_GEN_M_REG(CTU_SV37R, 0x594, 0x100), + RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40), + RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40), + RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40), + RSND_GEN_M_REG(MIX_MIXMR, 0xd10, 0x40), + RSND_GEN_M_REG(MIX_MVPDR, 0xd14, 0x40), + RSND_GEN_M_REG(MIX_MDBAR, 0xd18, 0x40), + RSND_GEN_M_REG(MIX_MDBBR, 0xd1c, 0x40), + RSND_GEN_M_REG(MIX_MDBCR, 0xd20, 0x40), + RSND_GEN_M_REG(MIX_MDBDR, 0xd24, 0x40), + RSND_GEN_M_REG(MIX_MDBER, 0xd28, 0x40), + RSND_GEN_M_REG(DVC_SWRSR, 0xe00, 0x100), + RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100), + RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100), + RSND_GEN_M_REG(DVC_DVUCR, 0xe10, 0x100), + RSND_GEN_M_REG(DVC_ZCMCR, 0xe14, 0x100), + RSND_GEN_M_REG(DVC_VRCTR, 0xe18, 0x100), + RSND_GEN_M_REG(DVC_VRPDR, 0xe1c, 0x100), + RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100), + RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100), + RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100), + RSND_GEN_M_REG(DVC_VOL2R, 0xe30, 0x100), + RSND_GEN_M_REG(DVC_VOL3R, 0xe34, 0x100), + RSND_GEN_M_REG(DVC_VOL4R, 0xe38, 0x100), + RSND_GEN_M_REG(DVC_VOL5R, 0xe3c, 0x100), + RSND_GEN_M_REG(DVC_VOL6R, 0xe40, 0x100), + RSND_GEN_M_REG(DVC_VOL7R, 0xe44, 0x100), + RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100), +}; + +static const struct rsnd_regmap_field_conf conf_common_adg[] = { + RSND_GEN_S_REG(BRRA, 0x00), + RSND_GEN_S_REG(BRRB, 0x04), + RSND_GEN_S_REG(BRGCKR, 0x08), + RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c), + RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10), + RSND_GEN_S_REG(AUDIO_CLK_SEL2, 0x14), + RSND_GEN_S_REG(DIV_EN, 0x30), + RSND_GEN_S_REG(SRCIN_TIMSEL0, 0x34), + RSND_GEN_S_REG(SRCIN_TIMSEL1, 0x38), + RSND_GEN_S_REG(SRCIN_TIMSEL2, 0x3c), + RSND_GEN_S_REG(SRCIN_TIMSEL3, 0x40), + RSND_GEN_S_REG(SRCIN_TIMSEL4, 0x44), + RSND_GEN_S_REG(SRCOUT_TIMSEL0, 0x48), + RSND_GEN_S_REG(SRCOUT_TIMSEL1, 0x4c), + RSND_GEN_S_REG(SRCOUT_TIMSEL2, 0x50), + RSND_GEN_S_REG(SRCOUT_TIMSEL3, 0x54), + RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58), + RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c), +}; + +static const struct rsnd_regmap_field_conf conf_common_ssi[] = { + RSND_GEN_M_REG(SSICR, 0x00, 0x40), + RSND_GEN_M_REG(SSISR, 0x04, 0x40), + RSND_GEN_M_REG(SSITDR, 0x08, 0x40), + RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40), + RSND_GEN_M_REG(SSIWSR, 0x20, 0x40), +}; + +/* + * Gen4 + */ +static int rsnd_gen4_probe(struct rsnd_priv *priv) +{ + struct rsnd_regmap_field_conf conf_null[] = { }; + + /* + * ssiu: SSIU0 + * ssi : SSI0 + */ + int ret_ssiu = rsnd_gen_regmap_init(priv, 1, RSND_BASE_SSIU, "ssiu", conf_common_ssiu); + int ret_ssi = rsnd_gen_regmap_init(priv, 1, RSND_BASE_SSI, "ssi", conf_common_ssi); + int ret_adg = rsnd_gen_regmap_init(priv, 1, RSND_BASE_ADG, "adg", conf_common_adg); + int ret_sdmc = rsnd_gen_regmap_init(priv, 1, RSND_BASE_SDMC, "sdmc", conf_null); + + return ret_adg | ret_ssiu | ret_ssi | ret_sdmc; +} + +/* + * Gen2 + */ +static int rsnd_gen2_probe(struct rsnd_priv *priv) +{ + /* + * ssi : SSI0 - SSI9 + * ssiu: SSIU0 - SSIU9 + * scu : SRC0 - SRC9 etc + */ + int ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SSI, "ssi", conf_common_ssi); + int ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SSIU, "ssiu", conf_common_ssiu); + int ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SCU, "scu", conf_common_scu); + int ret_adg = rsnd_gen_regmap_init(priv, 1, RSND_BASE_ADG, "adg", conf_common_adg); + + return ret_ssi | ret_ssiu | ret_scu | ret_adg; +} + +/* + * Gen1 + */ + +static int rsnd_gen1_probe(struct rsnd_priv *priv) +{ + /* + * ssi : SSI0 - SSI8 + */ + int ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_BASE_SSI, "ssi", conf_common_ssi); + int ret_adg = rsnd_gen_regmap_init(priv, 1, RSND_BASE_ADG, "adg", conf_common_adg); + + return ret_adg | ret_ssi; +} + +/* + * Gen + */ +int rsnd_gen_probe(struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen; + int ret; + + gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); + if (!gen) + return -ENOMEM; + + priv->gen = gen; + + ret = -ENODEV; + if (rsnd_is_gen1(priv)) + ret = rsnd_gen1_probe(priv); + else if (rsnd_is_gen2(priv) || + rsnd_is_gen3(priv)) + ret = rsnd_gen2_probe(priv); + else if (rsnd_is_gen4(priv)) + ret = rsnd_gen4_probe(priv); + + if (ret < 0) + dev_err(dev, "unknown generation R-Car sound device\n"); + + return ret; +} diff --git a/sound/soc/renesas/rcar/mix.c b/sound/soc/renesas/rcar/mix.c new file mode 100644 index 000000000000..024d91cc8748 --- /dev/null +++ b/sound/soc/renesas/rcar/mix.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mix.c +// +// Copyright (c) 2015 Kuninori Morimoto + +/* + * CTUn MIXn + * +------+ +------+ + * [SRC3 / SRC6] -> |CTU n0| -> [MIX n0| -> + * [SRC4 / SRC9] -> |CTU n1| -> [MIX n1| -> + * [SRC0 / SRC1] -> |CTU n2| -> [MIX n2| -> + * [SRC2 / SRC5] -> |CTU n3| -> [MIX n3| -> + * +------+ +------+ + * + * ex) + * DAI0 : playback = <&src0 &ctu02 &mix0 &dvc0 &ssi0>; + * DAI1 : playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>; + * + * MIX Volume + * amixer set "MIX",0 100% // DAI0 Volume + * amixer set "MIX",1 100% // DAI1 Volume + * + * Volume Ramp + * amixer set "MIX Ramp Up Rate" "0.125 dB/1 step" + * amixer set "MIX Ramp Down Rate" "4 dB/1 step" + * amixer set "MIX Ramp" on + * aplay xxx.wav & + * amixer set "MIX",0 80% // DAI0 Volume Down + * amixer set "MIX",1 100% // DAI1 Volume Up + */ + +#include "rsnd.h" + +#define MIX_NAME_SIZE 16 +#define MIX_NAME "mix" + +struct rsnd_mix { + struct rsnd_mod mod; + struct rsnd_kctrl_cfg_s volumeA; /* MDBAR */ + struct rsnd_kctrl_cfg_s volumeB; /* MDBBR */ + struct rsnd_kctrl_cfg_s volumeC; /* MDBCR */ + struct rsnd_kctrl_cfg_s volumeD; /* MDBDR */ + struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ + struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ + struct rsnd_kctrl_cfg_s rdw; /* Ramp Rate Down */ + u32 flags; +}; + +#define ONCE_KCTRL_INITIALIZED (1 << 0) +#define HAS_VOLA (1 << 1) +#define HAS_VOLB (1 << 2) +#define HAS_VOLC (1 << 3) +#define HAS_VOLD (1 << 4) + +#define VOL_MAX 0x3ff + +#define rsnd_mod_to_mix(_mod) \ + container_of((_mod), struct rsnd_mix, mod) + +#define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id) +#define rsnd_mix_nr(priv) ((priv)->mix_nr) +#define for_each_rsnd_mix(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_mix_nr(priv)) && \ + ((pos) = (struct rsnd_mix *)(priv)->mix + i); \ + i++) + +static void rsnd_mix_activation(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, MIX_SWRSR, 0); + rsnd_mod_write(mod, MIX_SWRSR, 1); +} + +static void rsnd_mix_halt(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, MIX_MIXIR, 1); + rsnd_mod_write(mod, MIX_SWRSR, 0); +} + +#define rsnd_mix_get_vol(mix, X) \ + rsnd_flags_has(mix, HAS_VOL##X) ? \ + (VOL_MAX - rsnd_kctrl_vals(mix->volume##X)) : 0 +static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mix *mix = rsnd_mod_to_mix(mod); + u32 volA = rsnd_mix_get_vol(mix, A); + u32 volB = rsnd_mix_get_vol(mix, B); + u32 volC = rsnd_mix_get_vol(mix, C); + u32 volD = rsnd_mix_get_vol(mix, D); + + dev_dbg(dev, "MIX A/B/C/D = %02x/%02x/%02x/%02x\n", + volA, volB, volC, volD); + + rsnd_mod_write(mod, MIX_MDBAR, volA); + rsnd_mod_write(mod, MIX_MDBBR, volB); + rsnd_mod_write(mod, MIX_MDBCR, volC); + rsnd_mod_write(mod, MIX_MDBDR, volD); +} + +static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_mix *mix = rsnd_mod_to_mix(mod); + + rsnd_mod_write(mod, MIX_MIXIR, 1); + + /* General Information */ + rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); + + /* volume step */ + rsnd_mod_write(mod, MIX_MIXMR, rsnd_kctrl_vals(mix->ren)); + rsnd_mod_write(mod, MIX_MVPDR, rsnd_kctrl_vals(mix->rup) << 8 | + rsnd_kctrl_vals(mix->rdw)); + + /* common volume parameter */ + rsnd_mix_volume_parameter(io, mod); + + rsnd_mod_write(mod, MIX_MIXIR, 0); +} + +static void rsnd_mix_volume_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + /* Disable MIX dB setting */ + rsnd_mod_write(mod, MIX_MDBER, 0); + + /* common volume parameter */ + rsnd_mix_volume_parameter(io, mod); + + /* Enable MIX dB setting */ + rsnd_mod_write(mod, MIX_MDBER, 1); +} + +static int rsnd_mix_probe_(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + return rsnd_cmd_attach(io, rsnd_mod_id(mod)); +} + +static int rsnd_mix_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + int ret; + + ret = rsnd_mod_power_on(mod); + if (ret < 0) + return ret; + + rsnd_mix_activation(mod); + + rsnd_mix_volume_init(io, mod); + + rsnd_mix_volume_update(io, mod); + + return 0; +} + +static int rsnd_mix_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mix_halt(mod); + + rsnd_mod_power_off(mod); + + return 0; +} + +static int rsnd_mix_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mix *mix = rsnd_mod_to_mix(mod); + struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); + struct rsnd_kctrl_cfg_s *volume; + int ret; + + switch (rsnd_mod_id(src_mod)) { + case 3: + case 6: /* MDBAR */ + volume = &mix->volumeA; + rsnd_flags_set(mix, HAS_VOLA); + break; + case 4: + case 9: /* MDBBR */ + volume = &mix->volumeB; + rsnd_flags_set(mix, HAS_VOLB); + break; + case 0: + case 1: /* MDBCR */ + volume = &mix->volumeC; + rsnd_flags_set(mix, HAS_VOLC); + break; + case 2: + case 5: /* MDBDR */ + volume = &mix->volumeD; + rsnd_flags_set(mix, HAS_VOLD); + break; + default: + dev_err(dev, "unknown SRC is connected\n"); + return -EINVAL; + } + + /* Volume */ + ret = rsnd_kctrl_new_s(mod, io, rtd, + "MIX Playback Volume", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + volume, VOL_MAX); + if (ret < 0) + return ret; + rsnd_kctrl_vals(*volume) = VOL_MAX; + + if (rsnd_flags_has(mix, ONCE_KCTRL_INITIALIZED)) + return ret; + + /* Ramp */ + ret = rsnd_kctrl_new_s(mod, io, rtd, + "MIX Ramp Switch", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + &mix->ren, 1); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_e(mod, io, rtd, + "MIX Ramp Up Rate", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + &mix->rup, + volume_ramp_rate, + VOLUME_RAMP_MAX_MIX); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_e(mod, io, rtd, + "MIX Ramp Down Rate", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + &mix->rdw, + volume_ramp_rate, + VOLUME_RAMP_MAX_MIX); + + rsnd_flags_set(mix, ONCE_KCTRL_INITIALIZED); + + return ret; +} + +#ifdef CONFIG_DEBUG_FS +static void rsnd_mix_debug_info(struct seq_file *m, + struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, + 0xd00 + rsnd_mod_id(mod) * 0x40, 0x30); +} +#define DEBUG_INFO .debug_info = rsnd_mix_debug_info +#else +#define DEBUG_INFO +#endif + +static struct rsnd_mod_ops rsnd_mix_ops = { + .name = MIX_NAME, + .probe = rsnd_mix_probe_, + .init = rsnd_mix_init, + .quit = rsnd_mix_quit, + .pcm_new = rsnd_mix_pcm_new, + .get_status = rsnd_mod_get_status, + DEBUG_INFO +}; + +struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv))) + id = 0; + + return rsnd_mod_get(rsnd_mix_get(priv, id)); +} + +int rsnd_mix_probe(struct rsnd_priv *priv) +{ + struct device_node *node; + struct device_node *np; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mix *mix; + struct clk *clk; + char name[MIX_NAME_SIZE]; + int i, nr, ret; + + node = rsnd_mix_of_node(priv); + if (!node) + return 0; /* not used is not error */ + + nr = of_get_child_count(node); + if (!nr) { + ret = -EINVAL; + goto rsnd_mix_probe_done; + } + + mix = devm_kcalloc(dev, nr, sizeof(*mix), GFP_KERNEL); + if (!mix) { + ret = -ENOMEM; + goto rsnd_mix_probe_done; + } + + priv->mix_nr = nr; + priv->mix = mix; + + i = 0; + ret = 0; + for_each_child_of_node(node, np) { + mix = rsnd_mix_get(priv, i); + + snprintf(name, MIX_NAME_SIZE, "%s.%d", + MIX_NAME, i); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + of_node_put(np); + goto rsnd_mix_probe_done; + } + + ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, + clk, RSND_MOD_MIX, i); + if (ret) { + of_node_put(np); + goto rsnd_mix_probe_done; + } + + i++; + } + +rsnd_mix_probe_done: + of_node_put(node); + + return ret; +} + +void rsnd_mix_remove(struct rsnd_priv *priv) +{ + struct rsnd_mix *mix; + int i; + + for_each_rsnd_mix(mix, priv, i) { + rsnd_mod_quit(rsnd_mod_get(mix)); + } +} diff --git a/sound/soc/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h new file mode 100644 index 000000000000..3c164d8e3b16 --- /dev/null +++ b/sound/soc/renesas/rcar/rsnd.h @@ -0,0 +1,896 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto + +#ifndef RSND_H +#define RSND_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RSND_BASE_ADG 0 +#define RSND_BASE_SSI 1 +#define RSND_BASE_SSIU 2 +#define RSND_BASE_SCU 3 // for Gen2/Gen3 +#define RSND_BASE_SDMC 3 // for Gen4 reuse +#define RSND_BASE_MAX 4 + +/* + * pseudo register + * + * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different. + * This driver uses pseudo register in order to hide it. + * see gen1/gen2 for detail + */ +enum rsnd_reg { + /* SCU (MIX/CTU/DVC) */ + SRC_I_BUSIF_MODE, + SRC_O_BUSIF_MODE, + SRC_ROUTE_MODE0, + SRC_SWRSR, + SRC_SRCIR, + SRC_ADINR, + SRC_IFSCR, + SRC_IFSVR, + SRC_SRCCR, + SRC_CTRL, + SRC_BSDSR, + SRC_BSISR, + SRC_INT_ENABLE0, + SRC_BUSIF_DALIGN, + SRCIN_TIMSEL0, + SRCIN_TIMSEL1, + SRCIN_TIMSEL2, + SRCIN_TIMSEL3, + SRCIN_TIMSEL4, + SRCOUT_TIMSEL0, + SRCOUT_TIMSEL1, + SRCOUT_TIMSEL2, + SRCOUT_TIMSEL3, + SRCOUT_TIMSEL4, + SCU_SYS_STATUS0, + SCU_SYS_STATUS1, + SCU_SYS_INT_EN0, + SCU_SYS_INT_EN1, + CMD_CTRL, + CMD_BUSIF_MODE, + CMD_BUSIF_DALIGN, + CMD_ROUTE_SLCT, + CMDOUT_TIMSEL, + CTU_SWRSR, + CTU_CTUIR, + CTU_ADINR, + CTU_CPMDR, + CTU_SCMDR, + CTU_SV00R, + CTU_SV01R, + CTU_SV02R, + CTU_SV03R, + CTU_SV04R, + CTU_SV05R, + CTU_SV06R, + CTU_SV07R, + CTU_SV10R, + CTU_SV11R, + CTU_SV12R, + CTU_SV13R, + CTU_SV14R, + CTU_SV15R, + CTU_SV16R, + CTU_SV17R, + CTU_SV20R, + CTU_SV21R, + CTU_SV22R, + CTU_SV23R, + CTU_SV24R, + CTU_SV25R, + CTU_SV26R, + CTU_SV27R, + CTU_SV30R, + CTU_SV31R, + CTU_SV32R, + CTU_SV33R, + CTU_SV34R, + CTU_SV35R, + CTU_SV36R, + CTU_SV37R, + MIX_SWRSR, + MIX_MIXIR, + MIX_ADINR, + MIX_MIXMR, + MIX_MVPDR, + MIX_MDBAR, + MIX_MDBBR, + MIX_MDBCR, + MIX_MDBDR, + MIX_MDBER, + DVC_SWRSR, + DVC_DVUIR, + DVC_ADINR, + DVC_DVUCR, + DVC_ZCMCR, + DVC_VOL0R, + DVC_VOL1R, + DVC_VOL2R, + DVC_VOL3R, + DVC_VOL4R, + DVC_VOL5R, + DVC_VOL6R, + DVC_VOL7R, + DVC_DVUER, + DVC_VRCTR, + DVC_VRPDR, + DVC_VRDBR, + + /* ADG */ + BRRA, + BRRB, + BRGCKR, + DIV_EN, + AUDIO_CLK_SEL0, + AUDIO_CLK_SEL1, + AUDIO_CLK_SEL2, + + /* SSIU */ + SSI_MODE, + SSI_MODE0, + SSI_MODE1, + SSI_MODE2, + SSI_CONTROL, + SSI_CTRL, + SSI_BUSIF0_MODE, + SSI_BUSIF1_MODE, + SSI_BUSIF2_MODE, + SSI_BUSIF3_MODE, + SSI_BUSIF4_MODE, + SSI_BUSIF5_MODE, + SSI_BUSIF6_MODE, + SSI_BUSIF7_MODE, + SSI_BUSIF0_ADINR, + SSI_BUSIF1_ADINR, + SSI_BUSIF2_ADINR, + SSI_BUSIF3_ADINR, + SSI_BUSIF4_ADINR, + SSI_BUSIF5_ADINR, + SSI_BUSIF6_ADINR, + SSI_BUSIF7_ADINR, + SSI_BUSIF0_DALIGN, + SSI_BUSIF1_DALIGN, + SSI_BUSIF2_DALIGN, + SSI_BUSIF3_DALIGN, + SSI_BUSIF4_DALIGN, + SSI_BUSIF5_DALIGN, + SSI_BUSIF6_DALIGN, + SSI_BUSIF7_DALIGN, + SSI_INT_ENABLE, + SSI_SYS_STATUS0, + SSI_SYS_STATUS1, + SSI_SYS_STATUS2, + SSI_SYS_STATUS3, + SSI_SYS_STATUS4, + SSI_SYS_STATUS5, + SSI_SYS_STATUS6, + SSI_SYS_STATUS7, + SSI_SYS_INT_ENABLE0, + SSI_SYS_INT_ENABLE1, + SSI_SYS_INT_ENABLE2, + SSI_SYS_INT_ENABLE3, + SSI_SYS_INT_ENABLE4, + SSI_SYS_INT_ENABLE5, + SSI_SYS_INT_ENABLE6, + SSI_SYS_INT_ENABLE7, + HDMI0_SEL, + HDMI1_SEL, + SSI9_BUSIF0_MODE, + SSI9_BUSIF1_MODE, + SSI9_BUSIF2_MODE, + SSI9_BUSIF3_MODE, + SSI9_BUSIF4_MODE, + SSI9_BUSIF5_MODE, + SSI9_BUSIF6_MODE, + SSI9_BUSIF7_MODE, + SSI9_BUSIF0_ADINR, + SSI9_BUSIF1_ADINR, + SSI9_BUSIF2_ADINR, + SSI9_BUSIF3_ADINR, + SSI9_BUSIF4_ADINR, + SSI9_BUSIF5_ADINR, + SSI9_BUSIF6_ADINR, + SSI9_BUSIF7_ADINR, + SSI9_BUSIF0_DALIGN, + SSI9_BUSIF1_DALIGN, + SSI9_BUSIF2_DALIGN, + SSI9_BUSIF3_DALIGN, + SSI9_BUSIF4_DALIGN, + SSI9_BUSIF5_DALIGN, + SSI9_BUSIF6_DALIGN, + SSI9_BUSIF7_DALIGN, + + /* SSI */ + SSICR, + SSISR, + SSITDR, + SSIRDR, + SSIWSR, + + REG_MAX, +}; +#define SRCIN_TIMSEL(i) (SRCIN_TIMSEL0 + (i)) +#define SRCOUT_TIMSEL(i) (SRCOUT_TIMSEL0 + (i)) +#define CTU_SVxxR(i, j) (CTU_SV00R + (i * 8) + (j)) +#define DVC_VOLxR(i) (DVC_VOL0R + (i)) +#define AUDIO_CLK_SEL(i) (AUDIO_CLK_SEL0 + (i)) +#define SSI_BUSIF_MODE(i) (SSI_BUSIF0_MODE + (i)) +#define SSI_BUSIF_ADINR(i) (SSI_BUSIF0_ADINR + (i)) +#define SSI_BUSIF_DALIGN(i) (SSI_BUSIF0_DALIGN + (i)) +#define SSI9_BUSIF_MODE(i) (SSI9_BUSIF0_MODE + (i)) +#define SSI9_BUSIF_ADINR(i) (SSI9_BUSIF0_ADINR + (i)) +#define SSI9_BUSIF_DALIGN(i) (SSI9_BUSIF0_DALIGN + (i)) +#define SSI_SYS_STATUS(i) (SSI_SYS_STATUS0 + (i)) +#define SSI_SYS_INT_ENABLE(i) (SSI_SYS_INT_ENABLE0 + (i)) + + +struct rsnd_priv; +struct rsnd_mod; +struct rsnd_dai; +struct rsnd_dai_stream; + +/* + * R-Car basic functions + */ +u32 rsnd_mod_read(struct rsnd_mod *mod, enum rsnd_reg reg); +void rsnd_mod_write(struct rsnd_mod *mod, enum rsnd_reg reg, u32 data); +void rsnd_mod_bset(struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); +u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); +u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod); + +/* + * R-Car DMA + */ +int rsnd_dma_attach(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, struct rsnd_mod **dma_mod); +int rsnd_dma_probe(struct rsnd_priv *priv); +struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name, + struct rsnd_mod *mod, char *x); + +/* + * R-Car sound mod + */ +enum rsnd_mod_type { + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + RSND_MOD_SSIM3, /* SSI multi 3 */ + RSND_MOD_SSIM2, /* SSI multi 2 */ + RSND_MOD_SSIM1, /* SSI multi 1 */ + RSND_MOD_SSIP, /* SSI parent */ + RSND_MOD_SSI, + RSND_MOD_SSIU, + RSND_MOD_MAX, +}; + +struct rsnd_mod_ops { + char *name; + struct dma_chan* (*dma_req)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod); + int (*probe)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); + int (*remove)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); + int (*init)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); + int (*quit)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); + int (*start)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); + int (*stop)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); + int (*irq)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, int enable); + int (*pcm_new)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd); + int (*hw_params)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params); + int (*pointer)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + snd_pcm_uframes_t *pointer); + int (*fallback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); + int (*prepare)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); + int (*cleanup)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv); + int (*hw_free)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream); + u32 *(*get_status)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type); + int (*id)(struct rsnd_mod *mod); + int (*id_sub)(struct rsnd_mod *mod); + int (*id_cmd)(struct rsnd_mod *mod); + +#ifdef CONFIG_DEBUG_FS + void (*debug_info)(struct seq_file *m, + struct rsnd_dai_stream *io, struct rsnd_mod *mod); +#endif +}; + +struct rsnd_dai_stream; +struct rsnd_mod { + int id; + enum rsnd_mod_type type; + struct rsnd_mod_ops *ops; + struct rsnd_priv *priv; + struct clk *clk; + u32 status; +}; +/* + * status + * + * 0xH000DCB0 + * + * B 0: init 1: quit + * C 0: start 1: stop + * D 0: hw_params 1: hw_free + * + * H is always called (see __rsnd_mod_call) + */ +#define __rsnd_mod_shift_init 4 +#define __rsnd_mod_shift_quit 4 +#define __rsnd_mod_shift_start 8 +#define __rsnd_mod_shift_stop 8 +#define __rsnd_mod_shift_hw_params 12 +#define __rsnd_mod_shift_hw_free 12 +#define __rsnd_mod_shift_probe 28 /* always called */ +#define __rsnd_mod_shift_remove 28 /* always called */ +#define __rsnd_mod_shift_irq 28 /* always called */ +#define __rsnd_mod_shift_pcm_new 28 /* always called */ +#define __rsnd_mod_shift_fallback 28 /* always called */ +#define __rsnd_mod_shift_pointer 28 /* always called */ +#define __rsnd_mod_shift_prepare 28 /* always called */ +#define __rsnd_mod_shift_cleanup 28 /* always called */ + +#define __rsnd_mod_add_probe 0 +#define __rsnd_mod_add_remove 0 +#define __rsnd_mod_add_prepare 0 +#define __rsnd_mod_add_cleanup 0 +#define __rsnd_mod_add_init 1 /* needs protect */ +#define __rsnd_mod_add_quit -1 /* needs protect */ +#define __rsnd_mod_add_start 1 /* needs protect */ +#define __rsnd_mod_add_stop -1 /* needs protect */ +#define __rsnd_mod_add_hw_params 1 /* needs protect */ +#define __rsnd_mod_add_hw_free -1 /* needs protect */ +#define __rsnd_mod_add_irq 0 +#define __rsnd_mod_add_pcm_new 0 +#define __rsnd_mod_add_fallback 0 +#define __rsnd_mod_add_pointer 0 + +#define __rsnd_mod_call_probe 0 +#define __rsnd_mod_call_remove 0 +#define __rsnd_mod_call_prepare 0 +#define __rsnd_mod_call_cleanup 0 +#define __rsnd_mod_call_init 0 /* needs protect */ +#define __rsnd_mod_call_quit 1 /* needs protect */ +#define __rsnd_mod_call_start 0 /* needs protect */ +#define __rsnd_mod_call_stop 1 /* needs protect */ +#define __rsnd_mod_call_hw_params 0 /* needs protect */ +#define __rsnd_mod_call_hw_free 1 /* needs protect */ +#define __rsnd_mod_call_irq 0 +#define __rsnd_mod_call_pcm_new 0 +#define __rsnd_mod_call_fallback 0 +#define __rsnd_mod_call_pointer 0 + +#define rsnd_mod_to_priv(mod) ((mod)->priv) +#define rsnd_mod_power_on(mod) clk_enable((mod)->clk) +#define rsnd_mod_power_off(mod) clk_disable((mod)->clk) +#define rsnd_mod_get(ip) (&(ip)->mod) + +int rsnd_mod_init(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_mod_ops *ops, + struct clk *clk, + enum rsnd_mod_type type, + int id); +void rsnd_mod_quit(struct rsnd_mod *mod); +struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod); +void rsnd_mod_interrupt(struct rsnd_mod *mod, + void (*callback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io)); +u32 *rsnd_mod_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type); +int rsnd_mod_id(struct rsnd_mod *mod); +int rsnd_mod_id_raw(struct rsnd_mod *mod); +int rsnd_mod_id_sub(struct rsnd_mod *mod); +char *rsnd_mod_name(struct rsnd_mod *mod); +struct rsnd_mod *rsnd_mod_next(int *iterator, + struct rsnd_dai_stream *io, + enum rsnd_mod_type *array, + int array_size); +#define for_each_rsnd_mod(iterator, pos, io) \ + for (iterator = 0; \ + (pos = rsnd_mod_next(&iterator, io, NULL, 0)); iterator++) +#define for_each_rsnd_mod_arrays(iterator, pos, io, array, size) \ + for (iterator = 0; \ + (pos = rsnd_mod_next(&iterator, io, array, size)); iterator++) +#define for_each_rsnd_mod_array(iterator, pos, io, array) \ + for_each_rsnd_mod_arrays(iterator, pos, io, array, ARRAY_SIZE(array)) + +void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name, + struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), + struct device_node *node, + struct device_node *playback, + struct device_node *capture); +int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name); +int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx); + +int rsnd_channel_normalization(int chan); +#define rsnd_runtime_channel_original(io) \ + rsnd_runtime_channel_original_with_params(io, NULL) +int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params); +#define rsnd_runtime_channel_after_ctu(io) \ + rsnd_runtime_channel_after_ctu_with_params(io, NULL) +int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params); +#define rsnd_runtime_channel_for_ssi(io) \ + rsnd_runtime_channel_for_ssi_with_params(io, NULL) +int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, + struct snd_pcm_hw_params *params); +int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io); +int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io); + +/* + * DT + */ +#define rsnd_parse_of_node(priv, node) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, node) +#define RSND_NODE_DAI "rcar_sound,dai" +#define RSND_NODE_SSI "rcar_sound,ssi" +#define RSND_NODE_SSIU "rcar_sound,ssiu" +#define RSND_NODE_SRC "rcar_sound,src" +#define RSND_NODE_CTU "rcar_sound,ctu" +#define RSND_NODE_MIX "rcar_sound,mix" +#define RSND_NODE_DVC "rcar_sound,dvc" + +/* + * R-Car sound DAI + */ +#define RSND_DAI_NAME_SIZE 16 +struct rsnd_dai_stream { + char name[RSND_DAI_NAME_SIZE]; + struct snd_pcm_substream *substream; + struct rsnd_mod *mod[RSND_MOD_MAX]; + struct rsnd_mod *dma; + struct rsnd_dai *rdai; + struct device *dmac_dev; /* for IPMMU */ + u32 converted_rate; /* converted sampling rate */ + int converted_chan; /* converted channels */ + u32 parent_ssi_status; + u32 flags; +}; + +/* flags */ +#define RSND_STREAM_HDMI0 (1 << 0) /* for HDMI0 */ +#define RSND_STREAM_HDMI1 (1 << 1) /* for HDMI1 */ +#define RSND_STREAM_TDM_SPLIT (1 << 2) /* for TDM split mode */ +#define RSND_HW_RULE_ERR (1 << 3) /* hw_rule error */ + +#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) +#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) +#define rsnd_io_to_mod_ssiu(io) rsnd_io_to_mod((io), RSND_MOD_SSIU) +#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP) +#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC) +#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU) +#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX) +#define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC) +#define rsnd_io_to_mod_cmd(io) rsnd_io_to_mod((io), RSND_MOD_CMD) +#define rsnd_io_to_rdai(io) ((io)->rdai) +#define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io))) +#define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) +#define rsnd_io_to_runtime(io) ((io)->substream ? \ + (io)->substream->runtime : NULL) +#define rsnd_io_converted_rate(io) ((io)->converted_rate) +#define rsnd_io_converted_chan(io) ((io)->converted_chan) +int rsnd_io_is_working(struct rsnd_dai_stream *io); + +struct rsnd_dai { + char name[RSND_DAI_NAME_SIZE]; + struct rsnd_dai_stream playback; + struct rsnd_dai_stream capture; + struct rsnd_priv *priv; + struct snd_pcm_hw_constraint_list constraint; + struct of_phandle_args dai_args; + + int max_channels; /* 2ch - 16ch */ + int ssi_lane; /* 1lane - 4lane */ + int chan_width; /* 16/24/32 bit width */ + + unsigned int clk_master:1; + unsigned int bit_clk_inv:1; + unsigned int frm_clk_inv:1; + unsigned int sys_delay:1; + unsigned int data_alignment:1; +}; + +#define rsnd_rdai_nr(priv) ((priv)->rdai_nr) +#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) +#define rsnd_rdai_to_priv(rdai) ((rdai)->priv) +#define for_each_rsnd_dai(rdai, priv, i) \ + for (i = 0; \ + (i < rsnd_rdai_nr(priv)) && \ + ((rdai) = rsnd_rdai_get(priv, i)); \ + i++) + +struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id); + +#define rsnd_rdai_channels_set(rdai, max_channels) \ + rsnd_rdai_channels_ctrl(rdai, max_channels) +#define rsnd_rdai_channels_get(rdai) \ + rsnd_rdai_channels_ctrl(rdai, 0) +int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai, + int max_channels); + +#define rsnd_rdai_ssi_lane_set(rdai, ssi_lane) \ + rsnd_rdai_ssi_lane_ctrl(rdai, ssi_lane) +#define rsnd_rdai_ssi_lane_get(rdai) \ + rsnd_rdai_ssi_lane_ctrl(rdai, 0) +int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai, + int ssi_lane); + +#define rsnd_rdai_width_set(rdai, width) \ + rsnd_rdai_width_ctrl(rdai, width) +#define rsnd_rdai_width_get(rdai) \ + rsnd_rdai_width_ctrl(rdai, 0) +int rsnd_rdai_width_ctrl(struct rsnd_dai *rdai, int width); +int rsnd_dai_connect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type); + +/* + * R-Car Gen1/Gen2 + */ +int rsnd_gen_probe(struct rsnd_priv *priv); +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg); +phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); +#ifdef CONFIG_DEBUG_FS +void __iomem *rsnd_gen_get_base_addr(struct rsnd_priv *priv, int reg_id); +#endif + +/* + * R-Car ADG + */ +int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate); +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod); +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate); +int rsnd_adg_probe(struct rsnd_priv *priv); +void rsnd_adg_remove(struct rsnd_priv *priv); +int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate); +int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, + struct rsnd_dai_stream *io); +#define rsnd_adg_clk_enable(priv) rsnd_adg_clk_control(priv, 1) +#define rsnd_adg_clk_disable(priv) rsnd_adg_clk_control(priv, 0) +void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable); +void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m); + +/* + * R-Car sound priv + */ +struct rsnd_priv { + + struct platform_device *pdev; + spinlock_t lock; + unsigned long flags; +#define RSND_GEN_MASK (0xF << 0) +#define RSND_GEN1 (1 << 0) +#define RSND_GEN2 (2 << 0) +#define RSND_GEN3 (3 << 0) +#define RSND_GEN4 (4 << 0) +#define RSND_SOC_MASK (0xFF << 4) +#define RSND_SOC_E (1 << 4) /* E1/E2/E3 */ + + /* + * below value will be filled on rsnd_gen_probe() + */ + void *gen; + + /* + * below value will be filled on rsnd_adg_probe() + */ + void *adg; + + /* + * below value will be filled on rsnd_dma_probe() + */ + void *dma; + + /* + * below value will be filled on rsnd_ssi_probe() + */ + void *ssi; + int ssi_nr; + + /* + * below value will be filled on rsnd_ssiu_probe() + */ + void *ssiu; + int ssiu_nr; + + /* + * below value will be filled on rsnd_src_probe() + */ + void *src; + int src_nr; + + /* + * below value will be filled on rsnd_ctu_probe() + */ + void *ctu; + int ctu_nr; + + /* + * below value will be filled on rsnd_mix_probe() + */ + void *mix; + int mix_nr; + + /* + * below value will be filled on rsnd_dvc_probe() + */ + void *dvc; + int dvc_nr; + + /* + * below value will be filled on rsnd_cmd_probe() + */ + void *cmd; + int cmd_nr; + + /* + * below value will be filled on rsnd_dai_probe() + */ + struct snd_soc_dai_driver *daidrv; + struct rsnd_dai *rdai; + int rdai_nr; + +#define RSND_MAX_COMPONENT 3 + int component_dais[RSND_MAX_COMPONENT]; +}; + +#define rsnd_priv_to_pdev(priv) ((priv)->pdev) +#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev)) + +#define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1) +#define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2) +#define rsnd_is_gen3(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN3) +#define rsnd_is_gen4(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN4) +#define rsnd_is_gen3_e3(priv) (((priv)->flags & \ + (RSND_GEN_MASK | RSND_SOC_MASK)) == \ + (RSND_GEN3 | RSND_SOC_E)) + +#define rsnd_flags_has(p, f) ((p)->flags & (f)) +#define rsnd_flags_set(p, f) ((p)->flags |= (f)) +#define rsnd_flags_del(p, f) ((p)->flags &= ~(f)) + +/* + * rsnd_kctrl + */ +struct rsnd_kctrl_cfg { + unsigned int max; + unsigned int size; + u32 *val; + const char * const *texts; + int (*accept)(struct rsnd_dai_stream *io); + void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod); + struct rsnd_dai_stream *io; + struct snd_card *card; + struct snd_kcontrol *kctrl; + struct rsnd_mod *mod; +}; + +#define RSND_MAX_CHANNELS 8 +struct rsnd_kctrl_cfg_m { + struct rsnd_kctrl_cfg cfg; + u32 val[RSND_MAX_CHANNELS]; +}; + +struct rsnd_kctrl_cfg_s { + struct rsnd_kctrl_cfg cfg; + u32 val; +}; +#define rsnd_kctrl_size(x) ((x).cfg.size) +#define rsnd_kctrl_max(x) ((x).cfg.max) +#define rsnd_kctrl_valm(x, i) ((x).val[i]) /* = (x).cfg.val[i] */ +#define rsnd_kctrl_vals(x) ((x).val) /* = (x).cfg.val[0] */ + +int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io); +int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io); +struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg); +struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg); +int rsnd_kctrl_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd, + const unsigned char *name, + int (*accept)(struct rsnd_dai_stream *io), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), + struct rsnd_kctrl_cfg *cfg, + const char * const *texts, + int size, + u32 max); + +#define rsnd_kctrl_new_m(mod, io, rtd, name, accept, update, cfg, size, max) \ + rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_m(cfg), \ + NULL, size, max) + +#define rsnd_kctrl_new_s(mod, io, rtd, name, accept, update, cfg, max) \ + rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ + NULL, 1, max) + +#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts, size) \ + rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ + texts, 1, size) + +extern const char * const volume_ramp_rate[]; +#define VOLUME_RAMP_MAX_DVC (0x17 + 1) +#define VOLUME_RAMP_MAX_MIX (0x0a + 1) + +/* + * R-Car SSI + */ +int rsnd_ssi_probe(struct rsnd_priv *priv); +void rsnd_ssi_remove(struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); +u32 rsnd_ssi_multi_secondaries_runtime(struct rsnd_dai_stream *io); +int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); + +#define rsnd_ssi_is_pin_sharing(io) \ + __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); + +#define rsnd_ssi_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSI) +void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture); +unsigned int rsnd_ssi_clk_query(struct rsnd_dai *rdai, + int param1, int param2, int *idx); + +/* + * R-Car SSIU + */ +int rsnd_ssiu_attach(struct rsnd_dai_stream *io, + struct rsnd_mod *mod); +int rsnd_ssiu_probe(struct rsnd_priv *priv); +void rsnd_ssiu_remove(struct rsnd_priv *priv); +void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture); +#define rsnd_ssiu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSIU) +bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod); + +/* + * R-Car SRC + */ +int rsnd_src_probe(struct rsnd_priv *priv); +void rsnd_src_remove(struct rsnd_priv *priv); +struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); + +#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1) +#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0) +unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + int is_in); + +#define rsnd_src_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SRC) +#define rsnd_parse_connect_src(rdai, playback, capture) \ + rsnd_parse_connect_common(rdai, "src", rsnd_src_mod_get, \ + rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \ + playback, capture) + +/* + * R-Car CTU + */ +int rsnd_ctu_probe(struct rsnd_priv *priv); +void rsnd_ctu_remove(struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU) +#define rsnd_parse_connect_ctu(rdai, playback, capture) \ + rsnd_parse_connect_common(rdai, "ctu", rsnd_ctu_mod_get, \ + rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \ + playback, capture) + +/* + * R-Car MIX + */ +int rsnd_mix_probe(struct rsnd_priv *priv); +void rsnd_mix_remove(struct rsnd_priv *priv); +struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_mix_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_MIX) +#define rsnd_parse_connect_mix(rdai, playback, capture) \ + rsnd_parse_connect_common(rdai, "mix", rsnd_mix_mod_get, \ + rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \ + playback, capture) + +/* + * R-Car DVC + */ +int rsnd_dvc_probe(struct rsnd_priv *priv); +void rsnd_dvc_remove(struct rsnd_priv *priv); +struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_dvc_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DVC) +#define rsnd_parse_connect_dvc(rdai, playback, capture) \ + rsnd_parse_connect_common(rdai, "dvc", rsnd_dvc_mod_get, \ + rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \ + playback, capture) + +/* + * R-Car CMD + */ +int rsnd_cmd_probe(struct rsnd_priv *priv); +void rsnd_cmd_remove(struct rsnd_priv *priv); +int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id); + +void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type); + +/* + * If you don't need interrupt status debug message, + * define RSND_DEBUG_NO_IRQ_STATUS as 1 on top of src.c/ssi.c + * + * #define RSND_DEBUG_NO_IRQ_STATUS 1 + */ +#define rsnd_print_irq_status(dev, param...) do { \ + if (!IS_BUILTIN(RSND_DEBUG_NO_IRQ_STATUS)) \ + dev_info(dev, param); \ +} while (0) + +#ifdef CONFIG_DEBUG_FS +int rsnd_debugfs_probe(struct snd_soc_component *component); +void rsnd_debugfs_reg_show(struct seq_file *m, phys_addr_t _addr, + void __iomem *base, int offset, int size); +void rsnd_debugfs_mod_reg_show(struct seq_file *m, struct rsnd_mod *mod, + int reg_id, int offset, int size); + +#else +#define rsnd_debugfs_probe NULL +#endif + +#endif /* RSND_H */ diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c new file mode 100644 index 000000000000..e7f86db0d94c --- /dev/null +++ b/sound/soc/renesas/rcar/src.c @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SRC support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto + +/* + * You can use Synchronous Sampling Rate Convert (if no DVC) + * + * amixer set "SRC Out Rate" on + * aplay xxx.wav & + * amixer set "SRC Out Rate" 96000 // convert rate to 96000Hz + * amixer set "SRC Out Rate" 22050 // convert rate to 22050Hz + */ + +/* + * you can enable below define if you don't need + * SSI interrupt status debug message when debugging + * see rsnd_print_irq_status() + * + * #define RSND_DEBUG_NO_IRQ_STATUS 1 + */ + +#include +#include "rsnd.h" + +#define SRC_NAME "src" + +/* SCU_SYSTEM_STATUS0/1 */ +#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) + +struct rsnd_src { + struct rsnd_mod mod; + struct rsnd_mod *dma; + struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ + struct rsnd_kctrl_cfg_s sync; /* sync convert */ + int irq; +}; + +#define RSND_SRC_NAME_SIZE 16 + +#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) +#define rsnd_src_nr(priv) ((priv)->src_nr) +#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val) + +#define rsnd_mod_to_src(_mod) \ + container_of((_mod), struct rsnd_src, mod) + +#define for_each_rsnd_src(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_src_nr(priv)) && \ + ((pos) = (struct rsnd_src *)(priv)->src + i); \ + i++) + + +/* + * image of SRC (Sampling Rate Converter) + * + * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ + * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | + * 44.1kHz <-> +-----+ +-----+ +-------+ + * ... + * + */ + +static void rsnd_src_activation(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, SRC_SWRSR, 0); + rsnd_mod_write(mod, SRC_SWRSR, 1); +} + +static void rsnd_src_halt(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, SRC_SRCIR, 1); + rsnd_mod_write(mod, SRC_SWRSR, 0); +} + +static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + int is_play = rsnd_io_is_play(io); + + return rsnd_dma_request_channel(rsnd_src_of_node(priv), + SRC_NAME, mod, + is_play ? "rx" : "tx"); +} + +static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 convert_rate; + + if (!runtime) + return 0; + + if (!rsnd_src_sync_is_enabled(mod)) + return rsnd_io_converted_rate(io); + + convert_rate = src->sync.val; + + if (!convert_rate) + convert_rate = rsnd_io_converted_rate(io); + + if (!convert_rate) + convert_rate = runtime->rate; + + return convert_rate; +} + +unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + int is_in) +{ + struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + unsigned int rate = 0; + int is_play = rsnd_io_is_play(io); + + /* + * Playback + * runtime_rate -> [SRC] -> convert_rate + * + * Capture + * convert_rate -> [SRC] -> runtime_rate + */ + + if (is_play == is_in) + return runtime->rate; + + /* + * return convert rate if SRC is used, + * otherwise, return runtime->rate as usual + */ + if (src_mod) + rate = rsnd_src_convert_rate(io, src_mod); + + if (!rate) + rate = runtime->rate; + + return rate; +} + +static const u32 bsdsr_table_pattern1[] = { + 0x01800000, /* 6 - 1/6 */ + 0x01000000, /* 6 - 1/4 */ + 0x00c00000, /* 6 - 1/3 */ + 0x00800000, /* 6 - 1/2 */ + 0x00600000, /* 6 - 2/3 */ + 0x00400000, /* 6 - 1 */ +}; + +static const u32 bsdsr_table_pattern2[] = { + 0x02400000, /* 6 - 1/6 */ + 0x01800000, /* 6 - 1/4 */ + 0x01200000, /* 6 - 1/3 */ + 0x00c00000, /* 6 - 1/2 */ + 0x00900000, /* 6 - 2/3 */ + 0x00600000, /* 6 - 1 */ +}; + +static const u32 bsisr_table[] = { + 0x00100060, /* 6 - 1/6 */ + 0x00100040, /* 6 - 1/4 */ + 0x00100030, /* 6 - 1/3 */ + 0x00100020, /* 6 - 1/2 */ + 0x00100020, /* 6 - 2/3 */ + 0x00100020, /* 6 - 1 */ +}; + +static const u32 chan288888[] = { + 0x00000006, /* 1 to 2 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ +}; + +static const u32 chan244888[] = { + 0x00000006, /* 1 to 2 */ + 0x0000001e, /* 1 to 4 */ + 0x0000001e, /* 1 to 4 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ + 0x000001fe, /* 1 to 8 */ +}; + +static const u32 chan222222[] = { + 0x00000006, /* 1 to 2 */ + 0x00000006, /* 1 to 2 */ + 0x00000006, /* 1 to 2 */ + 0x00000006, /* 1 to 2 */ + 0x00000006, /* 1 to 2 */ + 0x00000006, /* 1 to 2 */ +}; + +static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + int is_play = rsnd_io_is_play(io); + int use_src = 0; + u32 fin, fout; + u32 ifscr, fsrate, adinr; + u32 cr, route; + u32 i_busif, o_busif, tmp; + const u32 *bsdsr_table; + const u32 *chptn; + uint ratio; + int chan; + int idx; + + if (!runtime) + return; + + fin = rsnd_src_get_in_rate(priv, io); + fout = rsnd_src_get_out_rate(priv, io); + + chan = rsnd_runtime_channel_original(io); + + /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ + if (fin == fout) + ratio = 0; + else if (fin > fout) + ratio = 100 * fin / fout; + else + ratio = 100 * fout / fin; + + if (ratio > 600) { + dev_err(dev, "FSO/FSI ratio error\n"); + return; + } + + use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod); + + /* + * SRC_ADINR + */ + adinr = rsnd_get_adinr_bit(mod, io) | chan; + + /* + * SRC_IFSCR / SRC_IFSVR + */ + ifscr = 0; + fsrate = 0; + if (use_src) { + u64 n; + + ifscr = 1; + n = (u64)0x0400000 * fin; + do_div(n, fout); + fsrate = n; + } + + /* + * SRC_SRCCR / SRC_ROUTE_MODE0 + */ + cr = 0x00011110; + route = 0x0; + if (use_src) { + route = 0x1; + + if (rsnd_src_sync_is_enabled(mod)) { + cr |= 0x1; + route |= rsnd_io_is_play(io) ? + (0x1 << 24) : (0x1 << 25); + } + } + + /* + * SRC_BSDSR / SRC_BSISR + * + * see + * Combination of Register Setting Related to + * FSO/FSI Ratio and Channel, Latency + */ + switch (rsnd_mod_id(mod)) { + case 0: + chptn = chan288888; + bsdsr_table = bsdsr_table_pattern1; + break; + case 1: + case 3: + case 4: + chptn = chan244888; + bsdsr_table = bsdsr_table_pattern1; + break; + case 2: + case 9: + chptn = chan222222; + bsdsr_table = bsdsr_table_pattern1; + break; + case 5: + case 6: + case 7: + case 8: + chptn = chan222222; + bsdsr_table = bsdsr_table_pattern2; + break; + default: + goto convert_rate_err; + } + + /* + * E3 need to overwrite + */ + if (rsnd_is_gen3_e3(priv)) + switch (rsnd_mod_id(mod)) { + case 0: + case 4: + chptn = chan222222; + } + + for (idx = 0; idx < ARRAY_SIZE(chan222222); idx++) + if (chptn[idx] & (1 << chan)) + break; + + if (chan > 8 || + idx >= ARRAY_SIZE(chan222222)) + goto convert_rate_err; + + /* BUSIF_MODE */ + tmp = rsnd_get_busif_shift(io, mod); + i_busif = ( is_play ? tmp : 0) | 1; + o_busif = (!is_play ? tmp : 0) | 1; + + rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); + + rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ + rsnd_mod_write(mod, SRC_ADINR, adinr); + rsnd_mod_write(mod, SRC_IFSCR, ifscr); + rsnd_mod_write(mod, SRC_IFSVR, fsrate); + rsnd_mod_write(mod, SRC_SRCCR, cr); + rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]); + rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]); + rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ + + rsnd_mod_write(mod, SRC_I_BUSIF_MODE, i_busif); + rsnd_mod_write(mod, SRC_O_BUSIF_MODE, o_busif); + + rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); + + rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); + + return; + +convert_rate_err: + dev_err(dev, "unknown BSDSR/BSDIR settings\n"); +} + +static int rsnd_src_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 sys_int_val, int_val, sys_int_mask; + int irq = src->irq; + int id = rsnd_mod_id(mod); + + sys_int_val = + sys_int_mask = OUF_SRC(id); + int_val = 0x3300; + + /* + * IRQ is not supported on non-DT + * see + * rsnd_src_probe_() + */ + if ((irq <= 0) || !enable) { + sys_int_val = 0; + int_val = 0; + } + + /* + * WORKAROUND + * + * ignore over flow error when rsnd_src_sync_is_enabled() + */ + if (rsnd_src_sync_is_enabled(mod)) + sys_int_val = sys_int_val & 0xffff; + + rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); + rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); + rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); + + return 0; +} + +static void rsnd_src_status_clear(struct rsnd_mod *mod) +{ + u32 val = OUF_SRC(rsnd_mod_id(mod)); + + rsnd_mod_write(mod, SCU_SYS_STATUS0, val); + rsnd_mod_write(mod, SCU_SYS_STATUS1, val); +} + +static bool rsnd_src_error_occurred(struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 val0, val1; + u32 status0, status1; + bool ret = false; + + val0 = val1 = OUF_SRC(rsnd_mod_id(mod)); + + /* + * WORKAROUND + * + * ignore over flow error when rsnd_src_sync_is_enabled() + */ + if (rsnd_src_sync_is_enabled(mod)) + val0 = val0 & 0xffff; + + status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0); + status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1); + if ((status0 & val0) || (status1 & val1)) { + rsnd_print_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n", + rsnd_mod_name(mod), status0, status1); + + ret = true; + } + + return ret; +} + +static int rsnd_src_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + u32 val; + + /* + * WORKAROUND + * + * Enable SRC output if you want to use sync convert together with DVC + */ + val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ? + 0x01 : 0x11; + + rsnd_mod_write(mod, SRC_CTRL, val); + + return 0; +} + +static int rsnd_src_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_write(mod, SRC_CTRL, 0); + + return 0; +} + +static int rsnd_src_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + int ret; + + /* reset sync convert_rate */ + src->sync.val = 0; + + ret = rsnd_mod_power_on(mod); + if (ret < 0) + return ret; + + rsnd_src_activation(mod); + + rsnd_src_set_convert_rate(io, mod); + + rsnd_src_status_clear(mod); + + return 0; +} + +static int rsnd_src_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + rsnd_src_halt(mod); + + rsnd_mod_power_off(mod); + + /* reset sync convert_rate */ + src->sync.val = 0; + + return 0; +} + +static void __rsnd_src_interrupt(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + bool stop = false; + + spin_lock(&priv->lock); + + /* ignore all cases if not working */ + if (!rsnd_io_is_working(io)) + goto rsnd_src_interrupt_out; + + if (rsnd_src_error_occurred(mod)) + stop = true; + + rsnd_src_status_clear(mod); +rsnd_src_interrupt_out: + + spin_unlock(&priv->lock); + + if (stop) + snd_pcm_stop_xrun(io->substream); +} + +static irqreturn_t rsnd_src_interrupt(int irq, void *data) +{ + struct rsnd_mod *mod = data; + + rsnd_mod_interrupt(mod, __rsnd_src_interrupt); + + return IRQ_HANDLED; +} + +static int rsnd_src_probe_(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + struct device *dev = rsnd_priv_to_dev(priv); + int irq = src->irq; + int ret; + + if (irq > 0) { + /* + * IRQ is not supported on non-DT + * see + * rsnd_src_irq() + */ + ret = devm_request_irq(dev, irq, + rsnd_src_interrupt, + IRQF_SHARED, + dev_name(dev), mod); + if (ret) + return ret; + } + + ret = rsnd_dma_attach(io, mod, &src->dma); + + return ret; +} + +static int rsnd_src_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + int ret; + + /* + * enable SRC sync convert if possible + */ + + /* + * It can't use SRC Synchronous convert + * when Capture if it uses CMD + */ + if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io)) + return 0; + + /* + * enable sync convert + */ + ret = rsnd_kctrl_new_s(mod, io, rtd, + rsnd_io_is_play(io) ? + "SRC Out Rate Switch" : + "SRC In Rate Switch", + rsnd_kctrl_accept_anytime, + rsnd_src_set_convert_rate, + &src->sen, 1); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_s(mod, io, rtd, + rsnd_io_is_play(io) ? + "SRC Out Rate" : + "SRC In Rate", + rsnd_kctrl_accept_runtime, + rsnd_src_set_convert_rate, + &src->sync, 192000); + + return ret; +} + +#ifdef CONFIG_DEBUG_FS +static void rsnd_src_debug_info(struct seq_file *m, + struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, + rsnd_mod_id(mod) * 0x20, 0x20); + seq_puts(m, "\n"); + rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, + 0x1c0, 0x20); + seq_puts(m, "\n"); + rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, + 0x200 + rsnd_mod_id(mod) * 0x40, 0x40); +} +#define DEBUG_INFO .debug_info = rsnd_src_debug_info +#else +#define DEBUG_INFO +#endif + +static struct rsnd_mod_ops rsnd_src_ops = { + .name = SRC_NAME, + .dma_req = rsnd_src_dma_req, + .probe = rsnd_src_probe_, + .init = rsnd_src_init, + .quit = rsnd_src_quit, + .start = rsnd_src_start, + .stop = rsnd_src_stop, + .irq = rsnd_src_irq, + .pcm_new = rsnd_src_pcm_new, + .get_status = rsnd_mod_get_status, + DEBUG_INFO +}; + +struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) + id = 0; + + return rsnd_mod_get(rsnd_src_get(priv, id)); +} + +int rsnd_src_probe(struct rsnd_priv *priv) +{ + struct device_node *node; + struct device_node *np; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_src *src; + struct clk *clk; + char name[RSND_SRC_NAME_SIZE]; + int i, nr, ret; + + node = rsnd_src_of_node(priv); + if (!node) + return 0; /* not used is not error */ + + nr = rsnd_node_count(priv, node, SRC_NAME); + if (!nr) { + ret = -EINVAL; + goto rsnd_src_probe_done; + } + + src = devm_kcalloc(dev, nr, sizeof(*src), GFP_KERNEL); + if (!src) { + ret = -ENOMEM; + goto rsnd_src_probe_done; + } + + priv->src_nr = nr; + priv->src = src; + + i = 0; + for_each_child_of_node(node, np) { + if (!of_device_is_available(np)) + goto skip; + + i = rsnd_node_fixed_index(dev, np, SRC_NAME, i); + if (i < 0) { + ret = -EINVAL; + of_node_put(np); + goto rsnd_src_probe_done; + } + + src = rsnd_src_get(priv, i); + + snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d", + SRC_NAME, i); + + src->irq = irq_of_parse_and_map(np, 0); + if (!src->irq) { + ret = -EINVAL; + of_node_put(np); + goto rsnd_src_probe_done; + } + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + of_node_put(np); + goto rsnd_src_probe_done; + } + + ret = rsnd_mod_init(priv, rsnd_mod_get(src), + &rsnd_src_ops, clk, RSND_MOD_SRC, i); + if (ret) { + of_node_put(np); + goto rsnd_src_probe_done; + } + +skip: + i++; + } + + ret = 0; + +rsnd_src_probe_done: + of_node_put(node); + + return ret; +} + +void rsnd_src_remove(struct rsnd_priv *priv) +{ + struct rsnd_src *src; + int i; + + for_each_rsnd_src(src, priv, i) { + rsnd_mod_quit(rsnd_mod_get(src)); + } +} diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c new file mode 100644 index 000000000000..b3d4e8ae07ef --- /dev/null +++ b/sound/soc/renesas/rcar/ssi.c @@ -0,0 +1,1260 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SSIU/SSI support +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto +// +// Based on fsi.c +// Kuninori Morimoto + +/* + * you can enable below define if you don't need + * SSI interrupt status debug message when debugging + * see rsnd_print_irq_status() + * + * #define RSND_DEBUG_NO_IRQ_STATUS 1 + */ + +#include +#include +#include +#include +#include "rsnd.h" +#define RSND_SSI_NAME_SIZE 16 + +/* + * SSICR + */ +#define FORCE (1u << 31) /* Fixed */ +#define DMEN (1u << 28) /* DMA Enable */ +#define UIEN (1u << 27) /* Underflow Interrupt Enable */ +#define OIEN (1u << 26) /* Overflow Interrupt Enable */ +#define IIEN (1u << 25) /* Idle Mode Interrupt Enable */ +#define DIEN (1u << 24) /* Data Interrupt Enable */ +#define CHNL_4 (1u << 22) /* Channels */ +#define CHNL_6 (2u << 22) /* Channels */ +#define CHNL_8 (3u << 22) /* Channels */ +#define DWL_MASK (7u << 19) /* Data Word Length mask */ +#define DWL_8 (0u << 19) /* Data Word Length */ +#define DWL_16 (1u << 19) /* Data Word Length */ +#define DWL_18 (2u << 19) /* Data Word Length */ +#define DWL_20 (3u << 19) /* Data Word Length */ +#define DWL_22 (4u << 19) /* Data Word Length */ +#define DWL_24 (5u << 19) /* Data Word Length */ +#define DWL_32 (6u << 19) /* Data Word Length */ + +/* + * System word length + */ +#define SWL_16 (1 << 16) /* R/W System Word Length */ +#define SWL_24 (2 << 16) /* R/W System Word Length */ +#define SWL_32 (3 << 16) /* R/W System Word Length */ + +#define SCKD (1 << 15) /* Serial Bit Clock Direction */ +#define SWSD (1 << 14) /* Serial WS Direction */ +#define SCKP (1 << 13) /* Serial Bit Clock Polarity */ +#define SWSP (1 << 12) /* Serial WS Polarity */ +#define SDTA (1 << 10) /* Serial Data Alignment */ +#define PDTA (1 << 9) /* Parallel Data Alignment */ +#define DEL (1 << 8) /* Serial Data Delay */ +#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ +#define TRMD (1 << 1) /* Transmit/Receive Mode Select */ +#define EN (1 << 0) /* SSI Module Enable */ + +/* + * SSISR + */ +#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */ +#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */ +#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ +#define DIRQ (1 << 24) /* Data Interrupt Status Flag */ + +/* + * SSIWSR + */ +#define CONT (1 << 8) /* WS Continue Function */ +#define WS_MODE (1 << 0) /* WS Mode */ + +#define SSI_NAME "ssi" + +struct rsnd_ssi { + struct rsnd_mod mod; + + u32 flags; + u32 cr_own; + u32 cr_clk; + u32 cr_mode; + u32 cr_en; + u32 wsr; + int chan; + int rate; + int irq; + unsigned int usrcnt; + + /* for PIO */ + int byte_pos; + int byte_per_period; + int next_period_byte; +}; + +/* flags */ +#define RSND_SSI_CLK_PIN_SHARE (1 << 0) +#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ +#define RSND_SSI_PROBED (1 << 2) + +#define for_each_rsnd_ssi(pos, priv, i) \ + for (i = 0; \ + (i < rsnd_ssi_nr(priv)) && \ + ((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \ + i++) + +#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id) +#define rsnd_ssi_nr(priv) ((priv)->ssi_nr) +#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) +#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) +#define rsnd_ssi_is_multi_secondary(mod, io) \ + (rsnd_ssi_multi_secondaries(io) & (1 << rsnd_mod_id(mod))) +#define rsnd_ssi_is_run_mods(mod, io) \ + (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) +#define rsnd_ssi_can_output_clk(mod) (!__rsnd_ssi_is_pin_sharing(mod)) + +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + int use_busif = 0; + + if (!rsnd_ssi_is_dma_mode(mod)) + return 0; + + if (!(rsnd_flags_has(ssi, RSND_SSI_NO_BUSIF))) + use_busif = 1; + if (rsnd_io_to_mod_src(io)) + use_busif = 1; + + return use_busif; +} + +static void rsnd_ssi_status_clear(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, SSISR, 0); +} + +static u32 rsnd_ssi_status_get(struct rsnd_mod *mod) +{ + return rsnd_mod_read(mod, SSISR); +} + +static void rsnd_ssi_status_check(struct rsnd_mod *mod, + u32 bit) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 status; + int i; + + for (i = 0; i < 1024; i++) { + status = rsnd_ssi_status_get(mod); + if (status & bit) + return; + + udelay(5); + } + + dev_warn(dev, "%s status check failed\n", rsnd_mod_name(mod)); +} + +static u32 rsnd_ssi_multi_secondaries(struct rsnd_dai_stream *io) +{ + static const enum rsnd_mod_type types[] = { + RSND_MOD_SSIM1, + RSND_MOD_SSIM2, + RSND_MOD_SSIM3, + }; + int i, mask; + + mask = 0; + for (i = 0; i < ARRAY_SIZE(types); i++) { + struct rsnd_mod *mod = rsnd_io_to_mod(io, types[i]); + + if (!mod) + continue; + + mask |= 1 << rsnd_mod_id(mod); + } + + return mask; +} + +static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); + u32 mods; + + mods = rsnd_ssi_multi_secondaries_runtime(io) | + 1 << rsnd_mod_id(ssi_mod); + + if (ssi_parent_mod) + mods |= 1 << rsnd_mod_id(ssi_parent_mod); + + return mods; +} + +u32 rsnd_ssi_multi_secondaries_runtime(struct rsnd_dai_stream *io) +{ + if (rsnd_runtime_is_multi_ssi(io)) + return rsnd_ssi_multi_secondaries(io); + + return 0; +} + +static u32 rsnd_rdai_width_to_swl(struct rsnd_dai *rdai) +{ + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); + int width = rsnd_rdai_width_get(rdai); + + switch (width) { + case 32: return SWL_32; + case 24: return SWL_24; + case 16: return SWL_16; + } + + dev_err(dev, "unsupported slot width value: %d\n", width); + return 0; +} + +unsigned int rsnd_ssi_clk_query(struct rsnd_dai *rdai, + int param1, int param2, int *idx) +{ + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + static const int ssi_clk_mul_table[] = { + 1, 2, 4, 8, 16, 6, 12, + }; + int j, ret; + unsigned int main_rate; + int width = rsnd_rdai_width_get(rdai); + + for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { + + /* + * It will set SSIWSR.CONT here, but SSICR.CKDV = 000 + * with it is not allowed. (SSIWSR.WS_MODE with + * SSICR.CKDV = 000 is not allowed either). + * Skip it. See SSICR.CKDV + */ + if (j == 0) + continue; + + main_rate = width * param1 * param2 * ssi_clk_mul_table[j]; + + ret = rsnd_adg_clk_query(priv, main_rate); + if (ret < 0) + continue; + + if (idx) + *idx = j; + + return main_rate; + } + + return 0; +} + +static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + int chan = rsnd_runtime_channel_for_ssi(io); + int idx, ret; + unsigned int main_rate; + unsigned int rate = rsnd_io_is_play(io) ? + rsnd_src_get_out_rate(priv, io) : + rsnd_src_get_in_rate(priv, io); + + if (!rsnd_rdai_is_clk_master(rdai)) + return 0; + + if (!rsnd_ssi_can_output_clk(mod)) + return 0; + + if (rsnd_ssi_is_multi_secondary(mod, io)) + return 0; + + if (rsnd_runtime_is_tdm_split(io)) + chan = rsnd_io_converted_chan(io); + + chan = rsnd_channel_normalization(chan); + + if (ssi->usrcnt > 0) { + if (ssi->rate != rate) { + dev_err(dev, "SSI parent/child should use same rate\n"); + return -EINVAL; + } + + if (ssi->chan != chan) { + dev_err(dev, "SSI parent/child should use same chan\n"); + return -EINVAL; + } + + return 0; + } + + ret = -EIO; + main_rate = rsnd_ssi_clk_query(rdai, rate, chan, &idx); + if (!main_rate) + goto rate_err; + + ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); + if (ret < 0) + goto rate_err; + + /* + * SSI clock will be output contiguously + * by below settings. + * This means, rsnd_ssi_master_clk_start() + * and rsnd_ssi_register_setup() are necessary + * for SSI parent + * + * SSICR : FORCE, SCKD, SWSD + * SSIWSR : CONT + */ + ssi->cr_clk = FORCE | rsnd_rdai_width_to_swl(rdai) | + SCKD | SWSD | CKDV(idx); + ssi->wsr = CONT; + ssi->rate = rate; + ssi->chan = chan; + + dev_dbg(dev, "%s outputs %d chan %u Hz\n", + rsnd_mod_name(mod), chan, rate); + + return 0; + +rate_err: + dev_err(dev, "unsupported clock rate\n"); + return ret; +} + +static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + if (!rsnd_rdai_is_clk_master(rdai)) + return; + + if (!rsnd_ssi_can_output_clk(mod)) + return; + + if (ssi->usrcnt > 1) + return; + + ssi->cr_clk = 0; + ssi->rate = 0; + ssi->chan = 0; + + rsnd_adg_ssi_clk_stop(mod); +} + +static void rsnd_ssi_config_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 cr_own = ssi->cr_own; + u32 cr_mode = ssi->cr_mode; + u32 wsr = ssi->wsr; + int width; + int is_tdm, is_tdm_split; + + is_tdm = rsnd_runtime_is_tdm(io); + is_tdm_split = rsnd_runtime_is_tdm_split(io); + + if (is_tdm) + dev_dbg(dev, "TDM mode\n"); + if (is_tdm_split) + dev_dbg(dev, "TDM Split mode\n"); + + cr_own |= FORCE | rsnd_rdai_width_to_swl(rdai); + + if (rdai->bit_clk_inv) + cr_own |= SCKP; + if (rdai->frm_clk_inv && !is_tdm) + cr_own |= SWSP; + if (rdai->data_alignment) + cr_own |= SDTA; + if (rdai->sys_delay) + cr_own |= DEL; + + /* + * TDM Mode + * see + * rsnd_ssiu_init_gen2() + */ + if (is_tdm || is_tdm_split) { + wsr |= WS_MODE; + cr_own |= CHNL_8; + } + + /* + * We shouldn't exchange SWSP after running. + * This means, parent needs to care it. + */ + if (rsnd_ssi_is_parent(mod, io)) + goto init_end; + + if (rsnd_io_is_play(io)) + cr_own |= TRMD; + + cr_own &= ~DWL_MASK; + width = snd_pcm_format_width(runtime->format); + if (is_tdm_split) { + /* + * The SWL and DWL bits in SSICR should be fixed at 32-bit + * setting when TDM split mode. + * see datasheet + * Operation :: TDM Format Split Function (TDM Split Mode) + */ + width = 32; + } + + switch (width) { + case 8: + cr_own |= DWL_8; + break; + case 16: + cr_own |= DWL_16; + break; + case 24: + cr_own |= DWL_24; + break; + case 32: + cr_own |= DWL_32; + break; + } + + if (rsnd_ssi_is_dma_mode(mod)) { + cr_mode = UIEN | OIEN | /* over/under run */ + DMEN; /* DMA : enable DMA */ + } else { + cr_mode = DIEN; /* PIO : enable Data interrupt */ + } + +init_end: + ssi->cr_own = cr_own; + ssi->cr_mode = cr_mode; + ssi->wsr = wsr; +} + +static void rsnd_ssi_register_setup(struct rsnd_mod *mod) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + rsnd_mod_write(mod, SSIWSR, ssi->wsr); + rsnd_mod_write(mod, SSICR, ssi->cr_own | + ssi->cr_clk | + ssi->cr_mode | + ssi->cr_en); +} + +/* + * SSI mod common functions + */ +static int rsnd_ssi_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + int ret; + + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + + ret = rsnd_ssi_master_clk_start(mod, io); + if (ret < 0) + return ret; + + ssi->usrcnt++; + + ret = rsnd_mod_power_on(mod); + if (ret < 0) + return ret; + + rsnd_ssi_config_init(mod, io); + + rsnd_ssi_register_setup(mod); + + /* clear error status */ + rsnd_ssi_status_clear(mod); + + return 0; +} + +static int rsnd_ssi_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + + if (!ssi->usrcnt) { + dev_err(dev, "%s usrcnt error\n", rsnd_mod_name(mod)); + return -EIO; + } + + rsnd_ssi_master_clk_stop(mod, io); + + rsnd_mod_power_off(mod); + + ssi->usrcnt--; + + if (!ssi->usrcnt) { + ssi->cr_own = 0; + ssi->cr_mode = 0; + ssi->wsr = 0; + } + + return 0; +} + +static int rsnd_ssi_hw_params(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + unsigned int fmt_width = snd_pcm_format_width(params_format(params)); + + if (fmt_width > rdai->chan_width) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_err(dev, "invalid combination of slot-width and format-data-width\n"); + return -EINVAL; + } + + return 0; +} + +static int rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + + /* + * EN will be set via SSIU :: SSI_CONTROL + * if Multi channel mode + */ + if (rsnd_ssi_multi_secondaries_runtime(io)) + return 0; + + /* + * EN is for data output. + * SSI parent EN is not needed. + */ + if (rsnd_ssi_is_parent(mod, io)) + return 0; + + ssi->cr_en = EN; + + rsnd_mod_write(mod, SSICR, ssi->cr_own | + ssi->cr_clk | + ssi->cr_mode | + ssi->cr_en); + + return 0; +} + +static int rsnd_ssi_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 cr; + + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + + if (rsnd_ssi_is_parent(mod, io)) + return 0; + + cr = ssi->cr_own | + ssi->cr_clk; + + /* + * disable all IRQ, + * Playback: Wait all data was sent + * Capture: It might not receave data. Do nothing + */ + if (rsnd_io_is_play(io)) { + rsnd_mod_write(mod, SSICR, cr | ssi->cr_en); + rsnd_ssi_status_check(mod, DIRQ); + } + + /* In multi-SSI mode, stop is performed by setting ssi0129 in + * SSI_CONTROL to 0 (in rsnd_ssio_stop_gen2). Do nothing here. + */ + if (rsnd_ssi_multi_secondaries_runtime(io)) + return 0; + + /* + * disable SSI, + * and, wait idle state + */ + rsnd_mod_write(mod, SSICR, cr); /* disabled all */ + rsnd_ssi_status_check(mod, IIRQ); + + ssi->cr_en = 0; + + return 0; +} + +static int rsnd_ssi_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) +{ + u32 val = 0; + int is_tdm, is_tdm_split; + int id = rsnd_mod_id(mod); + + is_tdm = rsnd_runtime_is_tdm(io); + is_tdm_split = rsnd_runtime_is_tdm_split(io); + + if (rsnd_is_gen1(priv)) + return 0; + + if (rsnd_ssi_is_parent(mod, io)) + return 0; + + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + + if (enable) + val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; + + if (is_tdm || is_tdm_split) { + switch (id) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 9: + val |= 0x0000ff00; + break; + } + } + + rsnd_mod_write(mod, SSI_INT_ENABLE, val); + + return 0; +} + +static bool rsnd_ssi_pio_interrupt(struct rsnd_mod *mod, + struct rsnd_dai_stream *io); +static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + int is_dma = rsnd_ssi_is_dma_mode(mod); + u32 status; + bool elapsed = false; + bool stop = false; + + spin_lock(&priv->lock); + + /* ignore all cases if not working */ + if (!rsnd_io_is_working(io)) + goto rsnd_ssi_interrupt_out; + + status = rsnd_ssi_status_get(mod); + + /* PIO only */ + if (!is_dma && (status & DIRQ)) + elapsed = rsnd_ssi_pio_interrupt(mod, io); + + /* DMA only */ + if (is_dma && (status & (UIRQ | OIRQ))) { + rsnd_print_irq_status(dev, "%s err status : 0x%08x\n", + rsnd_mod_name(mod), status); + + stop = true; + } + + stop |= rsnd_ssiu_busif_err_status_clear(mod); + + rsnd_ssi_status_clear(mod); +rsnd_ssi_interrupt_out: + spin_unlock(&priv->lock); + + if (elapsed) + snd_pcm_period_elapsed(io->substream); + + if (stop) + snd_pcm_stop_xrun(io->substream); + +} + +static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) +{ + struct rsnd_mod *mod = data; + + rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt); + + return IRQ_HANDLED; +} + +static u32 *rsnd_ssi_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) +{ + /* + * SSIP (= SSI parent) needs to be special, otherwise, + * 2nd SSI might doesn't start. see also rsnd_mod_call() + * + * We can't include parent SSI status on SSI, because we don't know + * how many SSI requests parent SSI. Thus, it is localed on "io" now. + * ex) trouble case + * Playback: SSI0 + * Capture : SSI1 (needs SSI0) + * + * 1) start Capture -> SSI0/SSI1 are started. + * 2) start Playback -> SSI0 doesn't work, because it is already + * marked as "started" on 1) + * + * OTOH, using each mod's status is good for MUX case. + * It doesn't need to start in 2nd start + * ex) + * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 + * | + * IO-1: SRC1 -> CTU2 -+ + * + * 1) start IO-0 -> start SSI0 + * 2) start IO-1 -> SSI0 doesn't need to start, because it is + * already started on 1) + */ + if (type == RSND_MOD_SSIP) + return &io->parent_ssi_status; + + return rsnd_mod_get_status(mod, io, type); +} + +/* + * SSI PIO + */ +static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + + if (!__rsnd_ssi_is_pin_sharing(mod)) + return; + + if (!rsnd_rdai_is_clk_master(rdai)) + return; + + if (rsnd_ssi_is_multi_secondary(mod, io)) + return; + + switch (rsnd_mod_id(mod)) { + case 1: + case 2: + case 9: + rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP); + break; + case 4: + rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP); + break; + case 8: + rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP); + break; + } +} + +static int rsnd_ssi_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + /* + * rsnd_rdai_is_clk_master() will be enabled after set_fmt, + * and, pcm_new will be called after it. + * This function reuse pcm_new at this point. + */ + rsnd_ssi_parent_attach(mod, io); + + return 0; +} + +static int rsnd_ssi_common_probe(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + int ret = 0; + + /* + * SSIP/SSIU/IRQ are not needed on + * SSI Multi secondaries + */ + if (rsnd_ssi_is_multi_secondary(mod, io)) + return 0; + + /* + * It can't judge ssi parent at this point + * see rsnd_ssi_pcm_new() + */ + + /* + * SSI might be called again as PIO fallback + * It is easy to manual handling for IRQ request/free + * + * OTOH, this function might be called many times if platform is + * using MIX. It needs xxx_attach() many times on xxx_probe(). + * Because of it, we can't control .probe/.remove calling count by + * mod->status. + * But it don't need to call request_irq() many times. + * Let's control it by RSND_SSI_PROBED flag. + */ + if (!rsnd_flags_has(ssi, RSND_SSI_PROBED)) { + ret = request_irq(ssi->irq, + rsnd_ssi_interrupt, + IRQF_SHARED, + dev_name(dev), mod); + + rsnd_flags_set(ssi, RSND_SSI_PROBED); + } + + return ret; +} + +static int rsnd_ssi_common_remove(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_mod *pure_ssi_mod = rsnd_io_to_mod_ssi(io); + + /* Do nothing if non SSI (= SSI parent, multi SSI) mod */ + if (pure_ssi_mod != mod) + return 0; + + /* PIO will request IRQ again */ + if (rsnd_flags_has(ssi, RSND_SSI_PROBED)) { + free_irq(ssi->irq, mod); + + rsnd_flags_del(ssi, RSND_SSI_PROBED); + } + + return 0; +} + +/* + * SSI PIO functions + */ +static bool rsnd_ssi_pio_interrupt(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 *buf = (u32 *)(runtime->dma_area + ssi->byte_pos); + int shift = 0; + int byte_pos; + bool elapsed = false; + + if (snd_pcm_format_width(runtime->format) == 24) + shift = 8; + + /* + * 8/16/32 data can be assesse to TDR/RDR register + * directly as 32bit data + * see rsnd_ssi_init() + */ + if (rsnd_io_is_play(io)) + rsnd_mod_write(mod, SSITDR, (*buf) << shift); + else + *buf = (rsnd_mod_read(mod, SSIRDR) >> shift); + + byte_pos = ssi->byte_pos + sizeof(*buf); + + if (byte_pos >= ssi->next_period_byte) { + int period_pos = byte_pos / ssi->byte_per_period; + + if (period_pos >= runtime->periods) { + byte_pos = 0; + period_pos = 0; + } + + ssi->next_period_byte = (period_pos + 1) * ssi->byte_per_period; + + elapsed = true; + } + + WRITE_ONCE(ssi->byte_pos, byte_pos); + + return elapsed; +} + +static int rsnd_ssi_pio_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + if (!rsnd_ssi_is_parent(mod, io)) { + ssi->byte_pos = 0; + ssi->byte_per_period = runtime->period_size * + runtime->channels * + samples_to_bytes(runtime, 1); + ssi->next_period_byte = ssi->byte_per_period; + } + + return rsnd_ssi_init(mod, io, priv); +} + +static int rsnd_ssi_pio_pointer(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + snd_pcm_uframes_t *pointer) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + *pointer = bytes_to_frames(runtime, READ_ONCE(ssi->byte_pos)); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_pio_ops = { + .name = SSI_NAME, + .probe = rsnd_ssi_common_probe, + .remove = rsnd_ssi_common_remove, + .init = rsnd_ssi_pio_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_start, + .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, + .pointer = rsnd_ssi_pio_pointer, + .pcm_new = rsnd_ssi_pcm_new, + .hw_params = rsnd_ssi_hw_params, + .get_status = rsnd_ssi_get_status, +}; + +static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + int ret; + + /* + * SSIP/SSIU/IRQ/DMA are not needed on + * SSI Multi secondaries + */ + if (rsnd_ssi_is_multi_secondary(mod, io)) + return 0; + + ret = rsnd_ssi_common_probe(mod, io, priv); + if (ret) + return ret; + + /* SSI probe might be called many times in MUX multi path */ + ret = rsnd_dma_attach(io, mod, &io->dma); + + return ret; +} + +static int rsnd_ssi_fallback(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + + /* + * fallback to PIO + * + * SSI .probe might be called again. + * see + * rsnd_rdai_continuance_probe() + */ + mod->ops = &rsnd_ssi_pio_ops; + + dev_info(dev, "%s fallback to PIO mode\n", rsnd_mod_name(mod)); + + return 0; +} + +static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + int is_play = rsnd_io_is_play(io); + char *name; + + /* + * It should use "rcar_sound,ssiu" on DT. + * But, we need to keep compatibility for old version. + * + * If it has "rcar_sound.ssiu", it will be used. + * If not, "rcar_sound.ssi" will be used. + * see + * rsnd_ssiu_dma_req() + * rsnd_dma_of_path() + */ + + if (rsnd_ssi_use_busif(io)) + name = is_play ? "rxu" : "txu"; + else + name = is_play ? "rx" : "tx"; + + return rsnd_dma_request_channel(rsnd_ssi_of_node(priv), + SSI_NAME, mod, name); +} + +#ifdef CONFIG_DEBUG_FS +static void rsnd_ssi_debug_info(struct seq_file *m, + struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + seq_printf(m, "clock: %s\n", rsnd_rdai_is_clk_master(rdai) ? + "provider" : "consumer"); + seq_printf(m, "bit_clk_inv: %d\n", rdai->bit_clk_inv); + seq_printf(m, "frm_clk_inv: %d\n", rdai->frm_clk_inv); + seq_printf(m, "pin share: %d\n", __rsnd_ssi_is_pin_sharing(mod)); + seq_printf(m, "can out clk: %d\n", rsnd_ssi_can_output_clk(mod)); + seq_printf(m, "multi secondary: %d\n", rsnd_ssi_is_multi_secondary(mod, io)); + seq_printf(m, "tdm: %d, %d\n", rsnd_runtime_is_tdm(io), + rsnd_runtime_is_tdm_split(io)); + seq_printf(m, "chan: %d\n", ssi->chan); + seq_printf(m, "user: %d\n", ssi->usrcnt); + + rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SSI, + rsnd_mod_id(mod) * 0x40, 0x40); +} +#define DEBUG_INFO .debug_info = rsnd_ssi_debug_info +#else +#define DEBUG_INFO +#endif + +static struct rsnd_mod_ops rsnd_ssi_dma_ops = { + .name = SSI_NAME, + .dma_req = rsnd_ssi_dma_req, + .probe = rsnd_ssi_dma_probe, + .remove = rsnd_ssi_common_remove, + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_start, + .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, + .fallback = rsnd_ssi_fallback, + .hw_params = rsnd_ssi_hw_params, + .get_status = rsnd_ssi_get_status, + DEBUG_INFO +}; + +int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) +{ + return mod->ops == &rsnd_ssi_dma_ops; +} + +/* + * ssi mod function + */ +static void rsnd_ssi_connect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + static const enum rsnd_mod_type types[] = { + RSND_MOD_SSI, + RSND_MOD_SSIM1, + RSND_MOD_SSIM2, + RSND_MOD_SSIM3, + }; + enum rsnd_mod_type type; + int i; + + /* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */ + for (i = 0; i < ARRAY_SIZE(types); i++) { + type = types[i]; + if (!rsnd_io_to_mod(io, type)) { + rsnd_dai_connect(mod, io, type); + rsnd_rdai_channels_set(rdai, (i + 1) * 2); + rsnd_rdai_ssi_lane_set(rdai, (i + 1)); + return; + } + } +} + +void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture) +{ + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *node; + struct device_node *np; + int i; + + node = rsnd_ssi_of_node(priv); + if (!node) + return; + + i = 0; + for_each_child_of_node(node, np) { + struct rsnd_mod *mod; + + i = rsnd_node_fixed_index(dev, np, SSI_NAME, i); + if (i < 0) { + of_node_put(np); + break; + } + + mod = rsnd_ssi_mod_get(priv, i); + + if (np == playback) + rsnd_ssi_connect(mod, &rdai->playback); + if (np == capture) + rsnd_ssi_connect(mod, &rdai->capture); + i++; + } + + of_node_put(node); +} + +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) + id = 0; + + return rsnd_mod_get(rsnd_ssi_get(priv, id)); +} + +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) +{ + if (!mod) + return 0; + + return !!(rsnd_flags_has(rsnd_mod_to_ssi(mod), RSND_SSI_CLK_PIN_SHARE)); +} + +int rsnd_ssi_probe(struct rsnd_priv *priv) +{ + struct device_node *node; + struct device_node *np; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod_ops *ops; + struct clk *clk; + struct rsnd_ssi *ssi; + char name[RSND_SSI_NAME_SIZE]; + int i, nr, ret; + + node = rsnd_ssi_of_node(priv); + if (!node) + return -EINVAL; + + nr = rsnd_node_count(priv, node, SSI_NAME); + if (!nr) { + ret = -EINVAL; + goto rsnd_ssi_probe_done; + } + + ssi = devm_kcalloc(dev, nr, sizeof(*ssi), GFP_KERNEL); + if (!ssi) { + ret = -ENOMEM; + goto rsnd_ssi_probe_done; + } + + priv->ssi = ssi; + priv->ssi_nr = nr; + + i = 0; + for_each_child_of_node(node, np) { + if (!of_device_is_available(np)) + goto skip; + + i = rsnd_node_fixed_index(dev, np, SSI_NAME, i); + if (i < 0) { + ret = -EINVAL; + of_node_put(np); + goto rsnd_ssi_probe_done; + } + + ssi = rsnd_ssi_get(priv, i); + + snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d", + SSI_NAME, i); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + of_node_put(np); + goto rsnd_ssi_probe_done; + } + + if (of_property_read_bool(np, "shared-pin")) + rsnd_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE); + + if (of_property_read_bool(np, "no-busif")) + rsnd_flags_set(ssi, RSND_SSI_NO_BUSIF); + + ssi->irq = irq_of_parse_and_map(np, 0); + if (!ssi->irq) { + ret = -EINVAL; + of_node_put(np); + goto rsnd_ssi_probe_done; + } + + if (of_property_read_bool(np, "pio-transfer")) + ops = &rsnd_ssi_pio_ops; + else + ops = &rsnd_ssi_dma_ops; + + ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, + RSND_MOD_SSI, i); + if (ret) { + of_node_put(np); + goto rsnd_ssi_probe_done; + } +skip: + i++; + } + + ret = 0; + +rsnd_ssi_probe_done: + of_node_put(node); + + return ret; +} + +void rsnd_ssi_remove(struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) { + rsnd_mod_quit(rsnd_mod_get(ssi)); + } +} diff --git a/sound/soc/renesas/rcar/ssiu.c b/sound/soc/renesas/rcar/ssiu.c new file mode 100644 index 000000000000..665e8b2db579 --- /dev/null +++ b/sound/soc/renesas/rcar/ssiu.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas R-Car SSIU support +// +// Copyright (c) 2015 Kuninori Morimoto + +#include "rsnd.h" + +#define SSIU_NAME "ssiu" + +struct rsnd_ssiu { + struct rsnd_mod mod; + u32 busif_status[8]; /* for BUSIF0 - BUSIF7 */ + unsigned int usrcnt; + int id; + int id_sub; +}; + +/* SSI_MODE */ +#define TDM_EXT (1 << 0) +#define TDM_SPLIT (1 << 8) + +#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr) +#define rsnd_mod_to_ssiu(_mod) container_of((_mod), struct rsnd_ssiu, mod) +#define for_each_rsnd_ssiu(pos, priv, i) \ + for (i = 0; \ + (i < rsnd_ssiu_nr(priv)) && \ + ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \ + i++) + +/* + * SSI Gen2 Gen3 Gen4 + * 0 BUSIF0-3 BUSIF0-7 BUSIF0-7 + * 1 BUSIF0-3 BUSIF0-7 + * 2 BUSIF0-3 BUSIF0-7 + * 3 BUSIF0 BUSIF0-7 + * 4 BUSIF0 BUSIF0-7 + * 5 BUSIF0 BUSIF0 + * 6 BUSIF0 BUSIF0 + * 7 BUSIF0 BUSIF0 + * 8 BUSIF0 BUSIF0 + * 9 BUSIF0-3 BUSIF0-7 + * total 22 52 8 + */ +static const int gen2_id[] = { 0, 4, 8, 12, 13, 14, 15, 16, 17, 18 }; +static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 }; +static const int gen4_id[] = { 0 }; + +/* enable busif buffer over/under run interrupt. */ +#define rsnd_ssiu_busif_err_irq_enable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 1) +#define rsnd_ssiu_busif_err_irq_disable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 0) +static void rsnd_ssiu_busif_err_irq_ctrl(struct rsnd_mod *mod, int enable) +{ + int id = rsnd_mod_id(mod); + int shift, offset; + int i; + + switch (id) { + case 0: + case 1: + case 2: + case 3: + case 4: + shift = id; + offset = 0; + break; + case 9: + shift = 1; + offset = 1; + break; + default: + return; + } + + for (i = 0; i < 4; i++) { + enum rsnd_reg reg = SSI_SYS_INT_ENABLE((i * 2) + offset); + u32 val = 0xf << (shift * 4); + u32 sys_int_enable = rsnd_mod_read(mod, reg); + + if (enable) + sys_int_enable |= val; + else + sys_int_enable &= ~val; + rsnd_mod_write(mod, reg, sys_int_enable); + } +} + +bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod) +{ + bool error = false; + int id = rsnd_mod_id(mod); + int shift, offset; + int i; + + switch (id) { + case 0: + case 1: + case 2: + case 3: + case 4: + shift = id; + offset = 0; + break; + case 9: + shift = 1; + offset = 1; + break; + default: + goto out; + } + + for (i = 0; i < 4; i++) { + u32 reg = SSI_SYS_STATUS(i * 2) + offset; + u32 status = rsnd_mod_read(mod, reg); + u32 val = 0xf << (shift * 4); + + status &= val; + if (status) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + rsnd_print_irq_status(dev, "%s err status : 0x%08x\n", + rsnd_mod_name(mod), status); + error = true; + } + rsnd_mod_write(mod, reg, val); + } +out: + return error; +} + +static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + int busif = rsnd_mod_id_sub(mod); + + return &ssiu->busif_status[busif]; +} + +static int rsnd_ssiu_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + u32 ssis = rsnd_ssi_multi_secondaries_runtime(io); + int use_busif = rsnd_ssi_use_busif(io); + int id = rsnd_mod_id(mod); + int is_clk_master = rsnd_rdai_is_clk_master(rdai); + u32 val1, val2; + + /* clear status */ + rsnd_ssiu_busif_err_status_clear(mod); + + /* Gen4 doesn't have SSI_MODE */ + if (rsnd_is_gen4(priv)) + goto ssi_mode_setting_end; + + /* + * SSI_MODE0 + */ + rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id); + + /* + * SSI_MODE1 / SSI_MODE2 + * + * FIXME + * sharing/multi with SSI0 are mainly supported + */ + val1 = rsnd_mod_read(mod, SSI_MODE1); + val2 = rsnd_mod_read(mod, SSI_MODE2); + if (rsnd_ssi_is_pin_sharing(io)) { + + ssis |= (1 << id); + + } else if (ssis) { + /* + * Multi SSI + * + * set synchronized bit here + */ + + /* SSI4 is synchronized with SSI3 */ + if (ssis & (1 << 4)) + val1 |= (1 << 20); + /* SSI012 are synchronized */ + if (ssis == 0x0006) + val1 |= (1 << 4); + /* SSI0129 are synchronized */ + if (ssis == 0x0206) + val2 |= (1 << 4); + } + + /* SSI1 is sharing pin with SSI0 */ + if (ssis & (1 << 1)) + val1 |= is_clk_master ? 0x2 : 0x1; + + /* SSI2 is sharing pin with SSI0 */ + if (ssis & (1 << 2)) + val1 |= is_clk_master ? 0x2 << 2 : + 0x1 << 2; + /* SSI4 is sharing pin with SSI3 */ + if (ssis & (1 << 4)) + val1 |= is_clk_master ? 0x2 << 16 : + 0x1 << 16; + /* SSI9 is sharing pin with SSI0 */ + if (ssis & (1 << 9)) + val2 |= is_clk_master ? 0x2 : 0x1; + + rsnd_mod_bset(mod, SSI_MODE1, 0x0013001f, val1); + rsnd_mod_bset(mod, SSI_MODE2, 0x00000017, val2); + +ssi_mode_setting_end: + /* + * Enable busif buffer over/under run interrupt. + * It will be handled from ssi.c + * see + * __rsnd_ssi_interrupt() + */ + rsnd_ssiu_busif_err_irq_enable(mod); + + return 0; +} + +static int rsnd_ssiu_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + /* disable busif buffer over/under run interrupt. */ + rsnd_ssiu_busif_err_irq_disable(mod); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = { + .name = SSIU_NAME, + .init = rsnd_ssiu_init, + .quit = rsnd_ssiu_quit, + .get_status = rsnd_ssiu_get_status, +}; + +static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + u32 has_hdmi0 = rsnd_flags_has(io, RSND_STREAM_HDMI0); + u32 has_hdmi1 = rsnd_flags_has(io, RSND_STREAM_HDMI1); + int ret; + u32 mode = 0; + + ret = rsnd_ssiu_init(mod, io, priv); + if (ret < 0) + return ret; + + ssiu->usrcnt++; + + /* + * TDM Extend/Split Mode + * see + * rsnd_ssi_config_init() + */ + if (rsnd_runtime_is_tdm(io)) + mode = TDM_EXT; + else if (rsnd_runtime_is_tdm_split(io)) + mode = TDM_SPLIT; + + rsnd_mod_write(mod, SSI_MODE, mode); + + if (rsnd_ssi_use_busif(io)) { + int id = rsnd_mod_id(mod); + int busif = rsnd_mod_id_sub(mod); + enum rsnd_reg adinr_reg, mode_reg, dalign_reg; + + if ((id == 9) && (busif >= 4)) { + adinr_reg = SSI9_BUSIF_ADINR(busif); + mode_reg = SSI9_BUSIF_MODE(busif); + dalign_reg = SSI9_BUSIF_DALIGN(busif); + } else { + adinr_reg = SSI_BUSIF_ADINR(busif); + mode_reg = SSI_BUSIF_MODE(busif); + dalign_reg = SSI_BUSIF_DALIGN(busif); + } + + rsnd_mod_write(mod, adinr_reg, + rsnd_get_adinr_bit(mod, io) | + (rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io))); + rsnd_mod_write(mod, mode_reg, + rsnd_get_busif_shift(io, mod) | 1); + rsnd_mod_write(mod, dalign_reg, + rsnd_get_dalign(mod, io)); + } + + if (has_hdmi0 || has_hdmi1) { + enum rsnd_mod_type rsnd_ssi_array[] = { + RSND_MOD_SSIM1, + RSND_MOD_SSIM2, + RSND_MOD_SSIM3, + }; + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *pos; + u32 val; + int i; + + i = rsnd_mod_id(ssi_mod); + + /* output all same SSI as default */ + val = i << 16 | + i << 20 | + i << 24 | + i << 28 | + i; + + for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) { + int shift = (i * 4) + 20; + + val = (val & ~(0xF << shift)) | + rsnd_mod_id(pos) << shift; + } + + if (has_hdmi0) + rsnd_mod_write(mod, HDMI0_SEL, val); + if (has_hdmi1) + rsnd_mod_write(mod, HDMI1_SEL, val); + } + + return 0; +} + +static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + int busif = rsnd_mod_id_sub(mod); + + if (!rsnd_ssi_use_busif(io)) + return 0; + + rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 1 << (busif * 4)); + + if (rsnd_ssi_multi_secondaries_runtime(io)) + rsnd_mod_write(mod, SSI_CONTROL, 0x1); + + return 0; +} + +static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + int busif = rsnd_mod_id_sub(mod); + + if (!rsnd_ssi_use_busif(io)) + return 0; + + rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 0); + + if (--ssiu->usrcnt) + return 0; + + if (rsnd_ssi_multi_secondaries_runtime(io)) + rsnd_mod_write(mod, SSI_CONTROL, 0); + + return 0; +} + +static int rsnd_ssiu_id(struct rsnd_mod *mod) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + + /* see rsnd_ssiu_probe() */ + return ssiu->id; +} + +static int rsnd_ssiu_id_sub(struct rsnd_mod *mod) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + + /* see rsnd_ssiu_probe() */ + return ssiu->id_sub; +} + +static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + int is_play = rsnd_io_is_play(io); + char *name; + + /* + * It should use "rcar_sound,ssiu" on DT. + * But, we need to keep compatibility for old version. + * + * If it has "rcar_sound.ssiu", it will be used. + * If not, "rcar_sound.ssi" will be used. + * see + * rsnd_ssi_dma_req() + * rsnd_dma_of_path() + */ + + name = is_play ? "rx" : "tx"; + + return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv), + SSIU_NAME, mod, name); +} + +#ifdef CONFIG_DEBUG_FS +static void rsnd_ssiu_debug_info(struct seq_file *m, + struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SSIU, + rsnd_mod_id(mod) * 0x80, 0x80); +} +#define DEBUG_INFO .debug_info = rsnd_ssiu_debug_info +#else +#define DEBUG_INFO +#endif + +static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = { + .name = SSIU_NAME, + .dma_req = rsnd_ssiu_dma_req, + .init = rsnd_ssiu_init_gen2, + .quit = rsnd_ssiu_quit, + .start = rsnd_ssiu_start_gen2, + .stop = rsnd_ssiu_stop_gen2, + .get_status = rsnd_ssiu_get_status, + DEBUG_INFO +}; + +static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv))) + id = 0; + + return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id); +} + +static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_ssiu *ssiu; + int is_dma_mode; + int i; + + if (!ssi_mod) + return; + + is_dma_mode = rsnd_ssi_is_dma_mode(ssi_mod); + + /* select BUSIF0 */ + for_each_rsnd_ssiu(ssiu, priv, i) { + struct rsnd_mod *mod = rsnd_mod_get(ssiu); + + if (is_dma_mode && + (rsnd_mod_id(ssi_mod) == rsnd_mod_id(mod)) && + (rsnd_mod_id_sub(mod) == 0)) { + rsnd_dai_connect(mod, io, mod->type); + return; + } + } +} + +void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture) +{ + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *node = rsnd_ssiu_of_node(priv); + struct rsnd_dai_stream *io_p = &rdai->playback; + struct rsnd_dai_stream *io_c = &rdai->capture; + + /* use rcar_sound,ssiu if exist */ + if (node) { + struct device_node *np; + int i = 0; + + for_each_child_of_node(node, np) { + struct rsnd_mod *mod; + + i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i); + if (i < 0) { + of_node_put(np); + break; + } + + mod = rsnd_ssiu_mod_get(priv, i); + + if (np == playback) + rsnd_dai_connect(mod, io_p, mod->type); + if (np == capture) + rsnd_dai_connect(mod, io_c, mod->type); + i++; + } + + of_node_put(node); + } + + /* Keep DT compatibility */ + if (!rsnd_io_to_mod_ssiu(io_p)) + rsnd_parse_connect_ssiu_compatible(priv, io_p); + if (!rsnd_io_to_mod_ssiu(io_c)) + rsnd_parse_connect_ssiu_compatible(priv, io_c); +} + +int rsnd_ssiu_probe(struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *node; + struct rsnd_ssiu *ssiu; + struct rsnd_mod_ops *ops; + const int *list = NULL; + int i, nr; + + /* + * Keep DT compatibility. + * if it has "rcar_sound,ssiu", use it. + * if not, use "rcar_sound,ssi" + * see + * rsnd_ssiu_bufsif_to_id() + */ + node = rsnd_ssiu_of_node(priv); + if (node) + nr = rsnd_node_count(priv, node, SSIU_NAME); + else + nr = priv->ssi_nr; + + if (!nr) + return -EINVAL; + + ssiu = devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL); + if (!ssiu) + return -ENOMEM; + + priv->ssiu = ssiu; + priv->ssiu_nr = nr; + + if (rsnd_is_gen1(priv)) + ops = &rsnd_ssiu_ops_gen1; + else + ops = &rsnd_ssiu_ops_gen2; + + /* Keep compatibility */ + nr = 0; + if ((node) && + (ops == &rsnd_ssiu_ops_gen2)) { + ops->id = rsnd_ssiu_id; + ops->id_sub = rsnd_ssiu_id_sub; + + if (rsnd_is_gen2(priv)) { + list = gen2_id; + nr = ARRAY_SIZE(gen2_id); + } else if (rsnd_is_gen3(priv)) { + list = gen3_id; + nr = ARRAY_SIZE(gen3_id); + } else if (rsnd_is_gen4(priv)) { + list = gen4_id; + nr = ARRAY_SIZE(gen4_id); + } else { + dev_err(dev, "unknown SSIU\n"); + return -ENODEV; + } + } + + for_each_rsnd_ssiu(ssiu, priv, i) { + int ret; + + if (node) { + int j; + + /* + * see + * rsnd_ssiu_get_id() + * rsnd_ssiu_get_id_sub() + */ + for (j = 0; j < nr; j++) { + if (list[j] > i) + break; + ssiu->id = j; + ssiu->id_sub = i - list[ssiu->id]; + } + } else { + ssiu->id = i; + } + + ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu), + ops, NULL, RSND_MOD_SSIU, i); + if (ret) + return ret; + } + + return 0; +} + +void rsnd_ssiu_remove(struct rsnd_priv *priv) +{ + struct rsnd_ssiu *ssiu; + int i; + + for_each_rsnd_ssiu(ssiu, priv, i) { + rsnd_mod_quit(rsnd_mod_get(ssiu)); + } +} diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c new file mode 100644 index 000000000000..6efd017aaa7f --- /dev/null +++ b/sound/soc/renesas/rz-ssi.c @@ -0,0 +1,1212 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Renesas RZ/G2L ASoC Serial Sound Interface (SSIF-2) Driver +// +// Copyright (C) 2021 Renesas Electronics Corp. +// Copyright (C) 2019 Chris Brandt. +// + +#include +#include +#include +#include +#include +#include +#include + +/* REGISTER OFFSET */ +#define SSICR 0x000 +#define SSISR 0x004 +#define SSIFCR 0x010 +#define SSIFSR 0x014 +#define SSIFTDR 0x018 +#define SSIFRDR 0x01c +#define SSIOFR 0x020 +#define SSISCR 0x024 + +/* SSI REGISTER BITS */ +#define SSICR_DWL(x) (((x) & 0x7) << 19) +#define SSICR_SWL(x) (((x) & 0x7) << 16) + +#define SSICR_CKS BIT(30) +#define SSICR_TUIEN BIT(29) +#define SSICR_TOIEN BIT(28) +#define SSICR_RUIEN BIT(27) +#define SSICR_ROIEN BIT(26) +#define SSICR_MST BIT(14) +#define SSICR_BCKP BIT(13) +#define SSICR_LRCKP BIT(12) +#define SSICR_CKDV(x) (((x) & 0xf) << 4) +#define SSICR_TEN BIT(1) +#define SSICR_REN BIT(0) + +#define SSISR_TUIRQ BIT(29) +#define SSISR_TOIRQ BIT(28) +#define SSISR_RUIRQ BIT(27) +#define SSISR_ROIRQ BIT(26) +#define SSISR_IIRQ BIT(25) + +#define SSIFCR_AUCKE BIT(31) +#define SSIFCR_SSIRST BIT(16) +#define SSIFCR_TIE BIT(3) +#define SSIFCR_RIE BIT(2) +#define SSIFCR_TFRST BIT(1) +#define SSIFCR_RFRST BIT(0) +#define SSIFCR_FIFO_RST (SSIFCR_TFRST | SSIFCR_RFRST) + +#define SSIFSR_TDC_MASK 0x3f +#define SSIFSR_TDC_SHIFT 24 +#define SSIFSR_RDC_MASK 0x3f +#define SSIFSR_RDC_SHIFT 8 + +#define SSIFSR_TDE BIT(16) +#define SSIFSR_RDF BIT(0) + +#define SSIOFR_LRCONT BIT(8) + +#define SSISCR_TDES(x) (((x) & 0x1f) << 8) +#define SSISCR_RDFS(x) (((x) & 0x1f) << 0) + +/* Pre allocated buffers sizes */ +#define PREALLOC_BUFFER (SZ_32K) +#define PREALLOC_BUFFER_MAX (SZ_32K) + +#define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-44.1kHz */ +#define SSI_FMTS SNDRV_PCM_FMTBIT_S16_LE +#define SSI_CHAN_MIN 2 +#define SSI_CHAN_MAX 2 +#define SSI_FIFO_DEPTH 32 + +struct rz_ssi_priv; + +struct rz_ssi_stream { + struct rz_ssi_priv *priv; + struct snd_pcm_substream *substream; + int fifo_sample_size; /* sample capacity of SSI FIFO */ + int dma_buffer_pos; /* The address for the next DMA descriptor */ + int period_counter; /* for keeping track of periods transferred */ + int sample_width; + int buffer_pos; /* current frame position in the buffer */ + int running; /* 0=stopped, 1=running */ + + int uerr_num; + int oerr_num; + + struct dma_chan *dma_ch; + + int (*transfer)(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm); +}; + +struct rz_ssi_priv { + void __iomem *base; + struct platform_device *pdev; + struct reset_control *rstc; + struct device *dev; + struct clk *sfr_clk; + struct clk *clk; + + phys_addr_t phys; + int irq_int; + int irq_tx; + int irq_rx; + int irq_rt; + + spinlock_t lock; + + /* + * The SSI supports full-duplex transmission and reception. + * However, if an error occurs, channel reset (both transmission + * and reception reset) is required. + * So it is better to use as half-duplex (playing and recording + * should be done on separate channels). + */ + struct rz_ssi_stream playback; + struct rz_ssi_stream capture; + + /* clock */ + unsigned long audio_mck; + unsigned long audio_clk_1; + unsigned long audio_clk_2; + + bool lrckp_fsync_fall; /* LR clock polarity (SSICR.LRCKP) */ + bool bckp_rise; /* Bit clock polarity (SSICR.BCKP) */ + bool dma_rt; + + /* Full duplex communication support */ + struct { + unsigned int rate; + unsigned int channels; + unsigned int sample_width; + unsigned int sample_bits; + } hw_params_cache; +}; + +static void rz_ssi_dma_complete(void *data); + +static void rz_ssi_reg_writel(struct rz_ssi_priv *priv, uint reg, u32 data) +{ + writel(data, (priv->base + reg)); +} + +static u32 rz_ssi_reg_readl(struct rz_ssi_priv *priv, uint reg) +{ + return readl(priv->base + reg); +} + +static void rz_ssi_reg_mask_setl(struct rz_ssi_priv *priv, uint reg, + u32 bclr, u32 bset) +{ + u32 val; + + val = readl(priv->base + reg); + val = (val & ~bclr) | bset; + writel(val, (priv->base + reg)); +} + +static inline struct snd_soc_dai * +rz_ssi_get_dai(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + + return snd_soc_rtd_to_cpu(rtd, 0); +} + +static inline bool rz_ssi_stream_is_play(struct rz_ssi_priv *ssi, + struct snd_pcm_substream *substream) +{ + return substream->stream == SNDRV_PCM_STREAM_PLAYBACK; +} + +static inline struct rz_ssi_stream * +rz_ssi_stream_get(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream) +{ + struct rz_ssi_stream *stream = &ssi->playback; + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + stream = &ssi->capture; + + return stream; +} + +static inline bool rz_ssi_is_dma_enabled(struct rz_ssi_priv *ssi) +{ + return (ssi->playback.dma_ch && (ssi->dma_rt || ssi->capture.dma_ch)); +} + +static void rz_ssi_set_substream(struct rz_ssi_stream *strm, + struct snd_pcm_substream *substream) +{ + struct rz_ssi_priv *ssi = strm->priv; + unsigned long flags; + + spin_lock_irqsave(&ssi->lock, flags); + strm->substream = substream; + spin_unlock_irqrestore(&ssi->lock, flags); +} + +static bool rz_ssi_stream_is_valid(struct rz_ssi_priv *ssi, + struct rz_ssi_stream *strm) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&ssi->lock, flags); + ret = strm->substream && strm->substream->runtime; + spin_unlock_irqrestore(&ssi->lock, flags); + + return ret; +} + +static inline bool rz_ssi_is_stream_running(struct rz_ssi_stream *strm) +{ + return strm->substream && strm->running; +} + +static void rz_ssi_stream_init(struct rz_ssi_stream *strm, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + rz_ssi_set_substream(strm, substream); + strm->sample_width = samples_to_bytes(runtime, 1); + strm->dma_buffer_pos = 0; + strm->period_counter = 0; + strm->buffer_pos = 0; + + strm->oerr_num = 0; + strm->uerr_num = 0; + strm->running = 0; + + /* fifo init */ + strm->fifo_sample_size = SSI_FIFO_DEPTH; +} + +static void rz_ssi_stream_quit(struct rz_ssi_priv *ssi, + struct rz_ssi_stream *strm) +{ + struct snd_soc_dai *dai = rz_ssi_get_dai(strm->substream); + + rz_ssi_set_substream(strm, NULL); + + if (strm->oerr_num > 0) + dev_info(dai->dev, "overrun = %d\n", strm->oerr_num); + + if (strm->uerr_num > 0) + dev_info(dai->dev, "underrun = %d\n", strm->uerr_num); +} + +static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, + unsigned int channels) +{ + static s8 ckdv[16] = { 1, 2, 4, 8, 16, 32, 64, 128, + 6, 12, 24, 48, 96, -1, -1, -1 }; + unsigned int channel_bits = 32; /* System Word Length */ + unsigned long bclk_rate = rate * channels * channel_bits; + unsigned int div; + unsigned int i; + u32 ssicr = 0; + u32 clk_ckdv; + + /* Clear AUCKE so we can set MST */ + rz_ssi_reg_writel(ssi, SSIFCR, 0); + + /* Continue to output LRCK pin even when idle */ + rz_ssi_reg_writel(ssi, SSIOFR, SSIOFR_LRCONT); + if (ssi->audio_clk_1 && ssi->audio_clk_2) { + if (ssi->audio_clk_1 % bclk_rate) + ssi->audio_mck = ssi->audio_clk_2; + else + ssi->audio_mck = ssi->audio_clk_1; + } + + /* Clock setting */ + ssicr |= SSICR_MST; + if (ssi->audio_mck == ssi->audio_clk_1) + ssicr |= SSICR_CKS; + if (ssi->bckp_rise) + ssicr |= SSICR_BCKP; + if (ssi->lrckp_fsync_fall) + ssicr |= SSICR_LRCKP; + + /* Determine the clock divider */ + clk_ckdv = 0; + div = ssi->audio_mck / bclk_rate; + /* try to find an match */ + for (i = 0; i < ARRAY_SIZE(ckdv); i++) { + if (ckdv[i] == div) { + clk_ckdv = i; + break; + } + } + + if (i == ARRAY_SIZE(ckdv)) { + dev_err(ssi->dev, "Rate not divisible by audio clock source\n"); + return -EINVAL; + } + + /* + * DWL: Data Word Length = 16 bits + * SWL: System Word Length = 32 bits + */ + ssicr |= SSICR_CKDV(clk_ckdv); + ssicr |= SSICR_DWL(1) | SSICR_SWL(3); + rz_ssi_reg_writel(ssi, SSICR, ssicr); + rz_ssi_reg_writel(ssi, SSIFCR, SSIFCR_AUCKE | SSIFCR_FIFO_RST); + + return 0; +} + +static void rz_ssi_set_idle(struct rz_ssi_priv *ssi) +{ + int timeout; + + /* Disable irqs */ + rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TUIEN | SSICR_TOIEN | + SSICR_RUIEN | SSICR_ROIEN, 0); + rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_TIE | SSIFCR_RIE, 0); + + /* Clear all error flags */ + rz_ssi_reg_mask_setl(ssi, SSISR, + (SSISR_TOIRQ | SSISR_TUIRQ | SSISR_ROIRQ | + SSISR_RUIRQ), 0); + + /* Wait for idle */ + timeout = 100; + while (--timeout) { + if (rz_ssi_reg_readl(ssi, SSISR) & SSISR_IIRQ) + break; + udelay(1); + } + + if (!timeout) + dev_info(ssi->dev, "timeout waiting for SSI idle\n"); + + /* Hold FIFOs in reset */ + rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_FIFO_RST); +} + +static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) +{ + bool is_play = rz_ssi_stream_is_play(ssi, strm->substream); + bool is_full_duplex; + u32 ssicr, ssifcr; + + is_full_duplex = rz_ssi_is_stream_running(&ssi->playback) || + rz_ssi_is_stream_running(&ssi->capture); + ssicr = rz_ssi_reg_readl(ssi, SSICR); + ssifcr = rz_ssi_reg_readl(ssi, SSIFCR); + if (!is_full_duplex) { + ssifcr &= ~0xF; + } else { + rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0); + rz_ssi_set_idle(ssi); + ssifcr &= ~SSIFCR_FIFO_RST; + } + + /* FIFO interrupt thresholds */ + if (rz_ssi_is_dma_enabled(ssi)) + rz_ssi_reg_writel(ssi, SSISCR, 0); + else + rz_ssi_reg_writel(ssi, SSISCR, + SSISCR_TDES(strm->fifo_sample_size / 2 - 1) | + SSISCR_RDFS(0)); + + /* enable IRQ */ + if (is_play) { + ssicr |= SSICR_TUIEN | SSICR_TOIEN; + ssifcr |= SSIFCR_TIE; + if (!is_full_duplex) + ssifcr |= SSIFCR_RFRST; + } else { + ssicr |= SSICR_RUIEN | SSICR_ROIEN; + ssifcr |= SSIFCR_RIE; + if (!is_full_duplex) + ssifcr |= SSIFCR_TFRST; + } + + rz_ssi_reg_writel(ssi, SSICR, ssicr); + rz_ssi_reg_writel(ssi, SSIFCR, ssifcr); + + /* Clear all error flags */ + rz_ssi_reg_mask_setl(ssi, SSISR, + (SSISR_TOIRQ | SSISR_TUIRQ | SSISR_ROIRQ | + SSISR_RUIRQ), 0); + + strm->running = 1; + if (is_full_duplex) + ssicr |= SSICR_TEN | SSICR_REN; + else + ssicr |= is_play ? SSICR_TEN : SSICR_REN; + + rz_ssi_reg_writel(ssi, SSICR, ssicr); + + return 0; +} + +static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) +{ + strm->running = 0; + + if (rz_ssi_is_stream_running(&ssi->playback) || + rz_ssi_is_stream_running(&ssi->capture)) + return 0; + + /* Disable TX/RX */ + rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0); + + /* Cancel all remaining DMA transactions */ + if (rz_ssi_is_dma_enabled(ssi)) + dmaengine_terminate_async(strm->dma_ch); + + rz_ssi_set_idle(ssi); + + return 0; +} + +static void rz_ssi_pointer_update(struct rz_ssi_stream *strm, int frames) +{ + struct snd_pcm_substream *substream = strm->substream; + struct snd_pcm_runtime *runtime; + int current_period; + + if (!strm->running || !substream || !substream->runtime) + return; + + runtime = substream->runtime; + strm->buffer_pos += frames; + WARN_ON(strm->buffer_pos > runtime->buffer_size); + + /* ring buffer */ + if (strm->buffer_pos == runtime->buffer_size) + strm->buffer_pos = 0; + + current_period = strm->buffer_pos / runtime->period_size; + if (strm->period_counter != current_period) { + snd_pcm_period_elapsed(strm->substream); + strm->period_counter = current_period; + } +} + +static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) +{ + struct snd_pcm_substream *substream = strm->substream; + struct snd_pcm_runtime *runtime; + u16 *buf; + int fifo_samples; + int frames_left; + int samples; + int i; + + if (!rz_ssi_stream_is_valid(ssi, strm)) + return -EINVAL; + + runtime = substream->runtime; + + do { + /* frames left in this period */ + frames_left = runtime->period_size - + (strm->buffer_pos % runtime->period_size); + if (!frames_left) + frames_left = runtime->period_size; + + /* Samples in RX FIFO */ + fifo_samples = (rz_ssi_reg_readl(ssi, SSIFSR) >> + SSIFSR_RDC_SHIFT) & SSIFSR_RDC_MASK; + + /* Only read full frames at a time */ + samples = 0; + while (frames_left && (fifo_samples >= runtime->channels)) { + samples += runtime->channels; + fifo_samples -= runtime->channels; + frames_left--; + } + + /* not enough samples yet */ + if (!samples) + break; + + /* calculate new buffer index */ + buf = (u16 *)runtime->dma_area; + buf += strm->buffer_pos * runtime->channels; + + /* Note, only supports 16-bit samples */ + for (i = 0; i < samples; i++) + *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16); + + rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); + rz_ssi_pointer_update(strm, samples / runtime->channels); + } while (!frames_left && fifo_samples >= runtime->channels); + + return 0; +} + +static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) +{ + struct snd_pcm_substream *substream = strm->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + int sample_space; + int samples = 0; + int frames_left; + int i; + u32 ssifsr; + u16 *buf; + + if (!rz_ssi_stream_is_valid(ssi, strm)) + return -EINVAL; + + /* frames left in this period */ + frames_left = runtime->period_size - (strm->buffer_pos % + runtime->period_size); + if (frames_left == 0) + frames_left = runtime->period_size; + + sample_space = strm->fifo_sample_size; + ssifsr = rz_ssi_reg_readl(ssi, SSIFSR); + sample_space -= (ssifsr >> SSIFSR_TDC_SHIFT) & SSIFSR_TDC_MASK; + + /* Only add full frames at a time */ + while (frames_left && (sample_space >= runtime->channels)) { + samples += runtime->channels; + sample_space -= runtime->channels; + frames_left--; + } + + /* no space to send anything right now */ + if (samples == 0) + return 0; + + /* calculate new buffer index */ + buf = (u16 *)(runtime->dma_area); + buf += strm->buffer_pos * runtime->channels; + + /* Note, only supports 16-bit samples */ + for (i = 0; i < samples; i++) + rz_ssi_reg_writel(ssi, SSIFTDR, ((u32)(*buf++) << 16)); + + rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_TDE, 0); + rz_ssi_pointer_update(strm, samples / runtime->channels); + + return 0; +} + +static irqreturn_t rz_ssi_interrupt(int irq, void *data) +{ + struct rz_ssi_stream *strm_playback = NULL; + struct rz_ssi_stream *strm_capture = NULL; + struct rz_ssi_priv *ssi = data; + u32 ssisr = rz_ssi_reg_readl(ssi, SSISR); + + if (ssi->playback.substream) + strm_playback = &ssi->playback; + if (ssi->capture.substream) + strm_capture = &ssi->capture; + + if (!strm_playback && !strm_capture) + return IRQ_HANDLED; /* Left over TX/RX interrupt */ + + if (irq == ssi->irq_int) { /* error or idle */ + bool is_stopped = false; + int i, count; + + if (rz_ssi_is_dma_enabled(ssi)) + count = 4; + else + count = 1; + + if (ssisr & (SSISR_RUIRQ | SSISR_ROIRQ | SSISR_TUIRQ | SSISR_TOIRQ)) + is_stopped = true; + + if (ssi->capture.substream && is_stopped) { + if (ssisr & SSISR_RUIRQ) + strm_capture->uerr_num++; + if (ssisr & SSISR_ROIRQ) + strm_capture->oerr_num++; + + rz_ssi_stop(ssi, strm_capture); + } + + if (ssi->playback.substream && is_stopped) { + if (ssisr & SSISR_TUIRQ) + strm_playback->uerr_num++; + if (ssisr & SSISR_TOIRQ) + strm_playback->oerr_num++; + + rz_ssi_stop(ssi, strm_playback); + } + + /* Clear all flags */ + rz_ssi_reg_mask_setl(ssi, SSISR, SSISR_TOIRQ | SSISR_TUIRQ | + SSISR_ROIRQ | SSISR_RUIRQ, 0); + + /* Add/remove more data */ + if (ssi->capture.substream && is_stopped) { + for (i = 0; i < count; i++) + strm_capture->transfer(ssi, strm_capture); + } + + if (ssi->playback.substream && is_stopped) { + for (i = 0; i < count; i++) + strm_playback->transfer(ssi, strm_playback); + } + + /* Resume */ + if (ssi->playback.substream && is_stopped) + rz_ssi_start(ssi, &ssi->playback); + if (ssi->capture.substream && is_stopped) + rz_ssi_start(ssi, &ssi->capture); + } + + if (!rz_ssi_is_stream_running(&ssi->playback) && + !rz_ssi_is_stream_running(&ssi->capture)) + return IRQ_HANDLED; + + /* tx data empty */ + if (irq == ssi->irq_tx && rz_ssi_is_stream_running(&ssi->playback)) + strm_playback->transfer(ssi, &ssi->playback); + + /* rx data full */ + if (irq == ssi->irq_rx && rz_ssi_is_stream_running(&ssi->capture)) { + strm_capture->transfer(ssi, &ssi->capture); + rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); + } + + if (irq == ssi->irq_rt) { + if (ssi->playback.substream) { + strm_playback->transfer(ssi, &ssi->playback); + } else { + strm_capture->transfer(ssi, &ssi->capture); + rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); + } + } + + return IRQ_HANDLED; +} + +static int rz_ssi_dma_slave_config(struct rz_ssi_priv *ssi, + struct dma_chan *dma_ch, bool is_play) +{ + struct dma_slave_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + + cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + cfg.dst_addr = ssi->phys + SSIFTDR; + cfg.src_addr = ssi->phys + SSIFRDR; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + return dmaengine_slave_config(dma_ch, &cfg); +} + +static int rz_ssi_dma_transfer(struct rz_ssi_priv *ssi, + struct rz_ssi_stream *strm) +{ + struct snd_pcm_substream *substream = strm->substream; + struct dma_async_tx_descriptor *desc; + struct snd_pcm_runtime *runtime; + enum dma_transfer_direction dir; + u32 dma_paddr, dma_size; + int amount; + + if (!rz_ssi_stream_is_valid(ssi, strm)) + return -EINVAL; + + runtime = substream->runtime; + if (runtime->state == SNDRV_PCM_STATE_DRAINING) + /* + * Stream is ending, so do not queue up any more DMA + * transfers otherwise we play partial sound clips + * because we can't shut off the DMA quick enough. + */ + return 0; + + dir = rz_ssi_stream_is_play(ssi, substream) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + + /* Always transfer 1 period */ + amount = runtime->period_size; + + /* DMA physical address and size */ + dma_paddr = runtime->dma_addr + frames_to_bytes(runtime, + strm->dma_buffer_pos); + dma_size = frames_to_bytes(runtime, amount); + desc = dmaengine_prep_slave_single(strm->dma_ch, dma_paddr, dma_size, + dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(ssi->dev, "dmaengine_prep_slave_single() fail\n"); + return -ENOMEM; + } + + desc->callback = rz_ssi_dma_complete; + desc->callback_param = strm; + + if (dmaengine_submit(desc) < 0) { + dev_err(ssi->dev, "dmaengine_submit() fail\n"); + return -EIO; + } + + /* Update DMA pointer */ + strm->dma_buffer_pos += amount; + if (strm->dma_buffer_pos >= runtime->buffer_size) + strm->dma_buffer_pos = 0; + + /* Start DMA */ + dma_async_issue_pending(strm->dma_ch); + + return 0; +} + +static void rz_ssi_dma_complete(void *data) +{ + struct rz_ssi_stream *strm = (struct rz_ssi_stream *)data; + + if (!strm->running || !strm->substream || !strm->substream->runtime) + return; + + /* Note that next DMA transaction has probably already started */ + rz_ssi_pointer_update(strm, strm->substream->runtime->period_size); + + /* Queue up another DMA transaction */ + rz_ssi_dma_transfer(strm->priv, strm); +} + +static void rz_ssi_release_dma_channels(struct rz_ssi_priv *ssi) +{ + if (ssi->playback.dma_ch) { + dma_release_channel(ssi->playback.dma_ch); + ssi->playback.dma_ch = NULL; + if (ssi->dma_rt) + ssi->dma_rt = false; + } + + if (ssi->capture.dma_ch) { + dma_release_channel(ssi->capture.dma_ch); + ssi->capture.dma_ch = NULL; + } +} + +static int rz_ssi_dma_request(struct rz_ssi_priv *ssi, struct device *dev) +{ + ssi->playback.dma_ch = dma_request_chan(dev, "tx"); + if (IS_ERR(ssi->playback.dma_ch)) + ssi->playback.dma_ch = NULL; + + ssi->capture.dma_ch = dma_request_chan(dev, "rx"); + if (IS_ERR(ssi->capture.dma_ch)) + ssi->capture.dma_ch = NULL; + + if (!ssi->playback.dma_ch && !ssi->capture.dma_ch) { + ssi->playback.dma_ch = dma_request_chan(dev, "rt"); + if (IS_ERR(ssi->playback.dma_ch)) { + ssi->playback.dma_ch = NULL; + goto no_dma; + } + + ssi->dma_rt = true; + } + + if (!rz_ssi_is_dma_enabled(ssi)) + goto no_dma; + + if (ssi->playback.dma_ch && + (rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, true) < 0)) + goto no_dma; + + if (ssi->capture.dma_ch && + (rz_ssi_dma_slave_config(ssi, ssi->capture.dma_ch, false) < 0)) + goto no_dma; + + return 0; + +no_dma: + rz_ssi_release_dma_channels(ssi); + + return -ENODEV; +} + +static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); + struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream); + int ret = 0, i, num_transfer = 1; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* Soft Reset */ + if (!rz_ssi_is_stream_running(&ssi->playback) && + !rz_ssi_is_stream_running(&ssi->capture)) { + rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_SSIRST); + rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_SSIRST, 0); + udelay(5); + } + + rz_ssi_stream_init(strm, substream); + + if (ssi->dma_rt) { + bool is_playback; + + is_playback = rz_ssi_stream_is_play(ssi, substream); + ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, + is_playback); + /* Fallback to pio */ + if (ret < 0) { + ssi->playback.transfer = rz_ssi_pio_send; + ssi->capture.transfer = rz_ssi_pio_recv; + rz_ssi_release_dma_channels(ssi); + } + } + + /* For DMA, queue up multiple DMA descriptors */ + if (rz_ssi_is_dma_enabled(ssi)) + num_transfer = 4; + + for (i = 0; i < num_transfer; i++) { + ret = strm->transfer(ssi, strm); + if (ret) + goto done; + } + + ret = rz_ssi_start(ssi, strm); + break; + case SNDRV_PCM_TRIGGER_STOP: + rz_ssi_stop(ssi, strm); + rz_ssi_stream_quit(ssi, strm); + break; + } + +done: + return ret; +} + +static int rz_ssi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BP_FP: + break; + default: + dev_err(ssi->dev, "Codec should be clk and frame consumer\n"); + return -EINVAL; + } + + /* + * set clock polarity + * + * "normal" BCLK = Signal is available at rising edge of BCLK + * "normal" FSYNC = (I2S) Left ch starts with falling FSYNC edge + */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + ssi->bckp_rise = false; + ssi->lrckp_fsync_fall = false; + break; + case SND_SOC_DAIFMT_NB_IF: + ssi->bckp_rise = false; + ssi->lrckp_fsync_fall = true; + break; + case SND_SOC_DAIFMT_IB_NF: + ssi->bckp_rise = true; + ssi->lrckp_fsync_fall = false; + break; + case SND_SOC_DAIFMT_IB_IF: + ssi->bckp_rise = true; + ssi->lrckp_fsync_fall = true; + break; + default: + return -EINVAL; + } + + /* only i2s support */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + default: + dev_err(ssi->dev, "Only I2S mode is supported.\n"); + return -EINVAL; + } + + return 0; +} + +static bool rz_ssi_is_valid_hw_params(struct rz_ssi_priv *ssi, unsigned int rate, + unsigned int channels, + unsigned int sample_width, + unsigned int sample_bits) +{ + if (ssi->hw_params_cache.rate != rate || + ssi->hw_params_cache.channels != channels || + ssi->hw_params_cache.sample_width != sample_width || + ssi->hw_params_cache.sample_bits != sample_bits) + return false; + + return true; +} + +static void rz_ssi_cache_hw_params(struct rz_ssi_priv *ssi, unsigned int rate, + unsigned int channels, + unsigned int sample_width, + unsigned int sample_bits) +{ + ssi->hw_params_cache.rate = rate; + ssi->hw_params_cache.channels = channels; + ssi->hw_params_cache.sample_width = sample_width; + ssi->hw_params_cache.sample_bits = sample_bits; +} + +static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); + struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream); + unsigned int sample_bits = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + + if (sample_bits != 16) { + dev_err(ssi->dev, "Unsupported sample width: %d\n", + sample_bits); + return -EINVAL; + } + + if (channels != 2) { + dev_err(ssi->dev, "Number of channels not matched: %d\n", + channels); + return -EINVAL; + } + + if (rz_ssi_is_stream_running(&ssi->playback) || + rz_ssi_is_stream_running(&ssi->capture)) { + if (rz_ssi_is_valid_hw_params(ssi, rate, channels, + strm->sample_width, sample_bits)) + return 0; + + dev_err(ssi->dev, "Full duplex needs same HW params\n"); + return -EINVAL; + } + + rz_ssi_cache_hw_params(ssi, rate, channels, strm->sample_width, + sample_bits); + + return rz_ssi_clk_setup(ssi, rate, channels); +} + +static const struct snd_soc_dai_ops rz_ssi_dai_ops = { + .trigger = rz_ssi_dai_trigger, + .set_fmt = rz_ssi_dai_set_fmt, + .hw_params = rz_ssi_dai_hw_params, +}; + +static const struct snd_pcm_hardware rz_ssi_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .buffer_bytes_max = PREALLOC_BUFFER, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .channels_min = SSI_CHAN_MIN, + .channels_max = SSI_CHAN_MAX, + .periods_min = 1, + .periods_max = 32, + .fifo_size = 32 * 2, +}; + +static int rz_ssi_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + snd_soc_set_runtime_hwparams(substream, &rz_ssi_pcm_hardware); + + return snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); +} + +static snd_pcm_uframes_t rz_ssi_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_dai *dai = rz_ssi_get_dai(substream); + struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); + struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream); + + return strm->buffer_pos; +} + +static int rz_ssi_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, + rtd->card->snd_card->dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); + return 0; +} + +static struct snd_soc_dai_driver rz_ssi_soc_dai[] = { + { + .name = "rz-ssi-dai", + .playback = { + .rates = SSI_RATES, + .formats = SSI_FMTS, + .channels_min = SSI_CHAN_MIN, + .channels_max = SSI_CHAN_MAX, + }, + .capture = { + .rates = SSI_RATES, + .formats = SSI_FMTS, + .channels_min = SSI_CHAN_MIN, + .channels_max = SSI_CHAN_MAX, + }, + .ops = &rz_ssi_dai_ops, + }, +}; + +static const struct snd_soc_component_driver rz_ssi_soc_component = { + .name = "rz-ssi", + .open = rz_ssi_pcm_open, + .pointer = rz_ssi_pcm_pointer, + .pcm_construct = rz_ssi_pcm_new, + .legacy_dai_naming = 1, +}; + +static int rz_ssi_probe(struct platform_device *pdev) +{ + struct rz_ssi_priv *ssi; + struct clk *audio_clk; + struct resource *res; + int ret; + + ssi = devm_kzalloc(&pdev->dev, sizeof(*ssi), GFP_KERNEL); + if (!ssi) + return -ENOMEM; + + ssi->pdev = pdev; + ssi->dev = &pdev->dev; + ssi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(ssi->base)) + return PTR_ERR(ssi->base); + + ssi->phys = res->start; + ssi->clk = devm_clk_get(&pdev->dev, "ssi"); + if (IS_ERR(ssi->clk)) + return PTR_ERR(ssi->clk); + + ssi->sfr_clk = devm_clk_get(&pdev->dev, "ssi_sfr"); + if (IS_ERR(ssi->sfr_clk)) + return PTR_ERR(ssi->sfr_clk); + + audio_clk = devm_clk_get(&pdev->dev, "audio_clk1"); + if (IS_ERR(audio_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk), + "no audio clk1"); + + ssi->audio_clk_1 = clk_get_rate(audio_clk); + audio_clk = devm_clk_get(&pdev->dev, "audio_clk2"); + if (IS_ERR(audio_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk), + "no audio clk2"); + + ssi->audio_clk_2 = clk_get_rate(audio_clk); + if (!(ssi->audio_clk_1 || ssi->audio_clk_2)) + return dev_err_probe(&pdev->dev, -EINVAL, + "no audio clk1 or audio clk2"); + + ssi->audio_mck = ssi->audio_clk_1 ? ssi->audio_clk_1 : ssi->audio_clk_2; + + /* Detect DMA support */ + ret = rz_ssi_dma_request(ssi, &pdev->dev); + if (ret < 0) { + dev_warn(&pdev->dev, "DMA not available, using PIO\n"); + ssi->playback.transfer = rz_ssi_pio_send; + ssi->capture.transfer = rz_ssi_pio_recv; + } else { + dev_info(&pdev->dev, "DMA enabled"); + ssi->playback.transfer = rz_ssi_dma_transfer; + ssi->capture.transfer = rz_ssi_dma_transfer; + } + + ssi->playback.priv = ssi; + ssi->capture.priv = ssi; + + spin_lock_init(&ssi->lock); + dev_set_drvdata(&pdev->dev, ssi); + + /* Error Interrupt */ + ssi->irq_int = platform_get_irq_byname(pdev, "int_req"); + if (ssi->irq_int < 0) { + rz_ssi_release_dma_channels(ssi); + return ssi->irq_int; + } + + ret = devm_request_irq(&pdev->dev, ssi->irq_int, &rz_ssi_interrupt, + 0, dev_name(&pdev->dev), ssi); + if (ret < 0) { + rz_ssi_release_dma_channels(ssi); + return dev_err_probe(&pdev->dev, ret, + "irq request error (int_req)\n"); + } + + if (!rz_ssi_is_dma_enabled(ssi)) { + /* Tx and Rx interrupts (pio only) */ + ssi->irq_tx = platform_get_irq_byname(pdev, "dma_tx"); + ssi->irq_rx = platform_get_irq_byname(pdev, "dma_rx"); + if (ssi->irq_tx == -ENXIO && ssi->irq_rx == -ENXIO) { + ssi->irq_rt = platform_get_irq_byname(pdev, "dma_rt"); + if (ssi->irq_rt < 0) + return ssi->irq_rt; + + ret = devm_request_irq(&pdev->dev, ssi->irq_rt, + &rz_ssi_interrupt, 0, + dev_name(&pdev->dev), ssi); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "irq request error (dma_rt)\n"); + } else { + if (ssi->irq_tx < 0) + return ssi->irq_tx; + + if (ssi->irq_rx < 0) + return ssi->irq_rx; + + ret = devm_request_irq(&pdev->dev, ssi->irq_tx, + &rz_ssi_interrupt, 0, + dev_name(&pdev->dev), ssi); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "irq request error (dma_tx)\n"); + + ret = devm_request_irq(&pdev->dev, ssi->irq_rx, + &rz_ssi_interrupt, 0, + dev_name(&pdev->dev), ssi); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "irq request error (dma_rx)\n"); + } + } + + ssi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(ssi->rstc)) { + ret = PTR_ERR(ssi->rstc); + goto err_reset; + } + + reset_control_deassert(ssi->rstc); + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "pm_runtime_resume_and_get failed\n"); + goto err_pm; + } + + ret = devm_snd_soc_register_component(&pdev->dev, &rz_ssi_soc_component, + rz_ssi_soc_dai, + ARRAY_SIZE(rz_ssi_soc_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register snd component\n"); + goto err_snd_soc; + } + + return 0; + +err_snd_soc: + pm_runtime_put(ssi->dev); +err_pm: + pm_runtime_disable(ssi->dev); + reset_control_assert(ssi->rstc); +err_reset: + rz_ssi_release_dma_channels(ssi); + + return ret; +} + +static void rz_ssi_remove(struct platform_device *pdev) +{ + struct rz_ssi_priv *ssi = dev_get_drvdata(&pdev->dev); + + rz_ssi_release_dma_channels(ssi); + + pm_runtime_put(ssi->dev); + pm_runtime_disable(ssi->dev); + reset_control_assert(ssi->rstc); +} + +static const struct of_device_id rz_ssi_of_match[] = { + { .compatible = "renesas,rz-ssi", }, + {/* Sentinel */}, +}; +MODULE_DEVICE_TABLE(of, rz_ssi_of_match); + +static struct platform_driver rz_ssi_driver = { + .driver = { + .name = "rz-ssi-pcm-audio", + .of_match_table = rz_ssi_of_match, + }, + .probe = rz_ssi_probe, + .remove = rz_ssi_remove, +}; + +module_platform_driver(rz_ssi_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas RZ/G2L ASoC Serial Sound Interface Driver"); +MODULE_AUTHOR("Biju Das "); diff --git a/sound/soc/renesas/sh7760-ac97.c b/sound/soc/renesas/sh7760-ac97.c new file mode 100644 index 000000000000..d267243a159b --- /dev/null +++ b/sound/soc/renesas/sh7760-ac97.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Generic AC97 sound support for SH7760 +// +// (c) 2007 Manuel Lauss + +#include +#include +#include +#include +#include +#include +#include + +#define IPSEL 0xFE400034 + +SND_SOC_DAILINK_DEFS(ac97, + DAILINK_COMP_ARRAY(COMP_CPU("hac-dai.0")), /* HAC0 */ + DAILINK_COMP_ARRAY(COMP_CODEC("ac97-codec", "ac97-hifi")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("sh7760-pcm-audio"))); + +static struct snd_soc_dai_link sh7760_ac97_dai = { + .name = "AC97", + .stream_name = "AC97 HiFi", + SND_SOC_DAILINK_REG(ac97), +}; + +static struct snd_soc_card sh7760_ac97_soc_machine = { + .name = "SH7760 AC97", + .owner = THIS_MODULE, + .dai_link = &sh7760_ac97_dai, + .num_links = 1, +}; + +static struct platform_device *sh7760_ac97_snd_device; + +static int __init sh7760_ac97_init(void) +{ + int ret; + unsigned short ipsel; + + /* enable both AC97 controllers in pinmux reg */ + ipsel = __raw_readw(IPSEL); + __raw_writew(ipsel | (3 << 10), IPSEL); + + ret = -ENOMEM; + sh7760_ac97_snd_device = platform_device_alloc("soc-audio", -1); + if (!sh7760_ac97_snd_device) + goto out; + + platform_set_drvdata(sh7760_ac97_snd_device, + &sh7760_ac97_soc_machine); + ret = platform_device_add(sh7760_ac97_snd_device); + + if (ret) + platform_device_put(sh7760_ac97_snd_device); + +out: + return ret; +} + +static void __exit sh7760_ac97_exit(void) +{ + platform_device_unregister(sh7760_ac97_snd_device); +} + +module_init(sh7760_ac97_init); +module_exit(sh7760_ac97_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine"); +MODULE_AUTHOR("Manuel Lauss "); diff --git a/sound/soc/renesas/siu.h b/sound/soc/renesas/siu.h new file mode 100644 index 000000000000..a675c36fc9d9 --- /dev/null +++ b/sound/soc/renesas/siu.h @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski +// Copyright (C) 2006 Carlos Munoz + +#ifndef SIU_H +#define SIU_H + +/* Common kernel and user-space firmware-building defines and types */ + +#define YRAM0_SIZE (0x0040 / 4) /* 16 */ +#define YRAM1_SIZE (0x0080 / 4) /* 32 */ +#define YRAM2_SIZE (0x0040 / 4) /* 16 */ +#define YRAM3_SIZE (0x0080 / 4) /* 32 */ +#define YRAM4_SIZE (0x0080 / 4) /* 32 */ +#define YRAM_DEF_SIZE (YRAM0_SIZE + YRAM1_SIZE + YRAM2_SIZE + \ + YRAM3_SIZE + YRAM4_SIZE) +#define YRAM_FIR_SIZE (0x0400 / 4) /* 256 */ +#define YRAM_IIR_SIZE (0x0200 / 4) /* 128 */ + +#define XRAM0_SIZE (0x0400 / 4) /* 256 */ +#define XRAM1_SIZE (0x0200 / 4) /* 128 */ +#define XRAM2_SIZE (0x0200 / 4) /* 128 */ + +/* PRAM program array size */ +#define PRAM0_SIZE (0x0100 / 4) /* 64 */ +#define PRAM1_SIZE ((0x2000 - 0x0100) / 4) /* 1984 */ + +#include + +struct siu_spb_param { + __u32 ab1a; /* input FIFO address */ + __u32 ab0a; /* output FIFO address */ + __u32 dir; /* 0=the ather except CPUOUTPUT, 1=CPUINPUT */ + __u32 event; /* SPB program starting conditions */ + __u32 stfifo; /* STFIFO register setting value */ + __u32 trdat; /* TRDAT register setting value */ +}; + +struct siu_firmware { + __u32 yram_fir_coeff[YRAM_FIR_SIZE]; + __u32 pram0[PRAM0_SIZE]; + __u32 pram1[PRAM1_SIZE]; + __u32 yram0[YRAM0_SIZE]; + __u32 yram1[YRAM1_SIZE]; + __u32 yram2[YRAM2_SIZE]; + __u32 yram3[YRAM3_SIZE]; + __u32 yram4[YRAM4_SIZE]; + __u32 spbpar_num; + struct siu_spb_param spbpar[32]; +}; + +#ifdef __KERNEL__ + +#include +#include +#include +#include + +#include +#include +#include + +#define SIU_PERIOD_BYTES_MAX 8192 /* DMA transfer/period size */ +#define SIU_PERIOD_BYTES_MIN 256 /* DMA transfer/period size */ +#define SIU_PERIODS_MAX 64 /* Max periods in buffer */ +#define SIU_PERIODS_MIN 4 /* Min periods in buffer */ +#define SIU_BUFFER_BYTES_MAX (SIU_PERIOD_BYTES_MAX * SIU_PERIODS_MAX) + +/* SIU ports: only one can be used at a time */ +enum { + SIU_PORT_A, + SIU_PORT_B, + SIU_PORT_NUM, +}; + +/* SIU clock configuration */ +enum { + SIU_CLKA_PLL, + SIU_CLKA_EXT, + SIU_CLKB_PLL, + SIU_CLKB_EXT +}; + +struct device; +struct siu_info { + struct device *dev; + int port_id; + u32 __iomem *pram; + u32 __iomem *xram; + u32 __iomem *yram; + u32 __iomem *reg; + struct siu_firmware fw; +}; + +struct siu_stream { + struct work_struct work; + struct snd_pcm_substream *substream; + snd_pcm_format_t format; + size_t buf_bytes; + size_t period_bytes; + int cur_period; /* Period currently in dma */ + u32 volume; + snd_pcm_sframes_t xfer_cnt; /* Number of frames */ + u8 rw_flg; /* transfer status */ + /* DMA status */ + struct dma_chan *chan; /* DMA channel */ + struct dma_async_tx_descriptor *tx_desc; + dma_cookie_t cookie; + struct sh_dmae_slave param; +}; + +struct siu_port { + unsigned long play_cap; /* Used to track full duplex */ + struct snd_pcm *pcm; + struct siu_stream playback; + struct siu_stream capture; + u32 stfifo; /* STFIFO value from firmware */ + u32 trdat; /* TRDAT value from firmware */ +}; + +extern struct siu_port *siu_ports[SIU_PORT_NUM]; + +static inline struct siu_port *siu_port_info(struct snd_pcm_substream *substream) +{ + struct platform_device *pdev = + to_platform_device(substream->pcm->card->dev); + return siu_ports[pdev->id]; +} + +/* Register access */ +static inline void siu_write32(u32 __iomem *addr, u32 val) +{ + __raw_writel(val, addr); +} + +static inline u32 siu_read32(u32 __iomem *addr) +{ + return __raw_readl(addr); +} + +/* SIU registers */ +#define SIU_IFCTL (0x000 / sizeof(u32)) +#define SIU_SRCTL (0x004 / sizeof(u32)) +#define SIU_SFORM (0x008 / sizeof(u32)) +#define SIU_CKCTL (0x00c / sizeof(u32)) +#define SIU_TRDAT (0x010 / sizeof(u32)) +#define SIU_STFIFO (0x014 / sizeof(u32)) +#define SIU_DPAK (0x01c / sizeof(u32)) +#define SIU_CKREV (0x020 / sizeof(u32)) +#define SIU_EVNTC (0x028 / sizeof(u32)) +#define SIU_SBCTL (0x040 / sizeof(u32)) +#define SIU_SBPSET (0x044 / sizeof(u32)) +#define SIU_SBFSTS (0x068 / sizeof(u32)) +#define SIU_SBDVCA (0x06c / sizeof(u32)) +#define SIU_SBDVCB (0x070 / sizeof(u32)) +#define SIU_SBACTIV (0x074 / sizeof(u32)) +#define SIU_DMAIA (0x090 / sizeof(u32)) +#define SIU_DMAIB (0x094 / sizeof(u32)) +#define SIU_DMAOA (0x098 / sizeof(u32)) +#define SIU_DMAOB (0x09c / sizeof(u32)) +#define SIU_DMAML (0x0a0 / sizeof(u32)) +#define SIU_SPSTS (0x0cc / sizeof(u32)) +#define SIU_SPCTL (0x0d0 / sizeof(u32)) +#define SIU_BRGASEL (0x100 / sizeof(u32)) +#define SIU_BRRA (0x104 / sizeof(u32)) +#define SIU_BRGBSEL (0x108 / sizeof(u32)) +#define SIU_BRRB (0x10c / sizeof(u32)) + +extern const struct snd_soc_component_driver siu_component; +extern struct siu_info *siu_i2s_data; + +int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card); +void siu_free_port(struct siu_port *port_info); + +#endif + +#endif /* SIU_H */ diff --git a/sound/soc/renesas/siu_dai.c b/sound/soc/renesas/siu_dai.c new file mode 100644 index 000000000000..7e771a164a80 --- /dev/null +++ b/sound/soc/renesas/siu_dai.c @@ -0,0 +1,800 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski +// Copyright (C) 2006 Carlos Munoz + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "siu.h" + +/* Board specifics */ +#if defined(CONFIG_CPU_SUBTYPE_SH7722) +# define SIU_MAX_VOLUME 0x1000 +#else +# define SIU_MAX_VOLUME 0x7fff +#endif + +#define PRAM_SIZE 0x2000 +#define XRAM_SIZE 0x800 +#define YRAM_SIZE 0x800 + +#define XRAM_OFFSET 0x4000 +#define YRAM_OFFSET 0x6000 +#define REG_OFFSET 0xc000 + +#define PLAYBACK_ENABLED 1 +#define CAPTURE_ENABLED 2 + +#define VOLUME_CAPTURE 0 +#define VOLUME_PLAYBACK 1 +#define DFLT_VOLUME_LEVEL 0x08000800 + +/* + * SPDIF is only available on port A and on some SIU implementations it is only + * available for input. Due to the lack of hardware to test it, SPDIF is left + * disabled in this driver version + */ +struct format_flag { + u32 i2s; + u32 pcm; + u32 spdif; + u32 mask; +}; + +struct port_flag { + struct format_flag playback; + struct format_flag capture; +}; + +struct siu_info *siu_i2s_data; + +static struct port_flag siu_flags[SIU_PORT_NUM] = { + [SIU_PORT_A] = { + .playback = { + .i2s = 0x50000000, + .pcm = 0x40000000, + .spdif = 0x80000000, /* not on all SIU versions */ + .mask = 0xd0000000, + }, + .capture = { + .i2s = 0x05000000, + .pcm = 0x04000000, + .spdif = 0x08000000, + .mask = 0x0d000000, + }, + }, + [SIU_PORT_B] = { + .playback = { + .i2s = 0x00500000, + .pcm = 0x00400000, + .spdif = 0, /* impossible - turn off */ + .mask = 0x00500000, + }, + .capture = { + .i2s = 0x00050000, + .pcm = 0x00040000, + .spdif = 0, /* impossible - turn off */ + .mask = 0x00050000, + }, + }, +}; + +static void siu_dai_start(struct siu_port *port_info) +{ + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + + dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); + + /* Issue software reset to siu */ + siu_write32(base + SIU_SRCTL, 0); + + /* Wait for the reset to take effect */ + udelay(1); + + port_info->stfifo = 0; + port_info->trdat = 0; + + /* portA, portB, SIU operate */ + siu_write32(base + SIU_SRCTL, 0x301); + + /* portA=256fs, portB=256fs */ + siu_write32(base + SIU_CKCTL, 0x40400000); + + /* portA's BRG does not divide SIUCKA */ + siu_write32(base + SIU_BRGASEL, 0); + siu_write32(base + SIU_BRRA, 0); + + /* portB's BRG divides SIUCKB by half */ + siu_write32(base + SIU_BRGBSEL, 1); + siu_write32(base + SIU_BRRB, 0); + + siu_write32(base + SIU_IFCTL, 0x44440000); + + /* portA: 32 bit/fs, master; portB: 32 bit/fs, master */ + siu_write32(base + SIU_SFORM, 0x0c0c0000); + + /* + * Volume levels: looks like the DSP firmware implements volume controls + * differently from what's described in the datasheet + */ + siu_write32(base + SIU_SBDVCA, port_info->playback.volume); + siu_write32(base + SIU_SBDVCB, port_info->capture.volume); +} + +static void siu_dai_stop(struct siu_port *port_info) +{ + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + + /* SIU software reset */ + siu_write32(base + SIU_SRCTL, 0); +} + +static void siu_dai_spbAselect(struct siu_port *port_info) +{ + struct siu_info *info = siu_i2s_data; + struct siu_firmware *fw = &info->fw; + u32 *ydef = fw->yram0; + u32 idx; + + /* path A use */ + if (!info->port_id) + idx = 1; /* portA */ + else + idx = 2; /* portB */ + + ydef[0] = (fw->spbpar[idx].ab1a << 16) | + (fw->spbpar[idx].ab0a << 8) | + (fw->spbpar[idx].dir << 7) | 3; + ydef[1] = fw->yram0[1]; /* 0x03000300 */ + ydef[2] = (16 / 2) << 24; + ydef[3] = fw->yram0[3]; /* 0 */ + ydef[4] = fw->yram0[4]; /* 0 */ + ydef[7] = fw->spbpar[idx].event; + port_info->stfifo |= fw->spbpar[idx].stfifo; + port_info->trdat |= fw->spbpar[idx].trdat; +} + +static void siu_dai_spbBselect(struct siu_port *port_info) +{ + struct siu_info *info = siu_i2s_data; + struct siu_firmware *fw = &info->fw; + u32 *ydef = fw->yram0; + u32 idx; + + /* path B use */ + if (!info->port_id) + idx = 7; /* portA */ + else + idx = 8; /* portB */ + + ydef[5] = (fw->spbpar[idx].ab1a << 16) | + (fw->spbpar[idx].ab0a << 8) | 1; + ydef[6] = fw->spbpar[idx].event; + port_info->stfifo |= fw->spbpar[idx].stfifo; + port_info->trdat |= fw->spbpar[idx].trdat; +} + +static void siu_dai_open(struct siu_stream *siu_stream) +{ + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + u32 srctl, ifctl; + + srctl = siu_read32(base + SIU_SRCTL); + ifctl = siu_read32(base + SIU_IFCTL); + + switch (info->port_id) { + case SIU_PORT_A: + /* portA operates */ + srctl |= 0x200; + ifctl &= ~0xc2; + break; + case SIU_PORT_B: + /* portB operates */ + srctl |= 0x100; + ifctl &= ~0x31; + break; + } + + siu_write32(base + SIU_SRCTL, srctl); + /* Unmute and configure portA */ + siu_write32(base + SIU_IFCTL, ifctl); +} + +/* + * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower + * packing is supported + */ +static void siu_dai_pcmdatapack(struct siu_stream *siu_stream) +{ + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + u32 dpak; + + dpak = siu_read32(base + SIU_DPAK); + + switch (info->port_id) { + case SIU_PORT_A: + dpak &= ~0xc0000000; + break; + case SIU_PORT_B: + dpak &= ~0x00c00000; + break; + } + + siu_write32(base + SIU_DPAK, dpak); +} + +static int siu_dai_spbstart(struct siu_port *port_info) +{ + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + struct siu_firmware *fw = &info->fw; + u32 *ydef = fw->yram0; + int cnt; + u32 __iomem *add; + u32 *ptr; + + /* Load SPB Program in PRAM */ + ptr = fw->pram0; + add = info->pram; + for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++) + siu_write32(add, *ptr); + + ptr = fw->pram1; + add = info->pram + (0x0100 / sizeof(u32)); + for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++) + siu_write32(add, *ptr); + + /* XRAM initialization */ + add = info->xram; + for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++) + siu_write32(add, 0); + + /* YRAM variable area initialization */ + add = info->yram; + for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++) + siu_write32(add, ydef[cnt]); + + /* YRAM FIR coefficient area initialization */ + add = info->yram + (0x0200 / sizeof(u32)); + for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++) + siu_write32(add, fw->yram_fir_coeff[cnt]); + + /* YRAM IIR coefficient area initialization */ + add = info->yram + (0x0600 / sizeof(u32)); + for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++) + siu_write32(add, 0); + + siu_write32(base + SIU_TRDAT, port_info->trdat); + port_info->trdat = 0x0; + + + /* SPB start condition: software */ + siu_write32(base + SIU_SBACTIV, 0); + /* Start SPB */ + siu_write32(base + SIU_SBCTL, 0xc0000000); + /* Wait for program to halt */ + cnt = 0x10000; + while (--cnt && siu_read32(base + SIU_SBCTL) != 0x80000000) + cpu_relax(); + + if (!cnt) + return -EBUSY; + + /* SPB program start address setting */ + siu_write32(base + SIU_SBPSET, 0x00400000); + /* SPB hardware start(FIFOCTL source) */ + siu_write32(base + SIU_SBACTIV, 0xc0000000); + + return 0; +} + +static void siu_dai_spbstop(struct siu_port *port_info) +{ + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + + siu_write32(base + SIU_SBACTIV, 0); + /* SPB stop */ + siu_write32(base + SIU_SBCTL, 0); + + port_info->stfifo = 0; +} + +/* API functions */ + +/* Playback and capture hardware properties are identical */ +static const struct snd_pcm_hardware siu_dai_pcm_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = SIU_BUFFER_BYTES_MAX, + .period_bytes_min = SIU_PERIOD_BYTES_MIN, + .period_bytes_max = SIU_PERIOD_BYTES_MAX, + .periods_min = SIU_PERIODS_MIN, + .periods_max = SIU_PERIODS_MAX, +}; + +static int siu_dai_info_volume(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_info *uinfo) +{ + struct siu_port *port_info = snd_kcontrol_chip(kctrl); + + dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SIU_MAX_VOLUME; + + return 0; +} + +static int siu_dai_get_volume(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct siu_port *port_info = snd_kcontrol_chip(kctrl); + struct device *dev = port_info->pcm->card->dev; + u32 vol; + + dev_dbg(dev, "%s\n", __func__); + + switch (kctrl->private_value) { + case VOLUME_PLAYBACK: + /* Playback is always on port 0 */ + vol = port_info->playback.volume; + ucontrol->value.integer.value[0] = vol & 0xffff; + ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; + break; + case VOLUME_CAPTURE: + /* Capture is always on port 1 */ + vol = port_info->capture.volume; + ucontrol->value.integer.value[0] = vol & 0xffff; + ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; + break; + default: + dev_err(dev, "%s() invalid private_value=%ld\n", + __func__, kctrl->private_value); + return -EINVAL; + } + + return 0; +} + +static int siu_dai_put_volume(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct siu_port *port_info = snd_kcontrol_chip(kctrl); + struct device *dev = port_info->pcm->card->dev; + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + u32 new_vol; + u32 cur_vol; + + dev_dbg(dev, "%s\n", __func__); + + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > SIU_MAX_VOLUME || + ucontrol->value.integer.value[1] < 0 || + ucontrol->value.integer.value[1] > SIU_MAX_VOLUME) + return -EINVAL; + + new_vol = ucontrol->value.integer.value[0] | + ucontrol->value.integer.value[1] << 16; + + /* See comment above - DSP firmware implementation */ + switch (kctrl->private_value) { + case VOLUME_PLAYBACK: + /* Playback is always on port 0 */ + cur_vol = port_info->playback.volume; + siu_write32(base + SIU_SBDVCA, new_vol); + port_info->playback.volume = new_vol; + break; + case VOLUME_CAPTURE: + /* Capture is always on port 1 */ + cur_vol = port_info->capture.volume; + siu_write32(base + SIU_SBDVCB, new_vol); + port_info->capture.volume = new_vol; + break; + default: + dev_err(dev, "%s() invalid private_value=%ld\n", + __func__, kctrl->private_value); + return -EINVAL; + } + + if (cur_vol != new_vol) + return 1; + + return 0; +} + +static const struct snd_kcontrol_new playback_controls = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .index = 0, + .info = siu_dai_info_volume, + .get = siu_dai_get_volume, + .put = siu_dai_put_volume, + .private_value = VOLUME_PLAYBACK, +}; + +static const struct snd_kcontrol_new capture_controls = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Capture Volume", + .index = 0, + .info = siu_dai_info_volume, + .get = siu_dai_get_volume, + .put = siu_dai_put_volume, + .private_value = VOLUME_CAPTURE, +}; + +int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card) +{ + struct device *dev = card->dev; + struct snd_kcontrol *kctrl; + int ret; + + *port_info = kzalloc(sizeof(**port_info), GFP_KERNEL); + if (!*port_info) + return -ENOMEM; + + dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info); + + (*port_info)->playback.volume = DFLT_VOLUME_LEVEL; + (*port_info)->capture.volume = DFLT_VOLUME_LEVEL; + + /* + * Add mixer support. The SPB is used to change the volume. Both + * ports use the same SPB. Therefore, we only register one + * control instance since it will be used by both channels. + * In error case we continue without controls. + */ + kctrl = snd_ctl_new1(&playback_controls, *port_info); + ret = snd_ctl_add(card, kctrl); + if (ret < 0) + dev_err(dev, + "failed to add playback controls %p port=%d err=%d\n", + kctrl, port, ret); + + kctrl = snd_ctl_new1(&capture_controls, *port_info); + ret = snd_ctl_add(card, kctrl); + if (ret < 0) + dev_err(dev, + "failed to add capture controls %p port=%d err=%d\n", + kctrl, port, ret); + + return 0; +} + +void siu_free_port(struct siu_port *port_info) +{ + kfree(port_info); +} + +static int siu_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct siu_info *info = snd_soc_dai_get_drvdata(dai); + struct snd_pcm_runtime *rt = substream->runtime; + struct siu_port *port_info = siu_port_info(substream); + int ret; + + dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, + info->port_id, port_info); + + snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw); + + ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); + if (unlikely(ret < 0)) + return ret; + + siu_dai_start(port_info); + + return 0; +} + +static void siu_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct siu_info *info = snd_soc_dai_get_drvdata(dai); + struct siu_port *port_info = siu_port_info(substream); + + dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, + info->port_id, port_info); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_info->play_cap &= ~PLAYBACK_ENABLED; + else + port_info->play_cap &= ~CAPTURE_ENABLED; + + /* Stop the siu if the other stream is not using it */ + if (!port_info->play_cap) { + /* during stmread or stmwrite ? */ + if (WARN_ON(port_info->playback.rw_flg || port_info->capture.rw_flg)) + return; + siu_dai_spbstop(port_info); + siu_dai_stop(port_info); + } +} + +/* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */ +static int siu_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct siu_info *info = snd_soc_dai_get_drvdata(dai); + struct snd_pcm_runtime *rt = substream->runtime; + struct siu_port *port_info = siu_port_info(substream); + struct siu_stream *siu_stream; + int self, ret; + + dev_dbg(substream->pcm->card->dev, + "%s: port %d, active streams %lx, %d channels\n", + __func__, info->port_id, port_info->play_cap, rt->channels); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + self = PLAYBACK_ENABLED; + siu_stream = &port_info->playback; + } else { + self = CAPTURE_ENABLED; + siu_stream = &port_info->capture; + } + + /* Set up the siu if not already done */ + if (!port_info->play_cap) { + siu_stream->rw_flg = 0; /* stream-data transfer flag */ + + siu_dai_spbAselect(port_info); + siu_dai_spbBselect(port_info); + + siu_dai_open(siu_stream); + + siu_dai_pcmdatapack(siu_stream); + + ret = siu_dai_spbstart(port_info); + if (ret < 0) + goto fail; + } else { + ret = 0; + } + + port_info->play_cap |= self; + +fail: + return ret; +} + +/* + * SIU can set bus format to I2S / PCM / SPDIF independently for playback and + * capture, however, the current API sets the bus format globally for a DAI. + */ +static int siu_dai_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct siu_info *info = snd_soc_dai_get_drvdata(dai); + u32 __iomem *base = info->reg; + u32 ifctl; + + dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n", + __func__, fmt, info->port_id); + + if (info->port_id < 0) + return -ENODEV; + + /* Here select between I2S / PCM / SPDIF */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ifctl = siu_flags[info->port_id].playback.i2s | + siu_flags[info->port_id].capture.i2s; + break; + case SND_SOC_DAIFMT_LEFT_J: + ifctl = siu_flags[info->port_id].playback.pcm | + siu_flags[info->port_id].capture.pcm; + break; + /* SPDIF disabled - see comment at the top */ + default: + return -EINVAL; + } + + ifctl |= ~(siu_flags[info->port_id].playback.mask | + siu_flags[info->port_id].capture.mask) & + siu_read32(base + SIU_IFCTL); + siu_write32(base + SIU_IFCTL, ifctl); + + return 0; +} + +static int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct clk *siu_clk, *parent_clk; + char *siu_name, *parent_name; + int ret; + + if (dir != SND_SOC_CLOCK_IN) + return -EINVAL; + + dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id); + + switch (clk_id) { + case SIU_CLKA_PLL: + siu_name = "siua_clk"; + parent_name = "pll_clk"; + break; + case SIU_CLKA_EXT: + siu_name = "siua_clk"; + parent_name = "siumcka_clk"; + break; + case SIU_CLKB_PLL: + siu_name = "siub_clk"; + parent_name = "pll_clk"; + break; + case SIU_CLKB_EXT: + siu_name = "siub_clk"; + parent_name = "siumckb_clk"; + break; + default: + return -EINVAL; + } + + siu_clk = clk_get(dai->dev, siu_name); + if (IS_ERR(siu_clk)) { + dev_err(dai->dev, "%s: cannot get a SIU clock: %ld\n", __func__, + PTR_ERR(siu_clk)); + return PTR_ERR(siu_clk); + } + + parent_clk = clk_get(dai->dev, parent_name); + if (IS_ERR(parent_clk)) { + ret = PTR_ERR(parent_clk); + dev_err(dai->dev, "cannot get a SIU clock parent: %d\n", ret); + goto epclkget; + } + + ret = clk_set_parent(siu_clk, parent_clk); + if (ret < 0) { + dev_err(dai->dev, "cannot reparent the SIU clock: %d\n", ret); + goto eclksetp; + } + + ret = clk_set_rate(siu_clk, freq); + if (ret < 0) + dev_err(dai->dev, "cannot set SIU clock rate: %d\n", ret); + + /* TODO: when clkdev gets reference counting we'll move these to siu_dai_shutdown() */ +eclksetp: + clk_put(parent_clk); +epclkget: + clk_put(siu_clk); + + return ret; +} + +static const struct snd_soc_dai_ops siu_dai_ops = { + .startup = siu_dai_startup, + .shutdown = siu_dai_shutdown, + .prepare = siu_dai_prepare, + .set_sysclk = siu_dai_set_sysclk, + .set_fmt = siu_dai_set_fmt, +}; + +static struct snd_soc_dai_driver siu_i2s_dai = { + .name = "siu-i2s-dai", + .playback = { + .channels_min = 2, + .channels_max = 2, + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_8000_48000, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_8000_48000, + }, + .ops = &siu_dai_ops, +}; + +static int siu_probe(struct platform_device *pdev) +{ + const struct firmware *fw_entry; + struct resource *res, *region; + struct siu_info *info; + int ret; + + info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + siu_i2s_data = info; + info->dev = &pdev->dev; + + ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); + if (ret) + return ret; + + /* + * Loaded firmware is "const" - read only, but we have to modify it in + * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect() + */ + memcpy(&info->fw, fw_entry->data, fw_entry->size); + + release_firmware(fw_entry); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + region = devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name); + if (!region) { + dev_err(&pdev->dev, "SIU region already claimed\n"); + return -EBUSY; + } + + info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE); + if (!info->pram) + return -ENOMEM; + info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET, + XRAM_SIZE); + if (!info->xram) + return -ENOMEM; + info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET, + YRAM_SIZE); + if (!info->yram) + return -ENOMEM; + info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET, + resource_size(res) - REG_OFFSET); + if (!info->reg) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, info); + + /* register using ARRAY version so we can keep dai name */ + ret = devm_snd_soc_register_component(&pdev->dev, &siu_component, + &siu_i2s_dai, 1); + if (ret < 0) + return ret; + + pm_runtime_enable(&pdev->dev); + + return 0; +} + +static void siu_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); +} + +static struct platform_driver siu_driver = { + .driver = { + .name = "siu-pcm-audio", + }, + .probe = siu_probe, + .remove = siu_remove, +}; + +module_platform_driver(siu_driver); + +MODULE_AUTHOR("Carlos Munoz "); +MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver"); +MODULE_LICENSE("GPL"); + +MODULE_FIRMWARE("siu_spb.bin"); diff --git a/sound/soc/renesas/siu_pcm.c b/sound/soc/renesas/siu_pcm.c new file mode 100644 index 000000000000..f15ff36e7934 --- /dev/null +++ b/sound/soc/renesas/siu_pcm.c @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral. +// +// Copyright (C) 2009-2010 Guennadi Liakhovetski +// Copyright (C) 2006 Carlos Munoz + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "siu.h" + +#define DRV_NAME "siu-i2s" +#define GET_MAX_PERIODS(buf_bytes, period_bytes) \ + ((buf_bytes) / (period_bytes)) +#define PERIOD_OFFSET(buf_addr, period_num, period_bytes) \ + ((buf_addr) + ((period_num) * (period_bytes))) + +#define RWF_STM_RD 0x01 /* Read in progress */ +#define RWF_STM_WT 0x02 /* Write in progress */ + +struct siu_port *siu_ports[SIU_PORT_NUM]; + +/* transfersize is number of u32 dma transfers per period */ +static int siu_pcm_stmwrite_stop(struct siu_port *port_info) +{ + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + struct siu_stream *siu_stream = &port_info->playback; + u32 stfifo; + + if (!siu_stream->rw_flg) + return -EPERM; + + /* output FIFO disable */ + stfifo = siu_read32(base + SIU_STFIFO); + siu_write32(base + SIU_STFIFO, stfifo & ~0x0c180c18); + pr_debug("%s: STFIFO %x -> %x\n", __func__, + stfifo, stfifo & ~0x0c180c18); + + /* during stmwrite clear */ + siu_stream->rw_flg = 0; + + return 0; +} + +static int siu_pcm_stmwrite_start(struct siu_port *port_info) +{ + struct siu_stream *siu_stream = &port_info->playback; + + if (siu_stream->rw_flg) + return -EPERM; + + /* Current period in buffer */ + port_info->playback.cur_period = 0; + + /* during stmwrite flag set */ + siu_stream->rw_flg = RWF_STM_WT; + + /* DMA transfer start */ + queue_work(system_highpri_wq, &siu_stream->work); + + return 0; +} + +static void siu_dma_tx_complete(void *arg) +{ + struct siu_stream *siu_stream = arg; + + if (!siu_stream->rw_flg) + return; + + /* Update completed period count */ + if (++siu_stream->cur_period >= + GET_MAX_PERIODS(siu_stream->buf_bytes, + siu_stream->period_bytes)) + siu_stream->cur_period = 0; + + pr_debug("%s: done period #%d (%u/%u bytes), cookie %d\n", + __func__, siu_stream->cur_period, + siu_stream->cur_period * siu_stream->period_bytes, + siu_stream->buf_bytes, siu_stream->cookie); + + queue_work(system_highpri_wq, &siu_stream->work); + + /* Notify alsa: a period is done */ + snd_pcm_period_elapsed(siu_stream->substream); +} + +static int siu_pcm_wr_set(struct siu_port *port_info, + dma_addr_t buff, u32 size) +{ + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + struct siu_stream *siu_stream = &port_info->playback; + struct snd_pcm_substream *substream = siu_stream->substream; + struct device *dev = substream->pcm->card->dev; + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + struct scatterlist sg; + u32 stfifo; + + sg_init_table(&sg, 1); + sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)), + size, offset_in_page(buff)); + sg_dma_len(&sg) = size; + sg_dma_address(&sg) = buff; + + desc = dmaengine_prep_slave_sg(siu_stream->chan, + &sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(dev, "Failed to allocate a dma descriptor\n"); + return -ENOMEM; + } + + desc->callback = siu_dma_tx_complete; + desc->callback_param = siu_stream; + cookie = dmaengine_submit(desc); + if (cookie < 0) { + dev_err(dev, "Failed to submit a dma transfer\n"); + return cookie; + } + + siu_stream->tx_desc = desc; + siu_stream->cookie = cookie; + + dma_async_issue_pending(siu_stream->chan); + + /* only output FIFO enable */ + stfifo = siu_read32(base + SIU_STFIFO); + siu_write32(base + SIU_STFIFO, stfifo | (port_info->stfifo & 0x0c180c18)); + dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__, + stfifo, stfifo | (port_info->stfifo & 0x0c180c18)); + + return 0; +} + +static int siu_pcm_rd_set(struct siu_port *port_info, + dma_addr_t buff, size_t size) +{ + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + struct siu_stream *siu_stream = &port_info->capture; + struct snd_pcm_substream *substream = siu_stream->substream; + struct device *dev = substream->pcm->card->dev; + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + struct scatterlist sg; + u32 stfifo; + + dev_dbg(dev, "%s: %u@%llx\n", __func__, size, (unsigned long long)buff); + + sg_init_table(&sg, 1); + sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)), + size, offset_in_page(buff)); + sg_dma_len(&sg) = size; + sg_dma_address(&sg) = buff; + + desc = dmaengine_prep_slave_sg(siu_stream->chan, + &sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(dev, "Failed to allocate dma descriptor\n"); + return -ENOMEM; + } + + desc->callback = siu_dma_tx_complete; + desc->callback_param = siu_stream; + cookie = dmaengine_submit(desc); + if (cookie < 0) { + dev_err(dev, "Failed to submit dma descriptor\n"); + return cookie; + } + + siu_stream->tx_desc = desc; + siu_stream->cookie = cookie; + + dma_async_issue_pending(siu_stream->chan); + + /* only input FIFO enable */ + stfifo = siu_read32(base + SIU_STFIFO); + siu_write32(base + SIU_STFIFO, siu_read32(base + SIU_STFIFO) | + (port_info->stfifo & 0x13071307)); + dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__, + stfifo, stfifo | (port_info->stfifo & 0x13071307)); + + return 0; +} + +static void siu_io_work(struct work_struct *work) +{ + struct siu_stream *siu_stream = container_of(work, struct siu_stream, + work); + struct snd_pcm_substream *substream = siu_stream->substream; + struct device *dev = substream->pcm->card->dev; + struct snd_pcm_runtime *rt = substream->runtime; + struct siu_port *port_info = siu_port_info(substream); + + dev_dbg(dev, "%s: flags %x\n", __func__, siu_stream->rw_flg); + + if (!siu_stream->rw_flg) { + dev_dbg(dev, "%s: stream inactive\n", __func__); + return; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dma_addr_t buff; + size_t count; + + buff = (dma_addr_t)PERIOD_OFFSET(rt->dma_addr, + siu_stream->cur_period, + siu_stream->period_bytes); + count = siu_stream->period_bytes; + + /* DMA transfer start */ + siu_pcm_rd_set(port_info, buff, count); + } else { + siu_pcm_wr_set(port_info, + (dma_addr_t)PERIOD_OFFSET(rt->dma_addr, + siu_stream->cur_period, + siu_stream->period_bytes), + siu_stream->period_bytes); + } +} + +/* Capture */ +static int siu_pcm_stmread_start(struct siu_port *port_info) +{ + struct siu_stream *siu_stream = &port_info->capture; + + if (siu_stream->xfer_cnt > 0x1000000) + return -EINVAL; + if (siu_stream->rw_flg) + return -EPERM; + + /* Current period in buffer */ + siu_stream->cur_period = 0; + + /* during stmread flag set */ + siu_stream->rw_flg = RWF_STM_RD; + + queue_work(system_highpri_wq, &siu_stream->work); + + return 0; +} + +static int siu_pcm_stmread_stop(struct siu_port *port_info) +{ + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + struct siu_stream *siu_stream = &port_info->capture; + struct device *dev = siu_stream->substream->pcm->card->dev; + u32 stfifo; + + if (!siu_stream->rw_flg) + return -EPERM; + + /* input FIFO disable */ + stfifo = siu_read32(base + SIU_STFIFO); + siu_write32(base + SIU_STFIFO, stfifo & ~0x13071307); + dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__, + stfifo, stfifo & ~0x13071307); + + /* during stmread flag clear */ + siu_stream->rw_flg = 0; + + return 0; +} + +static bool filter(struct dma_chan *chan, void *secondary) +{ + struct sh_dmae_slave *param = secondary; + + pr_debug("%s: secondary ID %d\n", __func__, param->shdma_slave.slave_id); + + chan->private = ¶m->shdma_slave; + return true; +} + +static int siu_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *ss) +{ + /* Playback / Capture */ + struct siu_platform *pdata = component->dev->platform_data; + struct siu_info *info = siu_i2s_data; + struct siu_port *port_info = siu_port_info(ss); + struct siu_stream *siu_stream; + u32 port = info->port_id; + struct device *dev = ss->pcm->card->dev; + dma_cap_mask_t mask; + struct sh_dmae_slave *param; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dev_dbg(dev, "%s, port=%d@%p\n", __func__, port, port_info); + + if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) { + siu_stream = &port_info->playback; + param = &siu_stream->param; + param->shdma_slave.slave_id = port ? pdata->dma_slave_tx_b : + pdata->dma_slave_tx_a; + } else { + siu_stream = &port_info->capture; + param = &siu_stream->param; + param->shdma_slave.slave_id = port ? pdata->dma_slave_rx_b : + pdata->dma_slave_rx_a; + } + + /* Get DMA channel */ + siu_stream->chan = dma_request_channel(mask, filter, param); + if (!siu_stream->chan) { + dev_err(dev, "DMA channel allocation failed!\n"); + return -EBUSY; + } + + siu_stream->substream = ss; + + return 0; +} + +static int siu_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *ss) +{ + struct siu_info *info = siu_i2s_data; + struct device *dev = ss->pcm->card->dev; + struct siu_port *port_info = siu_port_info(ss); + struct siu_stream *siu_stream; + + dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id); + + if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) + siu_stream = &port_info->playback; + else + siu_stream = &port_info->capture; + + dma_release_channel(siu_stream->chan); + siu_stream->chan = NULL; + + siu_stream->substream = NULL; + + return 0; +} + +static int siu_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *ss) +{ + struct siu_info *info = siu_i2s_data; + struct siu_port *port_info = siu_port_info(ss); + struct device *dev = ss->pcm->card->dev; + struct snd_pcm_runtime *rt; + struct siu_stream *siu_stream; + snd_pcm_sframes_t xfer_cnt; + + if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) + siu_stream = &port_info->playback; + else + siu_stream = &port_info->capture; + + rt = siu_stream->substream->runtime; + + siu_stream->buf_bytes = snd_pcm_lib_buffer_bytes(ss); + siu_stream->period_bytes = snd_pcm_lib_period_bytes(ss); + + dev_dbg(dev, "%s: port=%d, %d channels, period=%u bytes\n", __func__, + info->port_id, rt->channels, siu_stream->period_bytes); + + /* We only support buffers that are multiples of the period */ + if (siu_stream->buf_bytes % siu_stream->period_bytes) { + dev_err(dev, "%s() - buffer=%d not multiple of period=%d\n", + __func__, siu_stream->buf_bytes, + siu_stream->period_bytes); + return -EINVAL; + } + + xfer_cnt = bytes_to_frames(rt, siu_stream->period_bytes); + if (!xfer_cnt || xfer_cnt > 0x1000000) + return -EINVAL; + + siu_stream->format = rt->format; + siu_stream->xfer_cnt = xfer_cnt; + + dev_dbg(dev, "port=%d buf=%lx buf_bytes=%d period_bytes=%d " + "format=%d channels=%d xfer_cnt=%d\n", info->port_id, + (unsigned long)rt->dma_addr, siu_stream->buf_bytes, + siu_stream->period_bytes, + siu_stream->format, rt->channels, (int)xfer_cnt); + + return 0; +} + +static int siu_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *ss, int cmd) +{ + struct siu_info *info = siu_i2s_data; + struct device *dev = ss->pcm->card->dev; + struct siu_port *port_info = siu_port_info(ss); + int ret; + + dev_dbg(dev, "%s: port=%d@%p, cmd=%d\n", __func__, + info->port_id, port_info, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = siu_pcm_stmwrite_start(port_info); + else + ret = siu_pcm_stmread_start(port_info); + + if (ret < 0) + dev_warn(dev, "%s: start failed on port=%d\n", + __func__, info->port_id); + + break; + case SNDRV_PCM_TRIGGER_STOP: + if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) + siu_pcm_stmwrite_stop(port_info); + else + siu_pcm_stmread_stop(port_info); + ret = 0; + + break; + default: + dev_err(dev, "%s() unsupported cmd=%d\n", __func__, cmd); + ret = -EINVAL; + } + + return ret; +} + +/* + * So far only resolution of one period is supported, subject to extending the + * dmangine API + */ +static snd_pcm_uframes_t +siu_pcm_pointer_dma(struct snd_soc_component *component, + struct snd_pcm_substream *ss) +{ + struct device *dev = ss->pcm->card->dev; + struct siu_info *info = siu_i2s_data; + u32 __iomem *base = info->reg; + struct siu_port *port_info = siu_port_info(ss); + struct snd_pcm_runtime *rt = ss->runtime; + size_t ptr; + struct siu_stream *siu_stream; + + if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) + siu_stream = &port_info->playback; + else + siu_stream = &port_info->capture; + + /* + * ptr is the offset into the buffer where the dma is currently at. We + * check if the dma buffer has just wrapped. + */ + ptr = PERIOD_OFFSET(rt->dma_addr, + siu_stream->cur_period, + siu_stream->period_bytes) - rt->dma_addr; + + dev_dbg(dev, + "%s: port=%d, events %x, FSTS %x, xferred %u/%u, cookie %d\n", + __func__, info->port_id, siu_read32(base + SIU_EVNTC), + siu_read32(base + SIU_SBFSTS), ptr, siu_stream->buf_bytes, + siu_stream->cookie); + + if (ptr >= siu_stream->buf_bytes) + ptr = 0; + + return bytes_to_frames(ss->runtime, ptr); +} + +static int siu_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + /* card->dev == socdev->dev, see snd_soc_new_pcms() */ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + struct siu_info *info = siu_i2s_data; + struct platform_device *pdev = to_platform_device(card->dev); + int ret; + int i; + + /* pdev->id selects between SIUA and SIUB */ + if (pdev->id < 0 || pdev->id >= SIU_PORT_NUM) + return -EINVAL; + + info->port_id = pdev->id; + + /* + * While the siu has 2 ports, only one port can be on at a time (only 1 + * SPB). So far all the boards using the siu had only one of the ports + * wired to a codec. To simplify things, we only register one port with + * alsa. In case both ports are needed, it should be changed here + */ + for (i = pdev->id; i < pdev->id + 1; i++) { + struct siu_port **port_info = &siu_ports[i]; + + ret = siu_init_port(i, port_info, card); + if (ret < 0) + return ret; + + snd_pcm_set_managed_buffer_all(pcm, + SNDRV_DMA_TYPE_DEV, card->dev, + SIU_BUFFER_BYTES_MAX, SIU_BUFFER_BYTES_MAX); + + (*port_info)->pcm = pcm; + + /* IO works */ + INIT_WORK(&(*port_info)->playback.work, siu_io_work); + INIT_WORK(&(*port_info)->capture.work, siu_io_work); + } + + dev_info(card->dev, "SuperH SIU driver initialized.\n"); + return 0; +} + +static void siu_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + struct platform_device *pdev = to_platform_device(pcm->card->dev); + struct siu_port *port_info = siu_ports[pdev->id]; + + cancel_work_sync(&port_info->capture.work); + cancel_work_sync(&port_info->playback.work); + + siu_free_port(port_info); + + dev_dbg(pcm->card->dev, "%s\n", __func__); +} + +const struct snd_soc_component_driver siu_component = { + .name = DRV_NAME, + .open = siu_pcm_open, + .close = siu_pcm_close, + .prepare = siu_pcm_prepare, + .trigger = siu_pcm_trigger, + .pointer = siu_pcm_pointer_dma, + .pcm_construct = siu_pcm_new, + .pcm_destruct = siu_pcm_free, + .legacy_dai_naming = 1, +}; +EXPORT_SYMBOL_GPL(siu_component); diff --git a/sound/soc/renesas/ssi.c b/sound/soc/renesas/ssi.c new file mode 100644 index 000000000000..96cf523c2273 --- /dev/null +++ b/sound/soc/renesas/ssi.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Serial Sound Interface (I2S) support for SH7760/SH7780 +// +// Copyright (c) 2007 Manuel Lauss +// +// dont forget to set IPSEL/OMSEL register bits (in your board code) to +// enable SSI output pins! + +/* + * LIMITATIONS: + * The SSI unit has only one physical data line, so full duplex is + * impossible. This can be remedied on the SH7760 by using the + * other SSI unit for recording; however the SH7780 has only 1 SSI + * unit, and its pins are shared with the AC97 unit, among others. + * + * FEATURES: + * The SSI features "compressed mode": in this mode it continuously + * streams PCM data over the I2S lines and uses LRCK as a handshake + * signal. Can be used to send compressed data (AC3/DTS) to a DSP. + * The number of bits sent over the wire in a frame can be adjusted + * and can be independent from the actual sample bit depth. This is + * useful to support TDM mode codecs like the AD1939 which have a + * fixed TDM slot size, regardless of sample resolution. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SSICR 0x00 +#define SSISR 0x04 + +#define CR_DMAEN (1 << 28) +#define CR_CHNL_SHIFT 22 +#define CR_CHNL_MASK (3 << CR_CHNL_SHIFT) +#define CR_DWL_SHIFT 19 +#define CR_DWL_MASK (7 << CR_DWL_SHIFT) +#define CR_SWL_SHIFT 16 +#define CR_SWL_MASK (7 << CR_SWL_SHIFT) +#define CR_SCK_MASTER (1 << 15) /* bitclock master bit */ +#define CR_SWS_MASTER (1 << 14) /* wordselect master bit */ +#define CR_SCKP (1 << 13) /* I2Sclock polarity */ +#define CR_SWSP (1 << 12) /* LRCK polarity */ +#define CR_SPDP (1 << 11) +#define CR_SDTA (1 << 10) /* i2s alignment (msb/lsb) */ +#define CR_PDTA (1 << 9) /* fifo data alignment */ +#define CR_DEL (1 << 8) /* delay data by 1 i2sclk */ +#define CR_BREN (1 << 7) /* clock gating in burst mode */ +#define CR_CKDIV_SHIFT 4 +#define CR_CKDIV_MASK (7 << CR_CKDIV_SHIFT) /* bitclock divider */ +#define CR_MUTE (1 << 3) /* SSI mute */ +#define CR_CPEN (1 << 2) /* compressed mode */ +#define CR_TRMD (1 << 1) /* transmit/receive select */ +#define CR_EN (1 << 0) /* enable SSI */ + +#define SSIREG(reg) (*(unsigned long *)(ssi->mmio + (reg))) + +struct ssi_priv { + unsigned long mmio; + unsigned long sysclk; + int inuse; +} ssi_cpu_data[] = { +#if defined(CONFIG_CPU_SUBTYPE_SH7760) + { + .mmio = 0xFE680000, + }, + { + .mmio = 0xFE690000, + }, +#elif defined(CONFIG_CPU_SUBTYPE_SH7780) + { + .mmio = 0xFFE70000, + }, +#else +#error "Unsupported SuperH SoC" +#endif +}; + +/* + * track usage of the SSI; it is simplex-only so prevent attempts of + * concurrent playback + capture. FIXME: any locking required? + */ +static int ssi_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; + if (ssi->inuse) { + pr_debug("ssi: already in use!\n"); + return -EBUSY; + } else + ssi->inuse = 1; + return 0; +} + +static void ssi_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; + + ssi->inuse = 0; +} + +static int ssi_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + SSIREG(SSICR) |= CR_DMAEN | CR_EN; + break; + case SNDRV_PCM_TRIGGER_STOP: + SSIREG(SSICR) &= ~(CR_DMAEN | CR_EN); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ssi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; + unsigned long ssicr = SSIREG(SSICR); + unsigned int bits, channels, swl, recv, i; + + channels = params_channels(params); + bits = params->msbits; + recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1; + + pr_debug("ssi_hw_params() enter\nssicr was %08lx\n", ssicr); + pr_debug("bits: %u channels: %u\n", bits, channels); + + ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA | + CR_SWL_MASK); + + /* direction (send/receive) */ + if (!recv) + ssicr |= CR_TRMD; /* transmit */ + + /* channels */ + if ((channels < 2) || (channels > 8) || (channels & 1)) { + pr_debug("ssi: invalid number of channels\n"); + return -EINVAL; + } + ssicr |= ((channels >> 1) - 1) << CR_CHNL_SHIFT; + + /* DATA WORD LENGTH (DWL): databits in audio sample */ + i = 0; + switch (bits) { + case 32: ++i; + case 24: ++i; + case 22: ++i; + case 20: ++i; + case 18: ++i; + case 16: ++i; + ssicr |= i << CR_DWL_SHIFT; + case 8: break; + default: + pr_debug("ssi: invalid sample width\n"); + return -EINVAL; + } + + /* + * SYSTEM WORD LENGTH: size in bits of half a frame over the I2S + * wires. This is usually bits_per_sample x channels/2; i.e. in + * Stereo mode the SWL equals DWL. SWL can be bigger than the + * product of (channels_per_slot x samplebits), e.g. for codecs + * like the AD1939 which only accept 32bit wide TDM slots. For + * "standard" I2S operation we set SWL = chans / 2 * DWL here. + * Waiting for ASoC to get TDM support ;-) + */ + if ((bits > 16) && (bits <= 24)) { + bits = 24; /* these are padded by the SSI */ + /*ssicr |= CR_PDTA;*/ /* cpu/data endianness ? */ + } + i = 0; + swl = (bits * channels) / 2; + switch (swl) { + case 256: ++i; + case 128: ++i; + case 64: ++i; + case 48: ++i; + case 32: ++i; + case 16: ++i; + ssicr |= i << CR_SWL_SHIFT; + case 8: break; + default: + pr_debug("ssi: invalid system word length computed\n"); + return -EINVAL; + } + + SSIREG(SSICR) = ssicr; + + pr_debug("ssi_hw_params() leave\nssicr is now %08lx\n", ssicr); + return 0; +} + +static int ssi_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, + unsigned int freq, int dir) +{ + struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id]; + + ssi->sysclk = freq; + + return 0; +} + +/* + * This divider is used to generate the SSI_SCK (I2S bitclock) from the + * clock at the HAC_BIT_CLK ("oversampling clock") pin. + */ +static int ssi_set_clkdiv(struct snd_soc_dai *dai, int did, int div) +{ + struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; + unsigned long ssicr; + int i; + + i = 0; + ssicr = SSIREG(SSICR) & ~CR_CKDIV_MASK; + switch (div) { + case 16: ++i; + case 8: ++i; + case 4: ++i; + case 2: ++i; + SSIREG(SSICR) = ssicr | (i << CR_CKDIV_SHIFT); + case 1: break; + default: + pr_debug("ssi: invalid sck divider %d\n", div); + return -EINVAL; + } + + return 0; +} + +static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; + unsigned long ssicr = SSIREG(SSICR); + + pr_debug("ssi_set_fmt()\nssicr was 0x%08lx\n", ssicr); + + ssicr &= ~(CR_DEL | CR_PDTA | CR_BREN | CR_SWSP | CR_SCKP | + CR_SWS_MASTER | CR_SCK_MASTER); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_RIGHT_J: + ssicr |= CR_DEL | CR_PDTA; + break; + case SND_SOC_DAIFMT_LEFT_J: + ssicr |= CR_DEL; + break; + default: + pr_debug("ssi: unsupported format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: + break; + case SND_SOC_DAIFMT_GATED: + ssicr |= CR_BREN; + break; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + ssicr |= CR_SCKP; /* sample data at low clkedge */ + break; + case SND_SOC_DAIFMT_NB_IF: + ssicr |= CR_SCKP | CR_SWSP; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + ssicr |= CR_SWSP; /* word select starts low */ + break; + default: + pr_debug("ssi: invalid inversion\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: + break; + case SND_SOC_DAIFMT_BP_FC: + ssicr |= CR_SCK_MASTER; + break; + case SND_SOC_DAIFMT_BC_FP: + ssicr |= CR_SWS_MASTER; + break; + case SND_SOC_DAIFMT_BP_FP: + ssicr |= CR_SWS_MASTER | CR_SCK_MASTER; + break; + default: + pr_debug("ssi: invalid master/secondary configuration\n"); + return -EINVAL; + } + + SSIREG(SSICR) = ssicr; + pr_debug("ssi_set_fmt() leave\nssicr is now 0x%08lx\n", ssicr); + + return 0; +} + +/* the SSI depends on an external clocksource (at HAC_BIT_CLK) even in + * Master mode, so really this is board specific; the SSI can do any + * rate with the right bitclk and divider settings. + */ +#define SSI_RATES \ + SNDRV_PCM_RATE_8000_192000 + +/* the SSI can do 8-32 bit samples, with 8 possible channels */ +#define SSI_FMTS \ + (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) + +static const struct snd_soc_dai_ops ssi_dai_ops = { + .startup = ssi_startup, + .shutdown = ssi_shutdown, + .trigger = ssi_trigger, + .hw_params = ssi_hw_params, + .set_sysclk = ssi_set_sysclk, + .set_clkdiv = ssi_set_clkdiv, + .set_fmt = ssi_set_fmt, +}; + +static struct snd_soc_dai_driver sh4_ssi_dai[] = { +{ + .name = "ssi-dai.0", + .playback = { + .rates = SSI_RATES, + .formats = SSI_FMTS, + .channels_min = 2, + .channels_max = 8, + }, + .capture = { + .rates = SSI_RATES, + .formats = SSI_FMTS, + .channels_min = 2, + .channels_max = 8, + }, + .ops = &ssi_dai_ops, +}, +#ifdef CONFIG_CPU_SUBTYPE_SH7760 +{ + .name = "ssi-dai.1", + .playback = { + .rates = SSI_RATES, + .formats = SSI_FMTS, + .channels_min = 2, + .channels_max = 8, + }, + .capture = { + .rates = SSI_RATES, + .formats = SSI_FMTS, + .channels_min = 2, + .channels_max = 8, + }, + .ops = &ssi_dai_ops, +}, +#endif +}; + +static const struct snd_soc_component_driver sh4_ssi_component = { + .name = "sh4-ssi", + .legacy_dai_naming = 1, +}; + +static int sh4_soc_dai_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, &sh4_ssi_component, + sh4_ssi_dai, + ARRAY_SIZE(sh4_ssi_dai)); +} + +static struct platform_driver sh4_ssi_driver = { + .driver = { + .name = "sh4-ssi-dai", + }, + + .probe = sh4_soc_dai_probe, +}; + +module_platform_driver(sh4_ssi_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver"); +MODULE_AUTHOR("Manuel Lauss "); diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig deleted file mode 100644 index 426632996a0a..000000000000 --- a/sound/soc/sh/Kconfig +++ /dev/null @@ -1,76 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -menu "SoC Audio support for Renesas SoCs" - depends on SUPERH || ARCH_RENESAS || COMPILE_TEST - -config SND_SOC_PCM_SH7760 - tristate "SoC Audio support for Renesas SH7760" - depends on CPU_SUBTYPE_SH7760 && SH_DMABRG - help - Enable this option for SH7760 AC97/I2S audio support. - - -## -## Audio unit modules -## - -config SND_SOC_SH4_HAC - tristate - select AC97_BUS - select SND_SOC_AC97_BUS - -config SND_SOC_SH4_SSI - tristate - -config SND_SOC_SH4_FSI - tristate "SH4 FSI support" - depends on SUPERH || COMMON_CLK - select SND_SIMPLE_CARD - help - This option enables FSI sound support - -config SND_SOC_SH4_SIU - tristate - depends on ARCH_SHMOBILE && HAVE_CLK - depends on DMADEVICES - select DMA_ENGINE - select SH_DMAE - select FW_LOADER - -config SND_SOC_RCAR - tristate "R-Car series SRU/SCU/SSIU/SSI support" - depends on COMMON_CLK - depends on OF - select SND_SIMPLE_CARD_UTILS - select SND_DMAENGINE_PCM - select REGMAP_MMIO - help - This option enables R-Car SRU/SCU/SSIU/SSI sound support - -config SND_SOC_RZ - tristate "RZ/G2L series SSIF-2 support" - depends on ARCH_RZG2L || COMPILE_TEST - help - This option enables RZ/G2L SSIF-2 sound support. - -## -## Boards -## - -config SND_SH7760_AC97 - tristate "SH7760 AC97 sound support" - depends on CPU_SUBTYPE_SH7760 && SND_SOC_PCM_SH7760 - select SND_SOC_SH4_HAC - select SND_SOC_AC97_CODEC - help - This option enables generic sound support for the first - AC97 unit of the SH7760. - -config SND_SIU_MIGOR - tristate "SIU sound support on Migo-R" - depends on SH_MIGOR && I2C - select SND_SOC_SH4_SIU - select SND_SOC_WM8978 - help - This option enables sound support for the SH7722 Migo-R board - -endmenu diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile deleted file mode 100644 index f0e19cbd1581..000000000000 --- a/sound/soc/sh/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -## DMA engines -snd-soc-dma-sh7760-y := dma-sh7760.o -obj-$(CONFIG_SND_SOC_PCM_SH7760) += snd-soc-dma-sh7760.o - -## audio units found on some SH-4 -snd-soc-hac-y := hac.o -snd-soc-ssi-y := ssi.o -snd-soc-fsi-y := fsi.o -snd-soc-siu-y := siu_pcm.o siu_dai.o -obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o -obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o -obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o -obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o - -## audio units for R-Car -obj-$(CONFIG_SND_SOC_RCAR) += rcar/ - -## boards -snd-soc-sh7760-ac97-y := sh7760-ac97.o -snd-soc-migor-y := migor.o - -obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o -obj-$(CONFIG_SND_SIU_MIGOR) += snd-soc-migor.o - -# RZ/G2L -snd-soc-rz-ssi-y := rz-ssi.o -obj-$(CONFIG_SND_SOC_RZ) += snd-soc-rz-ssi.o diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c deleted file mode 100644 index c53539482c20..000000000000 --- a/sound/soc/sh/dma-sh7760.c +++ /dev/null @@ -1,334 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// SH7760 ("camelot") DMABRG audio DMA unit support -// -// Copyright (C) 2007 Manuel Lauss -// -// The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which -// trigger an interrupt when one half of the programmed transfer size -// has been xmitted. -// -// FIXME: little-endian only for now - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* registers and bits */ -#define BRGATXSAR 0x00 -#define BRGARXDAR 0x04 -#define BRGATXTCR 0x08 -#define BRGARXTCR 0x0C -#define BRGACR 0x10 -#define BRGATXTCNT 0x14 -#define BRGARXTCNT 0x18 - -#define ACR_RAR (1 << 18) -#define ACR_RDS (1 << 17) -#define ACR_RDE (1 << 16) -#define ACR_TAR (1 << 2) -#define ACR_TDS (1 << 1) -#define ACR_TDE (1 << 0) - -/* receiver/transmitter data alignment */ -#define ACR_RAM_NONE (0 << 24) -#define ACR_RAM_4BYTE (1 << 24) -#define ACR_RAM_2WORD (2 << 24) -#define ACR_TAM_NONE (0 << 8) -#define ACR_TAM_4BYTE (1 << 8) -#define ACR_TAM_2WORD (2 << 8) - - -struct camelot_pcm { - unsigned long mmio; /* DMABRG audio channel control reg MMIO */ - unsigned int txid; /* ID of first DMABRG IRQ for this unit */ - - struct snd_pcm_substream *tx_ss; - unsigned long tx_period_size; - unsigned int tx_period; - - struct snd_pcm_substream *rx_ss; - unsigned long rx_period_size; - unsigned int rx_period; - -} cam_pcm_data[2] = { - { - .mmio = 0xFE3C0040, - .txid = DMABRGIRQ_A0TXF, - }, - { - .mmio = 0xFE3C0060, - .txid = DMABRGIRQ_A1TXF, - }, -}; - -#define BRGREG(x) (*(unsigned long *)(cam->mmio + (x))) - -/* - * set a minimum of 16kb per period, to avoid interrupt-"storm" and - * resulting skipping. In general, the bigger the minimum size, the - * better for overall system performance. (The SH7760 is a puny CPU - * with a slow SDRAM interface and poor internal bus bandwidth, - * *especially* when the LCDC is active). The minimum for the DMAC - * is 8 bytes; 16kbytes are enough to get skip-free playback of a - * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain - * reasonable responsiveness in MPlayer. - */ -#define DMABRG_PERIOD_MIN 16 * 1024 -#define DMABRG_PERIOD_MAX 0x03fffffc -#define DMABRG_PREALLOC_BUFFER 32 * 1024 -#define DMABRG_PREALLOC_BUFFER_MAX 32 * 1024 - -static const struct snd_pcm_hardware camelot_pcm_hardware = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH), - .buffer_bytes_max = DMABRG_PERIOD_MAX, - .period_bytes_min = DMABRG_PERIOD_MIN, - .period_bytes_max = DMABRG_PERIOD_MAX / 2, - .periods_min = 2, - .periods_max = 2, - .fifo_size = 128, -}; - -static void camelot_txdma(void *data) -{ - struct camelot_pcm *cam = data; - cam->tx_period ^= 1; - snd_pcm_period_elapsed(cam->tx_ss); -} - -static void camelot_rxdma(void *data) -{ - struct camelot_pcm *cam = data; - cam->rx_period ^= 1; - snd_pcm_period_elapsed(cam->rx_ss); -} - -static int camelot_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; - int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; - int ret, dmairq; - - snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware); - - /* DMABRG buffer half/full events */ - dmairq = (recv) ? cam->txid + 2 : cam->txid; - if (recv) { - cam->rx_ss = substream; - ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam); - if (unlikely(ret)) { - pr_debug("audio unit %d irqs already taken!\n", - snd_soc_rtd_to_cpu(rtd, 0)->id); - return -EBUSY; - } - (void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam); - } else { - cam->tx_ss = substream; - ret = dmabrg_request_irq(dmairq, camelot_txdma, cam); - if (unlikely(ret)) { - pr_debug("audio unit %d irqs already taken!\n", - snd_soc_rtd_to_cpu(rtd, 0)->id); - return -EBUSY; - } - (void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam); - } - return 0; -} - -static int camelot_pcm_close(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; - int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; - int dmairq; - - dmairq = (recv) ? cam->txid + 2 : cam->txid; - - if (recv) - cam->rx_ss = NULL; - else - cam->tx_ss = NULL; - - dmabrg_free_irq(dmairq + 1); - dmabrg_free_irq(dmairq); - - return 0; -} - -static int camelot_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; - int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; - - if (recv) { - cam->rx_period_size = params_period_bytes(hw_params); - cam->rx_period = 0; - } else { - cam->tx_period_size = params_period_bytes(hw_params); - cam->tx_period = 0; - } - return 0; -} - -static int camelot_prepare(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; - - pr_debug("PCM data: addr %pad len %zu\n", &runtime->dma_addr, - runtime->dma_bytes); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area; - BRGREG(BRGATXTCR) = runtime->dma_bytes; - } else { - BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area; - BRGREG(BRGARXTCR) = runtime->dma_bytes; - } - - return 0; -} - -static inline void dmabrg_play_dma_start(struct camelot_pcm *cam) -{ - unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); - /* start DMABRG engine: XFER start, auto-addr-reload */ - BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD; -} - -static inline void dmabrg_play_dma_stop(struct camelot_pcm *cam) -{ - unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); - /* forcibly terminate data transmission */ - BRGREG(BRGACR) = acr | ACR_TDS; -} - -static inline void dmabrg_rec_dma_start(struct camelot_pcm *cam) -{ - unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); - /* start DMABRG engine: recv start, auto-reload */ - BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD; -} - -static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam) -{ - unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); - /* forcibly terminate data receiver */ - BRGREG(BRGACR) = acr | ACR_RDS; -} - -static int camelot_trigger(struct snd_soc_component *component, - struct snd_pcm_substream *substream, int cmd) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; - int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (recv) - dmabrg_rec_dma_start(cam); - else - dmabrg_play_dma_start(cam); - break; - case SNDRV_PCM_TRIGGER_STOP: - if (recv) - dmabrg_rec_dma_stop(cam); - else - dmabrg_play_dma_stop(cam); - break; - default: - return -EINVAL; - } - - return 0; -} - -static snd_pcm_uframes_t camelot_pos(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct camelot_pcm *cam = &cam_pcm_data[snd_soc_rtd_to_cpu(rtd, 0)->id]; - int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; - unsigned long pos; - - /* cannot use the DMABRG pointer register: under load, by the - * time ALSA comes around to read the register, it is already - * far ahead (or worse, already done with the fragment) of the - * position at the time the IRQ was triggered, which results in - * fast-playback sound in my test application (ScummVM) - */ - if (recv) - pos = cam->rx_period ? cam->rx_period_size : 0; - else - pos = cam->tx_period ? cam->tx_period_size : 0; - - return bytes_to_frames(runtime, pos); -} - -static int camelot_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) -{ - struct snd_pcm *pcm = rtd->pcm; - - /* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel - * in MMAP mode (i.e. aplay -M) - */ - snd_pcm_set_managed_buffer_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - NULL, - DMABRG_PREALLOC_BUFFER, DMABRG_PREALLOC_BUFFER_MAX); - - return 0; -} - -static const struct snd_soc_component_driver sh7760_soc_component = { - .open = camelot_pcm_open, - .close = camelot_pcm_close, - .hw_params = camelot_hw_params, - .prepare = camelot_prepare, - .trigger = camelot_trigger, - .pointer = camelot_pos, - .pcm_construct = camelot_pcm_new, -}; - -static int sh7760_soc_platform_probe(struct platform_device *pdev) -{ - return devm_snd_soc_register_component(&pdev->dev, &sh7760_soc_component, - NULL, 0); -} - -static struct platform_driver sh7760_pcm_driver = { - .driver = { - .name = "sh7760-pcm-audio", - }, - - .probe = sh7760_soc_platform_probe, -}; - -module_platform_driver(sh7760_pcm_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver"); -MODULE_AUTHOR("Manuel Lauss "); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c deleted file mode 100644 index 221ce91f1950..000000000000 --- a/sound/soc/sh/fsi.c +++ /dev/null @@ -1,2119 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Fifo-attached Serial Interface (FSI) support for SH7724 -// -// Copyright (C) 2009 Renesas Solutions Corp. -// Kuninori Morimoto -// -// Based on ssi.c -// Copyright (c) 2007 Manuel Lauss - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* PortA/PortB register */ -#define REG_DO_FMT 0x0000 -#define REG_DOFF_CTL 0x0004 -#define REG_DOFF_ST 0x0008 -#define REG_DI_FMT 0x000C -#define REG_DIFF_CTL 0x0010 -#define REG_DIFF_ST 0x0014 -#define REG_CKG1 0x0018 -#define REG_CKG2 0x001C -#define REG_DIDT 0x0020 -#define REG_DODT 0x0024 -#define REG_MUTE_ST 0x0028 -#define REG_OUT_DMAC 0x002C -#define REG_OUT_SEL 0x0030 -#define REG_IN_DMAC 0x0038 - -/* master register */ -#define MST_CLK_RST 0x0210 -#define MST_SOFT_RST 0x0214 -#define MST_FIFO_SZ 0x0218 - -/* core register (depend on FSI version) */ -#define A_MST_CTLR 0x0180 -#define B_MST_CTLR 0x01A0 -#define CPU_INT_ST 0x01F4 -#define CPU_IEMSK 0x01F8 -#define CPU_IMSK 0x01FC -#define INT_ST 0x0200 -#define IEMSK 0x0204 -#define IMSK 0x0208 - -/* DO_FMT */ -/* DI_FMT */ -#define CR_BWS_MASK (0x3 << 20) /* FSI2 */ -#define CR_BWS_24 (0x0 << 20) /* FSI2 */ -#define CR_BWS_16 (0x1 << 20) /* FSI2 */ -#define CR_BWS_20 (0x2 << 20) /* FSI2 */ - -#define CR_DTMD_PCM (0x0 << 8) /* FSI2 */ -#define CR_DTMD_SPDIF_PCM (0x1 << 8) /* FSI2 */ -#define CR_DTMD_SPDIF_STREAM (0x2 << 8) /* FSI2 */ - -#define CR_MONO (0x0 << 4) -#define CR_MONO_D (0x1 << 4) -#define CR_PCM (0x2 << 4) -#define CR_I2S (0x3 << 4) -#define CR_TDM (0x4 << 4) -#define CR_TDM_D (0x5 << 4) - -/* OUT_DMAC */ -/* IN_DMAC */ -#define VDMD_MASK (0x3 << 4) -#define VDMD_FRONT (0x0 << 4) /* Package in front */ -#define VDMD_BACK (0x1 << 4) /* Package in back */ -#define VDMD_STREAM (0x2 << 4) /* Stream mode(16bit * 2) */ - -#define DMA_ON (0x1 << 0) - -/* DOFF_CTL */ -/* DIFF_CTL */ -#define IRQ_HALF 0x00100000 -#define FIFO_CLR 0x00000001 - -/* DOFF_ST */ -#define ERR_OVER 0x00000010 -#define ERR_UNDER 0x00000001 -#define ST_ERR (ERR_OVER | ERR_UNDER) - -/* CKG1 */ -#define ACKMD_MASK 0x00007000 -#define BPFMD_MASK 0x00000700 -#define DIMD (1 << 4) -#define DOMD (1 << 0) - -/* A/B MST_CTLR */ -#define BP (1 << 4) /* Fix the signal of Biphase output */ -#define SE (1 << 0) /* Fix the master clock */ - -/* CLK_RST */ -#define CRB (1 << 4) -#define CRA (1 << 0) - -/* IO SHIFT / MACRO */ -#define BI_SHIFT 12 -#define BO_SHIFT 8 -#define AI_SHIFT 4 -#define AO_SHIFT 0 -#define AB_IO(param, shift) (param << shift) - -/* SOFT_RST */ -#define PBSR (1 << 12) /* Port B Software Reset */ -#define PASR (1 << 8) /* Port A Software Reset */ -#define IR (1 << 4) /* Interrupt Reset */ -#define FSISR (1 << 0) /* Software Reset */ - -/* OUT_SEL (FSI2) */ -#define DMMD (1 << 4) /* SPDIF output timing 0: Biphase only */ - /* 1: Biphase and serial */ - -/* FIFO_SZ */ -#define FIFO_SZ_MASK 0x7 - -#define FSI_RATES SNDRV_PCM_RATE_8000_96000 - -#define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) - -/* - * bus options - * - * 0x000000BA - * - * A : sample widtht 16bit setting - * B : sample widtht 24bit setting - */ - -#define SHIFT_16DATA 0 -#define SHIFT_24DATA 4 - -#define PACKAGE_24BITBUS_BACK 0 -#define PACKAGE_24BITBUS_FRONT 1 -#define PACKAGE_16BITBUS_STREAM 2 - -#define BUSOP_SET(s, a) ((a) << SHIFT_ ## s ## DATA) -#define BUSOP_GET(s, a) (((a) >> SHIFT_ ## s ## DATA) & 0xF) - -/* - * FSI driver use below type name for variable - * - * xxx_num : number of data - * xxx_pos : position of data - * xxx_capa : capacity of data - */ - -/* - * period/frame/sample image - * - * ex) PCM (2ch) - * - * period pos period pos - * [n] [n + 1] - * |<-------------------- period--------------------->| - * ==|============================================ ... =|== - * | | - * ||<----- frame ----->|<------ frame ----->| ... | - * |+--------------------+--------------------+- ... | - * ||[ sample ][ sample ]|[ sample ][ sample ]| ... | - * |+--------------------+--------------------+- ... | - * ==|============================================ ... =|== - */ - -/* - * FSI FIFO image - * - * | | - * | | - * | [ sample ] | - * | [ sample ] | - * | [ sample ] | - * | [ sample ] | - * --> go to codecs - */ - -/* - * FSI clock - * - * FSIxCLK [CPG] (ick) -------> | - * |-> FSI_DIV (div)-> FSI2 - * FSIxCK [external] (xck) ---> | - */ - -/* - * struct - */ - -struct fsi_stream_handler; -struct fsi_stream { - - /* - * these are initialized by fsi_stream_init() - */ - struct snd_pcm_substream *substream; - int fifo_sample_capa; /* sample capacity of FSI FIFO */ - int buff_sample_capa; /* sample capacity of ALSA buffer */ - int buff_sample_pos; /* sample position of ALSA buffer */ - int period_samples; /* sample number / 1 period */ - int period_pos; /* current period position */ - int sample_width; /* sample width */ - int uerr_num; - int oerr_num; - - /* - * bus options - */ - u32 bus_option; - - /* - * these are initialized by fsi_handler_init() - */ - struct fsi_stream_handler *handler; - struct fsi_priv *priv; - - /* - * these are for DMAEngine - */ - struct dma_chan *chan; - int dma_id; -}; - -struct fsi_clk { - /* see [FSI clock] */ - struct clk *own; - struct clk *xck; - struct clk *ick; - struct clk *div; - int (*set_rate)(struct device *dev, - struct fsi_priv *fsi); - - unsigned long rate; - unsigned int count; -}; - -struct fsi_priv { - void __iomem *base; - phys_addr_t phys; - struct fsi_master *master; - - struct fsi_stream playback; - struct fsi_stream capture; - - struct fsi_clk clock; - - u32 fmt; - - int chan_num:16; - unsigned int clk_master:1; - unsigned int clk_cpg:1; - unsigned int spdif:1; - unsigned int enable_stream:1; - unsigned int bit_clk_inv:1; - unsigned int lr_clk_inv:1; -}; - -struct fsi_stream_handler { - int (*init)(struct fsi_priv *fsi, struct fsi_stream *io); - int (*quit)(struct fsi_priv *fsi, struct fsi_stream *io); - int (*probe)(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev); - int (*transfer)(struct fsi_priv *fsi, struct fsi_stream *io); - int (*remove)(struct fsi_priv *fsi, struct fsi_stream *io); - int (*start_stop)(struct fsi_priv *fsi, struct fsi_stream *io, - int enable); -}; -#define fsi_stream_handler_call(io, func, args...) \ - (!(io) ? -ENODEV : \ - !((io)->handler->func) ? 0 : \ - (io)->handler->func(args)) - -struct fsi_core { - int ver; - - u32 int_st; - u32 iemsk; - u32 imsk; - u32 a_mclk; - u32 b_mclk; -}; - -struct fsi_master { - void __iomem *base; - struct fsi_priv fsia; - struct fsi_priv fsib; - const struct fsi_core *core; - spinlock_t lock; -}; - -static inline int fsi_stream_is_play(struct fsi_priv *fsi, - struct fsi_stream *io) -{ - return &fsi->playback == io; -} - - -/* - * basic read write function - */ - -static void __fsi_reg_write(u32 __iomem *reg, u32 data) -{ - /* valid data area is 24bit */ - data &= 0x00ffffff; - - __raw_writel(data, reg); -} - -static u32 __fsi_reg_read(u32 __iomem *reg) -{ - return __raw_readl(reg); -} - -static void __fsi_reg_mask_set(u32 __iomem *reg, u32 mask, u32 data) -{ - u32 val = __fsi_reg_read(reg); - - val &= ~mask; - val |= data & mask; - - __fsi_reg_write(reg, val); -} - -#define fsi_reg_write(p, r, d)\ - __fsi_reg_write((p->base + REG_##r), d) - -#define fsi_reg_read(p, r)\ - __fsi_reg_read((p->base + REG_##r)) - -#define fsi_reg_mask_set(p, r, m, d)\ - __fsi_reg_mask_set((p->base + REG_##r), m, d) - -#define fsi_master_read(p, r) _fsi_master_read(p, MST_##r) -#define fsi_core_read(p, r) _fsi_master_read(p, p->core->r) -static u32 _fsi_master_read(struct fsi_master *master, u32 reg) -{ - u32 ret; - unsigned long flags; - - spin_lock_irqsave(&master->lock, flags); - ret = __fsi_reg_read(master->base + reg); - spin_unlock_irqrestore(&master->lock, flags); - - return ret; -} - -#define fsi_master_mask_set(p, r, m, d) _fsi_master_mask_set(p, MST_##r, m, d) -#define fsi_core_mask_set(p, r, m, d) _fsi_master_mask_set(p, p->core->r, m, d) -static void _fsi_master_mask_set(struct fsi_master *master, - u32 reg, u32 mask, u32 data) -{ - unsigned long flags; - - spin_lock_irqsave(&master->lock, flags); - __fsi_reg_mask_set(master->base + reg, mask, data); - spin_unlock_irqrestore(&master->lock, flags); -} - -/* - * basic function - */ -static int fsi_version(struct fsi_master *master) -{ - return master->core->ver; -} - -static struct fsi_master *fsi_get_master(struct fsi_priv *fsi) -{ - return fsi->master; -} - -static int fsi_is_clk_master(struct fsi_priv *fsi) -{ - return fsi->clk_master; -} - -static int fsi_is_port_a(struct fsi_priv *fsi) -{ - return fsi->master->base == fsi->base; -} - -static int fsi_is_spdif(struct fsi_priv *fsi) -{ - return fsi->spdif; -} - -static int fsi_is_enable_stream(struct fsi_priv *fsi) -{ - return fsi->enable_stream; -} - -static int fsi_is_play(struct snd_pcm_substream *substream) -{ - return substream->stream == SNDRV_PCM_STREAM_PLAYBACK; -} - -static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - - return snd_soc_rtd_to_cpu(rtd, 0); -} - -static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai) -{ - struct fsi_master *master = snd_soc_dai_get_drvdata(dai); - - if (dai->id == 0) - return &master->fsia; - else - return &master->fsib; -} - -static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) -{ - return fsi_get_priv_frm_dai(fsi_get_dai(substream)); -} - -static u32 fsi_get_port_shift(struct fsi_priv *fsi, struct fsi_stream *io) -{ - int is_play = fsi_stream_is_play(fsi, io); - int is_porta = fsi_is_port_a(fsi); - u32 shift; - - if (is_porta) - shift = is_play ? AO_SHIFT : AI_SHIFT; - else - shift = is_play ? BO_SHIFT : BI_SHIFT; - - return shift; -} - -static int fsi_frame2sample(struct fsi_priv *fsi, int frames) -{ - return frames * fsi->chan_num; -} - -static int fsi_sample2frame(struct fsi_priv *fsi, int samples) -{ - return samples / fsi->chan_num; -} - -static int fsi_get_current_fifo_samples(struct fsi_priv *fsi, - struct fsi_stream *io) -{ - int is_play = fsi_stream_is_play(fsi, io); - u32 status; - int frames; - - status = is_play ? - fsi_reg_read(fsi, DOFF_ST) : - fsi_reg_read(fsi, DIFF_ST); - - frames = 0x1ff & (status >> 8); - - return fsi_frame2sample(fsi, frames); -} - -static void fsi_count_fifo_err(struct fsi_priv *fsi) -{ - u32 ostatus = fsi_reg_read(fsi, DOFF_ST); - u32 istatus = fsi_reg_read(fsi, DIFF_ST); - - if (ostatus & ERR_OVER) - fsi->playback.oerr_num++; - - if (ostatus & ERR_UNDER) - fsi->playback.uerr_num++; - - if (istatus & ERR_OVER) - fsi->capture.oerr_num++; - - if (istatus & ERR_UNDER) - fsi->capture.uerr_num++; - - fsi_reg_write(fsi, DOFF_ST, 0); - fsi_reg_write(fsi, DIFF_ST, 0); -} - -/* - * fsi_stream_xx() function - */ -static inline struct fsi_stream *fsi_stream_get(struct fsi_priv *fsi, - struct snd_pcm_substream *substream) -{ - return fsi_is_play(substream) ? &fsi->playback : &fsi->capture; -} - -static int fsi_stream_is_working(struct fsi_priv *fsi, - struct fsi_stream *io) -{ - struct fsi_master *master = fsi_get_master(fsi); - unsigned long flags; - int ret; - - spin_lock_irqsave(&master->lock, flags); - ret = !!(io->substream && io->substream->runtime); - spin_unlock_irqrestore(&master->lock, flags); - - return ret; -} - -static struct fsi_priv *fsi_stream_to_priv(struct fsi_stream *io) -{ - return io->priv; -} - -static void fsi_stream_init(struct fsi_priv *fsi, - struct fsi_stream *io, - struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct fsi_master *master = fsi_get_master(fsi); - unsigned long flags; - - spin_lock_irqsave(&master->lock, flags); - io->substream = substream; - io->buff_sample_capa = fsi_frame2sample(fsi, runtime->buffer_size); - io->buff_sample_pos = 0; - io->period_samples = fsi_frame2sample(fsi, runtime->period_size); - io->period_pos = 0; - io->sample_width = samples_to_bytes(runtime, 1); - io->bus_option = 0; - io->oerr_num = -1; /* ignore 1st err */ - io->uerr_num = -1; /* ignore 1st err */ - fsi_stream_handler_call(io, init, fsi, io); - spin_unlock_irqrestore(&master->lock, flags); -} - -static void fsi_stream_quit(struct fsi_priv *fsi, struct fsi_stream *io) -{ - struct snd_soc_dai *dai = fsi_get_dai(io->substream); - struct fsi_master *master = fsi_get_master(fsi); - unsigned long flags; - - spin_lock_irqsave(&master->lock, flags); - - if (io->oerr_num > 0) - dev_err(dai->dev, "over_run = %d\n", io->oerr_num); - - if (io->uerr_num > 0) - dev_err(dai->dev, "under_run = %d\n", io->uerr_num); - - fsi_stream_handler_call(io, quit, fsi, io); - io->substream = NULL; - io->buff_sample_capa = 0; - io->buff_sample_pos = 0; - io->period_samples = 0; - io->period_pos = 0; - io->sample_width = 0; - io->bus_option = 0; - io->oerr_num = 0; - io->uerr_num = 0; - spin_unlock_irqrestore(&master->lock, flags); -} - -static int fsi_stream_transfer(struct fsi_stream *io) -{ - struct fsi_priv *fsi = fsi_stream_to_priv(io); - if (!fsi) - return -EIO; - - return fsi_stream_handler_call(io, transfer, fsi, io); -} - -#define fsi_stream_start(fsi, io)\ - fsi_stream_handler_call(io, start_stop, fsi, io, 1) - -#define fsi_stream_stop(fsi, io)\ - fsi_stream_handler_call(io, start_stop, fsi, io, 0) - -static int fsi_stream_probe(struct fsi_priv *fsi, struct device *dev) -{ - struct fsi_stream *io; - int ret1, ret2; - - io = &fsi->playback; - ret1 = fsi_stream_handler_call(io, probe, fsi, io, dev); - - io = &fsi->capture; - ret2 = fsi_stream_handler_call(io, probe, fsi, io, dev); - - if (ret1 < 0) - return ret1; - if (ret2 < 0) - return ret2; - - return 0; -} - -static int fsi_stream_remove(struct fsi_priv *fsi) -{ - struct fsi_stream *io; - int ret1, ret2; - - io = &fsi->playback; - ret1 = fsi_stream_handler_call(io, remove, fsi, io); - - io = &fsi->capture; - ret2 = fsi_stream_handler_call(io, remove, fsi, io); - - if (ret1 < 0) - return ret1; - if (ret2 < 0) - return ret2; - - return 0; -} - -/* - * format/bus/dma setting - */ -static void fsi_format_bus_setup(struct fsi_priv *fsi, struct fsi_stream *io, - u32 bus, struct device *dev) -{ - struct fsi_master *master = fsi_get_master(fsi); - int is_play = fsi_stream_is_play(fsi, io); - u32 fmt = fsi->fmt; - - if (fsi_version(master) >= 2) { - u32 dma = 0; - - /* - * FSI2 needs DMA/Bus setting - */ - switch (bus) { - case PACKAGE_24BITBUS_FRONT: - fmt |= CR_BWS_24; - dma |= VDMD_FRONT; - dev_dbg(dev, "24bit bus / package in front\n"); - break; - case PACKAGE_16BITBUS_STREAM: - fmt |= CR_BWS_16; - dma |= VDMD_STREAM; - dev_dbg(dev, "16bit bus / stream mode\n"); - break; - case PACKAGE_24BITBUS_BACK: - default: - fmt |= CR_BWS_24; - dma |= VDMD_BACK; - dev_dbg(dev, "24bit bus / package in back\n"); - break; - } - - if (is_play) - fsi_reg_write(fsi, OUT_DMAC, dma); - else - fsi_reg_write(fsi, IN_DMAC, dma); - } - - if (is_play) - fsi_reg_write(fsi, DO_FMT, fmt); - else - fsi_reg_write(fsi, DI_FMT, fmt); -} - -/* - * irq function - */ - -static void fsi_irq_enable(struct fsi_priv *fsi, struct fsi_stream *io) -{ - u32 data = AB_IO(1, fsi_get_port_shift(fsi, io)); - struct fsi_master *master = fsi_get_master(fsi); - - fsi_core_mask_set(master, imsk, data, data); - fsi_core_mask_set(master, iemsk, data, data); -} - -static void fsi_irq_disable(struct fsi_priv *fsi, struct fsi_stream *io) -{ - u32 data = AB_IO(1, fsi_get_port_shift(fsi, io)); - struct fsi_master *master = fsi_get_master(fsi); - - fsi_core_mask_set(master, imsk, data, 0); - fsi_core_mask_set(master, iemsk, data, 0); -} - -static u32 fsi_irq_get_status(struct fsi_master *master) -{ - return fsi_core_read(master, int_st); -} - -static void fsi_irq_clear_status(struct fsi_priv *fsi) -{ - u32 data = 0; - struct fsi_master *master = fsi_get_master(fsi); - - data |= AB_IO(1, fsi_get_port_shift(fsi, &fsi->playback)); - data |= AB_IO(1, fsi_get_port_shift(fsi, &fsi->capture)); - - /* clear interrupt factor */ - fsi_core_mask_set(master, int_st, data, 0); -} - -/* - * SPDIF master clock function - * - * These functions are used later FSI2 - */ -static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) -{ - struct fsi_master *master = fsi_get_master(fsi); - u32 mask, val; - - mask = BP | SE; - val = enable ? mask : 0; - - fsi_is_port_a(fsi) ? - fsi_core_mask_set(master, a_mclk, mask, val) : - fsi_core_mask_set(master, b_mclk, mask, val); -} - -/* - * clock function - */ -static int fsi_clk_init(struct device *dev, - struct fsi_priv *fsi, - int xck, - int ick, - int div, - int (*set_rate)(struct device *dev, - struct fsi_priv *fsi)) -{ - struct fsi_clk *clock = &fsi->clock; - int is_porta = fsi_is_port_a(fsi); - - clock->xck = NULL; - clock->ick = NULL; - clock->div = NULL; - clock->rate = 0; - clock->count = 0; - clock->set_rate = set_rate; - - clock->own = devm_clk_get(dev, NULL); - if (IS_ERR(clock->own)) - return -EINVAL; - - /* external clock */ - if (xck) { - clock->xck = devm_clk_get(dev, is_porta ? "xcka" : "xckb"); - if (IS_ERR(clock->xck)) { - dev_err(dev, "can't get xck clock\n"); - return -EINVAL; - } - if (clock->xck == clock->own) { - dev_err(dev, "cpu doesn't support xck clock\n"); - return -EINVAL; - } - } - - /* FSIACLK/FSIBCLK */ - if (ick) { - clock->ick = devm_clk_get(dev, is_porta ? "icka" : "ickb"); - if (IS_ERR(clock->ick)) { - dev_err(dev, "can't get ick clock\n"); - return -EINVAL; - } - if (clock->ick == clock->own) { - dev_err(dev, "cpu doesn't support ick clock\n"); - return -EINVAL; - } - } - - /* FSI-DIV */ - if (div) { - clock->div = devm_clk_get(dev, is_porta ? "diva" : "divb"); - if (IS_ERR(clock->div)) { - dev_err(dev, "can't get div clock\n"); - return -EINVAL; - } - if (clock->div == clock->own) { - dev_err(dev, "cpu doesn't support div clock\n"); - return -EINVAL; - } - } - - return 0; -} - -#define fsi_clk_invalid(fsi) fsi_clk_valid(fsi, 0) -static void fsi_clk_valid(struct fsi_priv *fsi, unsigned long rate) -{ - fsi->clock.rate = rate; -} - -static int fsi_clk_is_valid(struct fsi_priv *fsi) -{ - return fsi->clock.set_rate && - fsi->clock.rate; -} - -static int fsi_clk_enable(struct device *dev, - struct fsi_priv *fsi) -{ - struct fsi_clk *clock = &fsi->clock; - int ret = -EINVAL; - - if (!fsi_clk_is_valid(fsi)) - return ret; - - if (0 == clock->count) { - ret = clock->set_rate(dev, fsi); - if (ret < 0) { - fsi_clk_invalid(fsi); - return ret; - } - - ret = clk_enable(clock->xck); - if (ret) - goto err; - ret = clk_enable(clock->ick); - if (ret) - goto disable_xck; - ret = clk_enable(clock->div); - if (ret) - goto disable_ick; - - clock->count++; - } - - return ret; - -disable_ick: - clk_disable(clock->ick); -disable_xck: - clk_disable(clock->xck); -err: - return ret; -} - -static int fsi_clk_disable(struct device *dev, - struct fsi_priv *fsi) -{ - struct fsi_clk *clock = &fsi->clock; - - if (!fsi_clk_is_valid(fsi)) - return -EINVAL; - - if (1 == clock->count--) { - clk_disable(clock->xck); - clk_disable(clock->ick); - clk_disable(clock->div); - } - - return 0; -} - -static int fsi_clk_set_ackbpf(struct device *dev, - struct fsi_priv *fsi, - int ackmd, int bpfmd) -{ - u32 data = 0; - - /* check ackmd/bpfmd relationship */ - if (bpfmd > ackmd) { - dev_err(dev, "unsupported rate (%d/%d)\n", ackmd, bpfmd); - return -EINVAL; - } - - /* ACKMD */ - switch (ackmd) { - case 512: - data |= (0x0 << 12); - break; - case 256: - data |= (0x1 << 12); - break; - case 128: - data |= (0x2 << 12); - break; - case 64: - data |= (0x3 << 12); - break; - case 32: - data |= (0x4 << 12); - break; - default: - dev_err(dev, "unsupported ackmd (%d)\n", ackmd); - return -EINVAL; - } - - /* BPFMD */ - switch (bpfmd) { - case 32: - data |= (0x0 << 8); - break; - case 64: - data |= (0x1 << 8); - break; - case 128: - data |= (0x2 << 8); - break; - case 256: - data |= (0x3 << 8); - break; - case 512: - data |= (0x4 << 8); - break; - case 16: - data |= (0x7 << 8); - break; - default: - dev_err(dev, "unsupported bpfmd (%d)\n", bpfmd); - return -EINVAL; - } - - dev_dbg(dev, "ACKMD/BPFMD = %d/%d\n", ackmd, bpfmd); - - fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); - udelay(10); - - return 0; -} - -static int fsi_clk_set_rate_external(struct device *dev, - struct fsi_priv *fsi) -{ - struct clk *xck = fsi->clock.xck; - struct clk *ick = fsi->clock.ick; - unsigned long rate = fsi->clock.rate; - unsigned long xrate; - int ackmd, bpfmd; - int ret = 0; - - /* check clock rate */ - xrate = clk_get_rate(xck); - if (xrate % rate) { - dev_err(dev, "unsupported clock rate\n"); - return -EINVAL; - } - - clk_set_parent(ick, xck); - clk_set_rate(ick, xrate); - - bpfmd = fsi->chan_num * 32; - ackmd = xrate / rate; - - dev_dbg(dev, "external/rate = %ld/%ld\n", xrate, rate); - - ret = fsi_clk_set_ackbpf(dev, fsi, ackmd, bpfmd); - if (ret < 0) - dev_err(dev, "%s failed", __func__); - - return ret; -} - -static int fsi_clk_set_rate_cpg(struct device *dev, - struct fsi_priv *fsi) -{ - struct clk *ick = fsi->clock.ick; - struct clk *div = fsi->clock.div; - unsigned long rate = fsi->clock.rate; - unsigned long target = 0; /* 12288000 or 11289600 */ - unsigned long actual, cout; - unsigned long diff, min; - unsigned long best_cout, best_act; - int adj; - int ackmd, bpfmd; - int ret = -EINVAL; - - if (!(12288000 % rate)) - target = 12288000; - if (!(11289600 % rate)) - target = 11289600; - if (!target) { - dev_err(dev, "unsupported rate\n"); - return ret; - } - - bpfmd = fsi->chan_num * 32; - ackmd = target / rate; - ret = fsi_clk_set_ackbpf(dev, fsi, ackmd, bpfmd); - if (ret < 0) { - dev_err(dev, "%s failed", __func__); - return ret; - } - - /* - * The clock flow is - * - * [CPG] = cout => [FSI_DIV] = audio => [FSI] => [codec] - * - * But, it needs to find best match of CPG and FSI_DIV - * combination, since it is difficult to generate correct - * frequency of audio clock from ick clock only. - * Because ick is created from its parent clock. - * - * target = rate x [512/256/128/64]fs - * cout = round(target x adjustment) - * actual = cout / adjustment (by FSI-DIV) ~= target - * audio = actual - */ - min = ~0; - best_cout = 0; - best_act = 0; - for (adj = 1; adj < 0xffff; adj++) { - - cout = target * adj; - if (cout > 100000000) /* max clock = 100MHz */ - break; - - /* cout/actual audio clock */ - cout = clk_round_rate(ick, cout); - actual = cout / adj; - - /* find best frequency */ - diff = abs(actual - target); - if (diff < min) { - min = diff; - best_cout = cout; - best_act = actual; - } - } - - ret = clk_set_rate(ick, best_cout); - if (ret < 0) { - dev_err(dev, "ick clock failed\n"); - return -EIO; - } - - ret = clk_set_rate(div, clk_round_rate(div, best_act)); - if (ret < 0) { - dev_err(dev, "div clock failed\n"); - return -EIO; - } - - dev_dbg(dev, "ick/div = %ld/%ld\n", - clk_get_rate(ick), clk_get_rate(div)); - - return ret; -} - -static void fsi_pointer_update(struct fsi_stream *io, int size) -{ - io->buff_sample_pos += size; - - if (io->buff_sample_pos >= - io->period_samples * (io->period_pos + 1)) { - struct snd_pcm_substream *substream = io->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - - io->period_pos++; - - if (io->period_pos >= runtime->periods) { - io->buff_sample_pos = 0; - io->period_pos = 0; - } - - snd_pcm_period_elapsed(substream); - } -} - -/* - * pio data transfer handler - */ -static void fsi_pio_push16(struct fsi_priv *fsi, u8 *_buf, int samples) -{ - int i; - - if (fsi_is_enable_stream(fsi)) { - /* - * stream mode - * see - * fsi_pio_push_init() - */ - u32 *buf = (u32 *)_buf; - - for (i = 0; i < samples / 2; i++) - fsi_reg_write(fsi, DODT, buf[i]); - } else { - /* normal mode */ - u16 *buf = (u16 *)_buf; - - for (i = 0; i < samples; i++) - fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8)); - } -} - -static void fsi_pio_pop16(struct fsi_priv *fsi, u8 *_buf, int samples) -{ - u16 *buf = (u16 *)_buf; - int i; - - for (i = 0; i < samples; i++) - *(buf + i) = (u16)(fsi_reg_read(fsi, DIDT) >> 8); -} - -static void fsi_pio_push32(struct fsi_priv *fsi, u8 *_buf, int samples) -{ - u32 *buf = (u32 *)_buf; - int i; - - for (i = 0; i < samples; i++) - fsi_reg_write(fsi, DODT, *(buf + i)); -} - -static void fsi_pio_pop32(struct fsi_priv *fsi, u8 *_buf, int samples) -{ - u32 *buf = (u32 *)_buf; - int i; - - for (i = 0; i < samples; i++) - *(buf + i) = fsi_reg_read(fsi, DIDT); -} - -static u8 *fsi_pio_get_area(struct fsi_priv *fsi, struct fsi_stream *io) -{ - struct snd_pcm_runtime *runtime = io->substream->runtime; - - return runtime->dma_area + - samples_to_bytes(runtime, io->buff_sample_pos); -} - -static int fsi_pio_transfer(struct fsi_priv *fsi, struct fsi_stream *io, - void (*run16)(struct fsi_priv *fsi, u8 *buf, int samples), - void (*run32)(struct fsi_priv *fsi, u8 *buf, int samples), - int samples) -{ - u8 *buf; - - if (!fsi_stream_is_working(fsi, io)) - return -EINVAL; - - buf = fsi_pio_get_area(fsi, io); - - switch (io->sample_width) { - case 2: - run16(fsi, buf, samples); - break; - case 4: - run32(fsi, buf, samples); - break; - default: - return -EINVAL; - } - - fsi_pointer_update(io, samples); - - return 0; -} - -static int fsi_pio_pop(struct fsi_priv *fsi, struct fsi_stream *io) -{ - int sample_residues; /* samples in FSI fifo */ - int sample_space; /* ALSA free samples space */ - int samples; - - sample_residues = fsi_get_current_fifo_samples(fsi, io); - sample_space = io->buff_sample_capa - io->buff_sample_pos; - - samples = min(sample_residues, sample_space); - - return fsi_pio_transfer(fsi, io, - fsi_pio_pop16, - fsi_pio_pop32, - samples); -} - -static int fsi_pio_push(struct fsi_priv *fsi, struct fsi_stream *io) -{ - int sample_residues; /* ALSA residue samples */ - int sample_space; /* FSI fifo free samples space */ - int samples; - - sample_residues = io->buff_sample_capa - io->buff_sample_pos; - sample_space = io->fifo_sample_capa - - fsi_get_current_fifo_samples(fsi, io); - - samples = min(sample_residues, sample_space); - - return fsi_pio_transfer(fsi, io, - fsi_pio_push16, - fsi_pio_push32, - samples); -} - -static int fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, - int enable) -{ - struct fsi_master *master = fsi_get_master(fsi); - u32 clk = fsi_is_port_a(fsi) ? CRA : CRB; - - if (enable) - fsi_irq_enable(fsi, io); - else - fsi_irq_disable(fsi, io); - - if (fsi_is_clk_master(fsi)) - fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); - - return 0; -} - -static int fsi_pio_push_init(struct fsi_priv *fsi, struct fsi_stream *io) -{ - /* - * we can use 16bit stream mode - * when "playback" and "16bit data" - * and platform allows "stream mode" - * see - * fsi_pio_push16() - */ - if (fsi_is_enable_stream(fsi)) - io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | - BUSOP_SET(16, PACKAGE_16BITBUS_STREAM); - else - io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | - BUSOP_SET(16, PACKAGE_24BITBUS_BACK); - return 0; -} - -static int fsi_pio_pop_init(struct fsi_priv *fsi, struct fsi_stream *io) -{ - /* - * always 24bit bus, package back when "capture" - */ - io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | - BUSOP_SET(16, PACKAGE_24BITBUS_BACK); - return 0; -} - -static struct fsi_stream_handler fsi_pio_push_handler = { - .init = fsi_pio_push_init, - .transfer = fsi_pio_push, - .start_stop = fsi_pio_start_stop, -}; - -static struct fsi_stream_handler fsi_pio_pop_handler = { - .init = fsi_pio_pop_init, - .transfer = fsi_pio_pop, - .start_stop = fsi_pio_start_stop, -}; - -static irqreturn_t fsi_interrupt(int irq, void *data) -{ - struct fsi_master *master = data; - u32 int_st = fsi_irq_get_status(master); - - /* clear irq status */ - fsi_master_mask_set(master, SOFT_RST, IR, 0); - fsi_master_mask_set(master, SOFT_RST, IR, IR); - - if (int_st & AB_IO(1, AO_SHIFT)) - fsi_stream_transfer(&master->fsia.playback); - if (int_st & AB_IO(1, BO_SHIFT)) - fsi_stream_transfer(&master->fsib.playback); - if (int_st & AB_IO(1, AI_SHIFT)) - fsi_stream_transfer(&master->fsia.capture); - if (int_st & AB_IO(1, BI_SHIFT)) - fsi_stream_transfer(&master->fsib.capture); - - fsi_count_fifo_err(&master->fsia); - fsi_count_fifo_err(&master->fsib); - - fsi_irq_clear_status(&master->fsia); - fsi_irq_clear_status(&master->fsib); - - return IRQ_HANDLED; -} - -/* - * dma data transfer handler - */ -static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io) -{ - /* - * 24bit data : 24bit bus / package in back - * 16bit data : 16bit bus / stream mode - */ - io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | - BUSOP_SET(16, PACKAGE_16BITBUS_STREAM); - - return 0; -} - -static void fsi_dma_complete(void *data) -{ - struct fsi_stream *io = (struct fsi_stream *)data; - struct fsi_priv *fsi = fsi_stream_to_priv(io); - - fsi_pointer_update(io, io->period_samples); - - fsi_count_fifo_err(fsi); -} - -static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) -{ - struct snd_soc_dai *dai = fsi_get_dai(io->substream); - struct snd_pcm_substream *substream = io->substream; - struct dma_async_tx_descriptor *desc; - int is_play = fsi_stream_is_play(fsi, io); - enum dma_transfer_direction dir; - int ret = -EIO; - - if (is_play) - dir = DMA_MEM_TO_DEV; - else - dir = DMA_DEV_TO_MEM; - - desc = dmaengine_prep_dma_cyclic(io->chan, - substream->runtime->dma_addr, - snd_pcm_lib_buffer_bytes(substream), - snd_pcm_lib_period_bytes(substream), - dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - dev_err(dai->dev, "dmaengine_prep_dma_cyclic() fail\n"); - goto fsi_dma_transfer_err; - } - - desc->callback = fsi_dma_complete; - desc->callback_param = io; - - if (dmaengine_submit(desc) < 0) { - dev_err(dai->dev, "tx_submit() fail\n"); - goto fsi_dma_transfer_err; - } - - dma_async_issue_pending(io->chan); - - /* - * FIXME - * - * In DMAEngine case, codec and FSI cannot be started simultaneously - * since FSI is using the scheduler work queue. - * Therefore, in capture case, probably FSI FIFO will have got - * overflow error in this point. - * in that case, DMA cannot start transfer until error was cleared. - */ - if (!is_play) { - if (ERR_OVER & fsi_reg_read(fsi, DIFF_ST)) { - fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR); - fsi_reg_write(fsi, DIFF_ST, 0); - } - } - - ret = 0; - -fsi_dma_transfer_err: - return ret; -} - -static int fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, - int start) -{ - struct fsi_master *master = fsi_get_master(fsi); - u32 clk = fsi_is_port_a(fsi) ? CRA : CRB; - u32 enable = start ? DMA_ON : 0; - - fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable); - - dmaengine_terminate_all(io->chan); - - if (fsi_is_clk_master(fsi)) - fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); - - return 0; -} - -static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev) -{ - int is_play = fsi_stream_is_play(fsi, io); - -#ifdef CONFIG_SUPERH - dma_cap_mask_t mask; - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - io->chan = dma_request_channel(mask, shdma_chan_filter, - (void *)io->dma_id); -#else - io->chan = dma_request_chan(dev, is_play ? "tx" : "rx"); - if (IS_ERR(io->chan)) - io->chan = NULL; -#endif - if (io->chan) { - struct dma_slave_config cfg = {}; - int ret; - - if (is_play) { - cfg.dst_addr = fsi->phys + REG_DODT; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.direction = DMA_MEM_TO_DEV; - } else { - cfg.src_addr = fsi->phys + REG_DIDT; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.direction = DMA_DEV_TO_MEM; - } - - ret = dmaengine_slave_config(io->chan, &cfg); - if (ret < 0) { - dma_release_channel(io->chan); - io->chan = NULL; - } - } - - if (!io->chan) { - - /* switch to PIO handler */ - if (is_play) - fsi->playback.handler = &fsi_pio_push_handler; - else - fsi->capture.handler = &fsi_pio_pop_handler; - - dev_info(dev, "switch handler (dma => pio)\n"); - - /* probe again */ - return fsi_stream_probe(fsi, dev); - } - - return 0; -} - -static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io) -{ - fsi_stream_stop(fsi, io); - - if (io->chan) - dma_release_channel(io->chan); - - io->chan = NULL; - return 0; -} - -static struct fsi_stream_handler fsi_dma_push_handler = { - .init = fsi_dma_init, - .probe = fsi_dma_probe, - .transfer = fsi_dma_transfer, - .remove = fsi_dma_remove, - .start_stop = fsi_dma_push_start_stop, -}; - -/* - * dai ops - */ -static void fsi_fifo_init(struct fsi_priv *fsi, - struct fsi_stream *io, - struct device *dev) -{ - struct fsi_master *master = fsi_get_master(fsi); - int is_play = fsi_stream_is_play(fsi, io); - u32 shift, i; - int frame_capa; - - /* get on-chip RAM capacity */ - shift = fsi_master_read(master, FIFO_SZ); - shift >>= fsi_get_port_shift(fsi, io); - shift &= FIFO_SZ_MASK; - frame_capa = 256 << shift; - dev_dbg(dev, "fifo = %d words\n", frame_capa); - - /* - * The maximum number of sample data varies depending - * on the number of channels selected for the format. - * - * FIFOs are used in 4-channel units in 3-channel mode - * and in 8-channel units in 5- to 7-channel mode - * meaning that more FIFOs than the required size of DPRAM - * are used. - * - * ex) if 256 words of DP-RAM is connected - * 1 channel: 256 (256 x 1 = 256) - * 2 channels: 128 (128 x 2 = 256) - * 3 channels: 64 ( 64 x 3 = 192) - * 4 channels: 64 ( 64 x 4 = 256) - * 5 channels: 32 ( 32 x 5 = 160) - * 6 channels: 32 ( 32 x 6 = 192) - * 7 channels: 32 ( 32 x 7 = 224) - * 8 channels: 32 ( 32 x 8 = 256) - */ - for (i = 1; i < fsi->chan_num; i <<= 1) - frame_capa >>= 1; - dev_dbg(dev, "%d channel %d store\n", - fsi->chan_num, frame_capa); - - io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa); - - /* - * set interrupt generation factor - * clear FIFO - */ - if (is_play) { - fsi_reg_write(fsi, DOFF_CTL, IRQ_HALF); - fsi_reg_mask_set(fsi, DOFF_CTL, FIFO_CLR, FIFO_CLR); - } else { - fsi_reg_write(fsi, DIFF_CTL, IRQ_HALF); - fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR); - } -} - -static int fsi_hw_startup(struct fsi_priv *fsi, - struct fsi_stream *io, - struct device *dev) -{ - u32 data = 0; - - /* clock setting */ - if (fsi_is_clk_master(fsi)) - data = DIMD | DOMD; - - fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); - - /* clock inversion (CKG2) */ - data = 0; - if (fsi->bit_clk_inv) - data |= (1 << 0); - if (fsi->lr_clk_inv) - data |= (1 << 4); - if (fsi_is_clk_master(fsi)) - data <<= 8; - fsi_reg_write(fsi, CKG2, data); - - /* spdif ? */ - if (fsi_is_spdif(fsi)) { - fsi_spdif_clk_ctrl(fsi, 1); - fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); - } - - /* - * get bus settings - */ - data = 0; - switch (io->sample_width) { - case 2: - data = BUSOP_GET(16, io->bus_option); - break; - case 4: - data = BUSOP_GET(24, io->bus_option); - break; - } - fsi_format_bus_setup(fsi, io, data, dev); - - /* irq clear */ - fsi_irq_disable(fsi, io); - fsi_irq_clear_status(fsi); - - /* fifo init */ - fsi_fifo_init(fsi, io, dev); - - /* start master clock */ - if (fsi_is_clk_master(fsi)) - return fsi_clk_enable(dev, fsi); - - return 0; -} - -static int fsi_hw_shutdown(struct fsi_priv *fsi, - struct device *dev) -{ - /* stop master clock */ - if (fsi_is_clk_master(fsi)) - return fsi_clk_disable(dev, fsi); - - return 0; -} - -static int fsi_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct fsi_priv *fsi = fsi_get_priv(substream); - - fsi_clk_invalid(fsi); - - return 0; -} - -static void fsi_dai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct fsi_priv *fsi = fsi_get_priv(substream); - - fsi_clk_invalid(fsi); -} - -static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct fsi_priv *fsi = fsi_get_priv(substream); - struct fsi_stream *io = fsi_stream_get(fsi, substream); - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - fsi_stream_init(fsi, io, substream); - if (!ret) - ret = fsi_hw_startup(fsi, io, dai->dev); - if (!ret) - ret = fsi_stream_start(fsi, io); - if (!ret) - ret = fsi_stream_transfer(io); - break; - case SNDRV_PCM_TRIGGER_STOP: - if (!ret) - ret = fsi_hw_shutdown(fsi, dai->dev); - fsi_stream_stop(fsi, io); - fsi_stream_quit(fsi, io); - break; - } - - return ret; -} - -static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) -{ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - fsi->fmt = CR_I2S; - fsi->chan_num = 2; - break; - case SND_SOC_DAIFMT_LEFT_J: - fsi->fmt = CR_PCM; - fsi->chan_num = 2; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int fsi_set_fmt_spdif(struct fsi_priv *fsi) -{ - struct fsi_master *master = fsi_get_master(fsi); - - if (fsi_version(master) < 2) - return -EINVAL; - - fsi->fmt = CR_DTMD_SPDIF_PCM | CR_PCM; - fsi->chan_num = 2; - - return 0; -} - -static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -{ - struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); - int ret; - - /* set clock master audio interface */ - switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { - case SND_SOC_DAIFMT_BC_FC: - break; - case SND_SOC_DAIFMT_BP_FP: - fsi->clk_master = 1; /* cpu is master */ - break; - default: - return -EINVAL; - } - - /* set clock inversion */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_IF: - fsi->bit_clk_inv = 0; - fsi->lr_clk_inv = 1; - break; - case SND_SOC_DAIFMT_IB_NF: - fsi->bit_clk_inv = 1; - fsi->lr_clk_inv = 0; - break; - case SND_SOC_DAIFMT_IB_IF: - fsi->bit_clk_inv = 1; - fsi->lr_clk_inv = 1; - break; - case SND_SOC_DAIFMT_NB_NF: - default: - fsi->bit_clk_inv = 0; - fsi->lr_clk_inv = 0; - break; - } - - if (fsi_is_clk_master(fsi)) { - if (fsi->clk_cpg) - fsi_clk_init(dai->dev, fsi, 0, 1, 1, - fsi_clk_set_rate_cpg); - else - fsi_clk_init(dai->dev, fsi, 1, 1, 0, - fsi_clk_set_rate_external); - } - - /* set format */ - if (fsi_is_spdif(fsi)) - ret = fsi_set_fmt_spdif(fsi); - else - ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK); - - return ret; -} - -static int fsi_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct fsi_priv *fsi = fsi_get_priv(substream); - - if (fsi_is_clk_master(fsi)) - fsi_clk_valid(fsi, params_rate(params)); - - return 0; -} - -/* - * Select below from Sound Card, not auto - * SND_SOC_DAIFMT_CBC_CFC - * SND_SOC_DAIFMT_CBP_CFP - */ -static const u64 fsi_dai_formats = - SND_SOC_POSSIBLE_DAIFMT_I2S | - SND_SOC_POSSIBLE_DAIFMT_LEFT_J | - SND_SOC_POSSIBLE_DAIFMT_NB_NF | - SND_SOC_POSSIBLE_DAIFMT_NB_IF | - SND_SOC_POSSIBLE_DAIFMT_IB_NF | - SND_SOC_POSSIBLE_DAIFMT_IB_IF; - -static const struct snd_soc_dai_ops fsi_dai_ops = { - .startup = fsi_dai_startup, - .shutdown = fsi_dai_shutdown, - .trigger = fsi_dai_trigger, - .set_fmt = fsi_dai_set_fmt, - .hw_params = fsi_dai_hw_params, - .auto_selectable_formats = &fsi_dai_formats, - .num_auto_selectable_formats = 1, -}; - -/* - * pcm ops - */ - -static const struct snd_pcm_hardware fsi_pcm_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, - .buffer_bytes_max = 64 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 32, - .fifo_size = 256, -}; - -static int fsi_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int ret = 0; - - snd_soc_set_runtime_hwparams(substream, &fsi_pcm_hardware); - - ret = snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - - return ret; -} - -static snd_pcm_uframes_t fsi_pointer(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct fsi_priv *fsi = fsi_get_priv(substream); - struct fsi_stream *io = fsi_stream_get(fsi, substream); - - return fsi_sample2frame(fsi, io->buff_sample_pos); -} - -/* - * snd_soc_component - */ - -#define PREALLOC_BUFFER (32 * 1024) -#define PREALLOC_BUFFER_MAX (32 * 1024) - -static int fsi_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) -{ - snd_pcm_set_managed_buffer_all( - rtd->pcm, - SNDRV_DMA_TYPE_DEV, - rtd->card->snd_card->dev, - PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); - return 0; -} - -/* - * alsa struct - */ - -static struct snd_soc_dai_driver fsi_soc_dai[] = { - { - .name = "fsia-dai", - .playback = { - .rates = FSI_RATES, - .formats = FSI_FMTS, - .channels_min = 2, - .channels_max = 2, - }, - .capture = { - .rates = FSI_RATES, - .formats = FSI_FMTS, - .channels_min = 2, - .channels_max = 2, - }, - .ops = &fsi_dai_ops, - }, - { - .name = "fsib-dai", - .playback = { - .rates = FSI_RATES, - .formats = FSI_FMTS, - .channels_min = 2, - .channels_max = 2, - }, - .capture = { - .rates = FSI_RATES, - .formats = FSI_FMTS, - .channels_min = 2, - .channels_max = 2, - }, - .ops = &fsi_dai_ops, - }, -}; - -static const struct snd_soc_component_driver fsi_soc_component = { - .name = "fsi", - .open = fsi_pcm_open, - .pointer = fsi_pointer, - .pcm_construct = fsi_pcm_new, -}; - -/* - * platform function - */ -static void fsi_of_parse(char *name, - struct device_node *np, - struct sh_fsi_port_info *info, - struct device *dev) -{ - int i; - char prop[128]; - unsigned long flags = 0; - struct { - char *name; - unsigned int val; - } of_parse_property[] = { - { "spdif-connection", SH_FSI_FMT_SPDIF }, - { "stream-mode-support", SH_FSI_ENABLE_STREAM_MODE }, - { "use-internal-clock", SH_FSI_CLK_CPG }, - }; - - for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) { - sprintf(prop, "%s,%s", name, of_parse_property[i].name); - if (of_property_present(np, prop)) - flags |= of_parse_property[i].val; - } - info->flags = flags; - - dev_dbg(dev, "%s flags : %lx\n", name, info->flags); -} - -static void fsi_port_info_init(struct fsi_priv *fsi, - struct sh_fsi_port_info *info) -{ - if (info->flags & SH_FSI_FMT_SPDIF) - fsi->spdif = 1; - - if (info->flags & SH_FSI_CLK_CPG) - fsi->clk_cpg = 1; - - if (info->flags & SH_FSI_ENABLE_STREAM_MODE) - fsi->enable_stream = 1; -} - -static void fsi_handler_init(struct fsi_priv *fsi, - struct sh_fsi_port_info *info) -{ - fsi->playback.handler = &fsi_pio_push_handler; /* default PIO */ - fsi->playback.priv = fsi; - fsi->capture.handler = &fsi_pio_pop_handler; /* default PIO */ - fsi->capture.priv = fsi; - - if (info->tx_id) { - fsi->playback.dma_id = info->tx_id; - fsi->playback.handler = &fsi_dma_push_handler; - } -} - -static const struct fsi_core fsi1_core = { - .ver = 1, - - /* Interrupt */ - .int_st = INT_ST, - .iemsk = IEMSK, - .imsk = IMSK, -}; - -static const struct fsi_core fsi2_core = { - .ver = 2, - - /* Interrupt */ - .int_st = CPU_INT_ST, - .iemsk = CPU_IEMSK, - .imsk = CPU_IMSK, - .a_mclk = A_MST_CTLR, - .b_mclk = B_MST_CTLR, -}; - -static const struct of_device_id fsi_of_match[] = { - { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, - { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, - {}, -}; -MODULE_DEVICE_TABLE(of, fsi_of_match); - -static const struct platform_device_id fsi_id_table[] = { - { "sh_fsi", (kernel_ulong_t)&fsi1_core }, - {}, -}; -MODULE_DEVICE_TABLE(platform, fsi_id_table); - -static int fsi_probe(struct platform_device *pdev) -{ - struct fsi_master *master; - struct device_node *np = pdev->dev.of_node; - struct sh_fsi_platform_info info; - const struct fsi_core *core; - struct fsi_priv *fsi; - struct resource *res; - unsigned int irq; - int ret; - - memset(&info, 0, sizeof(info)); - - core = NULL; - if (np) { - core = of_device_get_match_data(&pdev->dev); - fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); - fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); - } else { - const struct platform_device_id *id_entry = pdev->id_entry; - if (id_entry) - core = (struct fsi_core *)id_entry->driver_data; - - if (pdev->dev.platform_data) - memcpy(&info, pdev->dev.platform_data, sizeof(info)); - } - - if (!core) { - dev_err(&pdev->dev, "unknown fsi device\n"); - return -ENODEV; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!res || (int)irq <= 0) { - dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); - return -ENODEV; - } - - master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); - if (!master) - return -ENOMEM; - - master->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!master->base) { - dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n"); - return -ENXIO; - } - - /* master setting */ - master->core = core; - spin_lock_init(&master->lock); - - /* FSI A setting */ - fsi = &master->fsia; - fsi->base = master->base; - fsi->phys = res->start; - fsi->master = master; - fsi_port_info_init(fsi, &info.port_a); - fsi_handler_init(fsi, &info.port_a); - ret = fsi_stream_probe(fsi, &pdev->dev); - if (ret < 0) { - dev_err(&pdev->dev, "FSIA stream probe failed\n"); - return ret; - } - - /* FSI B setting */ - fsi = &master->fsib; - fsi->base = master->base + 0x40; - fsi->phys = res->start + 0x40; - fsi->master = master; - fsi_port_info_init(fsi, &info.port_b); - fsi_handler_init(fsi, &info.port_b); - ret = fsi_stream_probe(fsi, &pdev->dev); - if (ret < 0) { - dev_err(&pdev->dev, "FSIB stream probe failed\n"); - goto exit_fsia; - } - - pm_runtime_enable(&pdev->dev); - dev_set_drvdata(&pdev->dev, master); - - ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0, - dev_name(&pdev->dev), master); - if (ret) { - dev_err(&pdev->dev, "irq request err\n"); - goto exit_fsib; - } - - ret = devm_snd_soc_register_component(&pdev->dev, &fsi_soc_component, - fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai)); - if (ret < 0) { - dev_err(&pdev->dev, "cannot snd component register\n"); - goto exit_fsib; - } - - return ret; - -exit_fsib: - pm_runtime_disable(&pdev->dev); - fsi_stream_remove(&master->fsib); -exit_fsia: - fsi_stream_remove(&master->fsia); - - return ret; -} - -static void fsi_remove(struct platform_device *pdev) -{ - struct fsi_master *master; - - master = dev_get_drvdata(&pdev->dev); - - pm_runtime_disable(&pdev->dev); - - fsi_stream_remove(&master->fsia); - fsi_stream_remove(&master->fsib); -} - -static void __fsi_suspend(struct fsi_priv *fsi, - struct fsi_stream *io, - struct device *dev) -{ - if (!fsi_stream_is_working(fsi, io)) - return; - - fsi_stream_stop(fsi, io); - fsi_hw_shutdown(fsi, dev); -} - -static void __fsi_resume(struct fsi_priv *fsi, - struct fsi_stream *io, - struct device *dev) -{ - if (!fsi_stream_is_working(fsi, io)) - return; - - fsi_hw_startup(fsi, io, dev); - fsi_stream_start(fsi, io); -} - -static int fsi_suspend(struct device *dev) -{ - struct fsi_master *master = dev_get_drvdata(dev); - struct fsi_priv *fsia = &master->fsia; - struct fsi_priv *fsib = &master->fsib; - - __fsi_suspend(fsia, &fsia->playback, dev); - __fsi_suspend(fsia, &fsia->capture, dev); - - __fsi_suspend(fsib, &fsib->playback, dev); - __fsi_suspend(fsib, &fsib->capture, dev); - - return 0; -} - -static int fsi_resume(struct device *dev) -{ - struct fsi_master *master = dev_get_drvdata(dev); - struct fsi_priv *fsia = &master->fsia; - struct fsi_priv *fsib = &master->fsib; - - __fsi_resume(fsia, &fsia->playback, dev); - __fsi_resume(fsia, &fsia->capture, dev); - - __fsi_resume(fsib, &fsib->playback, dev); - __fsi_resume(fsib, &fsib->capture, dev); - - return 0; -} - -static const struct dev_pm_ops fsi_pm_ops = { - .suspend = fsi_suspend, - .resume = fsi_resume, -}; - -static struct platform_driver fsi_driver = { - .driver = { - .name = "fsi-pcm-audio", - .pm = &fsi_pm_ops, - .of_match_table = fsi_of_match, - }, - .probe = fsi_probe, - .remove = fsi_remove, - .id_table = fsi_id_table, -}; - -module_platform_driver(fsi_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("SuperH onchip FSI audio driver"); -MODULE_AUTHOR("Kuninori Morimoto "); -MODULE_ALIAS("platform:fsi-pcm-audio"); diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c deleted file mode 100644 index db618c09d1e0..000000000000 --- a/sound/soc/sh/hac.c +++ /dev/null @@ -1,344 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Hitachi Audio Controller (AC97) support for SH7760/SH7780 -// -// Copyright (c) 2007 Manuel Lauss -// -// dont forget to set IPSEL/OMSEL register bits (in your board code) to -// enable HAC output pins! - -/* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only - * the FIRST can be used since ASoC does not pass any information to the - * ac97_read/write() functions regarding WHICH unit to use. You'll have - * to edit the code a bit to use the other AC97 unit. --mlau - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* regs and bits */ -#define HACCR 0x08 -#define HACCSAR 0x20 -#define HACCSDR 0x24 -#define HACPCML 0x28 -#define HACPCMR 0x2C -#define HACTIER 0x50 -#define HACTSR 0x54 -#define HACRIER 0x58 -#define HACRSR 0x5C -#define HACACR 0x60 - -#define CR_CR (1 << 15) /* "codec-ready" indicator */ -#define CR_CDRT (1 << 11) /* cold reset */ -#define CR_WMRT (1 << 10) /* warm reset */ -#define CR_B9 (1 << 9) /* the mysterious "bit 9" */ -#define CR_ST (1 << 5) /* AC97 link start bit */ - -#define CSAR_RD (1 << 19) /* AC97 data read bit */ -#define CSAR_WR (0) - -#define TSR_CMDAMT (1 << 31) -#define TSR_CMDDMT (1 << 30) - -#define RSR_STARY (1 << 22) -#define RSR_STDRY (1 << 21) - -#define ACR_DMARX16 (1 << 30) -#define ACR_DMATX16 (1 << 29) -#define ACR_TX12ATOM (1 << 26) -#define ACR_DMARX20 ((1 << 24) | (1 << 22)) -#define ACR_DMATX20 ((1 << 23) | (1 << 21)) - -#define CSDR_SHIFT 4 -#define CSDR_MASK (0xffff << CSDR_SHIFT) -#define CSAR_SHIFT 12 -#define CSAR_MASK (0x7f << CSAR_SHIFT) - -#define AC97_WRITE_RETRY 1 -#define AC97_READ_RETRY 5 - -/* manual-suggested AC97 codec access timeouts (us) */ -#define TMO_E1 500 /* 21 < E1 < 1000 */ -#define TMO_E2 13 /* 13 < E2 */ -#define TMO_E3 21 /* 21 < E3 */ -#define TMO_E4 500 /* 21 < E4 < 1000 */ - -struct hac_priv { - unsigned long mmio; /* HAC base address */ -} hac_cpu_data[] = { -#if defined(CONFIG_CPU_SUBTYPE_SH7760) - { - .mmio = 0xFE240000, - }, - { - .mmio = 0xFE250000, - }, -#elif defined(CONFIG_CPU_SUBTYPE_SH7780) - { - .mmio = 0xFFE40000, - }, -#else -#error "Unsupported SuperH SoC" -#endif -}; - -#define HACREG(reg) (*(unsigned long *)(hac->mmio + (reg))) - -/* - * AC97 read/write flow as outlined in the SH7760 manual (pages 903-906) - */ -static int hac_get_codec_data(struct hac_priv *hac, unsigned short r, - unsigned short *v) -{ - unsigned int to1, to2, i; - unsigned short adr; - - for (i = AC97_READ_RETRY; i; i--) { - *v = 0; - /* wait for HAC to receive something from the codec */ - for (to1 = TMO_E4; - to1 && !(HACREG(HACRSR) & RSR_STARY); - --to1) - udelay(1); - for (to2 = TMO_E4; - to2 && !(HACREG(HACRSR) & RSR_STDRY); - --to2) - udelay(1); - - if (!to1 && !to2) - return 0; /* codec comm is down */ - - adr = ((HACREG(HACCSAR) & CSAR_MASK) >> CSAR_SHIFT); - *v = ((HACREG(HACCSDR) & CSDR_MASK) >> CSDR_SHIFT); - - HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); - - if (r == adr) - break; - - /* manual says: wait at least 21 usec before retrying */ - udelay(21); - } - HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); - return i; -} - -static unsigned short hac_read_codec_aux(struct hac_priv *hac, - unsigned short reg) -{ - unsigned short val; - unsigned int i, to; - - for (i = AC97_READ_RETRY; i; i--) { - /* send_read_request */ - local_irq_disable(); - HACREG(HACTSR) &= ~(TSR_CMDAMT); - HACREG(HACCSAR) = (reg << CSAR_SHIFT) | CSAR_RD; - local_irq_enable(); - - for (to = TMO_E3; - to && !(HACREG(HACTSR) & TSR_CMDAMT); - --to) - udelay(1); - - HACREG(HACTSR) &= ~TSR_CMDAMT; - val = 0; - if (hac_get_codec_data(hac, reg, &val) != 0) - break; - } - - return i ? val : ~0; -} - -static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - unsigned short val) -{ - int unit_id = 0 /* ac97->private_data */; - struct hac_priv *hac = &hac_cpu_data[unit_id]; - unsigned int i, to; - /* write_codec_aux */ - for (i = AC97_WRITE_RETRY; i; i--) { - /* send_write_request */ - local_irq_disable(); - HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT); - HACREG(HACCSDR) = (val << CSDR_SHIFT); - HACREG(HACCSAR) = (reg << CSAR_SHIFT) & (~CSAR_RD); - local_irq_enable(); - - /* poll-wait for CMDAMT and CMDDMT */ - for (to = TMO_E1; - to && !(HACREG(HACTSR) & (TSR_CMDAMT|TSR_CMDDMT)); - --to) - udelay(1); - - HACREG(HACTSR) &= ~(TSR_CMDAMT | TSR_CMDDMT); - if (to) - break; - /* timeout, try again */ - } -} - -static unsigned short hac_ac97_read(struct snd_ac97 *ac97, - unsigned short reg) -{ - int unit_id = 0 /* ac97->private_data */; - struct hac_priv *hac = &hac_cpu_data[unit_id]; - return hac_read_codec_aux(hac, reg); -} - -static void hac_ac97_warmrst(struct snd_ac97 *ac97) -{ - int unit_id = 0 /* ac97->private_data */; - struct hac_priv *hac = &hac_cpu_data[unit_id]; - unsigned int tmo; - - HACREG(HACCR) = CR_WMRT | CR_ST | CR_B9; - msleep(10); - HACREG(HACCR) = CR_ST | CR_B9; - for (tmo = 1000; (tmo > 0) && !(HACREG(HACCR) & CR_CR); tmo--) - udelay(1); - - if (!tmo) - printk(KERN_INFO "hac: reset: AC97 link down!\n"); - /* settings this bit lets us have a conversation with codec */ - HACREG(HACACR) |= ACR_TX12ATOM; -} - -static void hac_ac97_coldrst(struct snd_ac97 *ac97) -{ - int unit_id = 0 /* ac97->private_data */; - struct hac_priv *hac; - hac = &hac_cpu_data[unit_id]; - - HACREG(HACCR) = 0; - HACREG(HACCR) = CR_CDRT | CR_ST | CR_B9; - msleep(10); - hac_ac97_warmrst(ac97); -} - -static struct snd_ac97_bus_ops hac_ac97_ops = { - .read = hac_ac97_read, - .write = hac_ac97_write, - .reset = hac_ac97_coldrst, - .warm_reset = hac_ac97_warmrst, -}; - -static int hac_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct hac_priv *hac = &hac_cpu_data[dai->id]; - int d = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; - - switch (params->msbits) { - case 16: - HACREG(HACACR) |= d ? ACR_DMARX16 : ACR_DMATX16; - HACREG(HACACR) &= d ? ~ACR_DMARX20 : ~ACR_DMATX20; - break; - case 20: - HACREG(HACACR) &= d ? ~ACR_DMARX16 : ~ACR_DMATX16; - HACREG(HACACR) |= d ? ACR_DMARX20 : ACR_DMATX20; - break; - default: - pr_debug("hac: invalid depth %d bit\n", params->msbits); - return -EINVAL; - break; - } - - return 0; -} - -#define AC97_RATES \ - SNDRV_PCM_RATE_8000_192000 - -#define AC97_FMTS \ - SNDRV_PCM_FMTBIT_S16_LE - -static const struct snd_soc_dai_ops hac_dai_ops = { - .hw_params = hac_hw_params, -}; - -static struct snd_soc_dai_driver sh4_hac_dai[] = { -{ - .name = "hac-dai.0", - .playback = { - .rates = AC97_RATES, - .formats = AC97_FMTS, - .channels_min = 2, - .channels_max = 2, - }, - .capture = { - .rates = AC97_RATES, - .formats = AC97_FMTS, - .channels_min = 2, - .channels_max = 2, - }, - .ops = &hac_dai_ops, -}, -#ifdef CONFIG_CPU_SUBTYPE_SH7760 -{ - .name = "hac-dai.1", - .id = 1, - .playback = { - .rates = AC97_RATES, - .formats = AC97_FMTS, - .channels_min = 2, - .channels_max = 2, - }, - .capture = { - .rates = AC97_RATES, - .formats = AC97_FMTS, - .channels_min = 2, - .channels_max = 2, - }, - .ops = &hac_dai_ops, - -}, -#endif -}; - -static const struct snd_soc_component_driver sh4_hac_component = { - .name = "sh4-hac", - .legacy_dai_naming = 1, -}; - -static int hac_soc_platform_probe(struct platform_device *pdev) -{ - int ret; - - ret = snd_soc_set_ac97_ops(&hac_ac97_ops); - if (ret != 0) - return ret; - - return devm_snd_soc_register_component(&pdev->dev, &sh4_hac_component, - sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai)); -} - -static void hac_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_set_ac97_ops(NULL); -} - -static struct platform_driver hac_pcm_driver = { - .driver = { - .name = "hac-pcm-audio", - }, - - .probe = hac_soc_platform_probe, - .remove = hac_soc_platform_remove, -}; - -module_platform_driver(hac_pcm_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver"); -MODULE_AUTHOR("Manuel Lauss "); diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c deleted file mode 100644 index 5a0bc6edac0a..000000000000 --- a/sound/soc/sh/migor.c +++ /dev/null @@ -1,205 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// ALSA SoC driver for Migo-R -// -// Copyright (C) 2009-2010 Guennadi Liakhovetski - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include "../codecs/wm8978.h" -#include "siu.h" - -/* Default 8000Hz sampling frequency */ -static unsigned long codec_freq = 8000 * 512; - -static unsigned int use_count; - -/* External clock, sourced from the codec at the SIUMCKB pin */ -static unsigned long siumckb_recalc(struct clk *clk) -{ - return codec_freq; -} - -static struct sh_clk_ops siumckb_clk_ops = { - .recalc = siumckb_recalc, -}; - -static struct clk siumckb_clk = { - .ops = &siumckb_clk_ops, - .rate = 0, /* initialised at run-time */ -}; - -static struct clk_lookup *siumckb_lookup; - -static int migor_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - int ret; - unsigned int rate = params_rate(params); - - ret = snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 13000000, - SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_OPCLKRATE, rate * 512); - if (ret < 0) - return ret; - - codec_freq = rate * 512; - /* - * This propagates the parent frequency change to children and - * recalculates the frequency table - */ - clk_set_rate(&siumckb_clk, codec_freq); - dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq); - - ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), SIU_CLKB_EXT, - codec_freq / 2, SND_SOC_CLOCK_IN); - - if (!ret) - use_count++; - - return ret; -} - -static int migor_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - - if (use_count) { - use_count--; - - if (!use_count) - snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 0, - SND_SOC_CLOCK_IN); - } else { - dev_dbg(codec_dai->dev, "Unbalanced hw_free!\n"); - } - - return 0; -} - -static const struct snd_soc_ops migor_dai_ops = { - .hw_params = migor_hw_params, - .hw_free = migor_hw_free, -}; - -static const struct snd_soc_dapm_widget migor_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone", NULL), - SND_SOC_DAPM_MIC("Onboard Microphone", NULL), - SND_SOC_DAPM_MIC("External Microphone", NULL), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - /* Headphone output connected to LHP/RHP, enable OUT4 for VMID */ - { "Headphone", NULL, "OUT4 VMID" }, - { "OUT4 VMID", NULL, "LHP" }, - { "OUT4 VMID", NULL, "RHP" }, - - /* On-board microphone */ - { "RMICN", NULL, "Mic Bias" }, - { "RMICP", NULL, "Mic Bias" }, - { "Mic Bias", NULL, "Onboard Microphone" }, - - /* External microphone */ - { "LMICN", NULL, "Mic Bias" }, - { "LMICP", NULL, "Mic Bias" }, - { "Mic Bias", NULL, "External Microphone" }, -}; - -/* migor digital audio interface glue - connects codec <--> CPU */ -SND_SOC_DAILINK_DEFS(wm8978, - DAILINK_COMP_ARRAY(COMP_CPU("siu-pcm-audio")), - DAILINK_COMP_ARRAY(COMP_CODEC("wm8978.0-001a", "wm8978-hifi")), - DAILINK_COMP_ARRAY(COMP_PLATFORM("siu-pcm-audio"))); - -static struct snd_soc_dai_link migor_dai = { - .name = "wm8978", - .stream_name = "WM8978", - .dai_fmt = SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_CBS_CFS, - .ops = &migor_dai_ops, - SND_SOC_DAILINK_REG(wm8978), -}; - -/* migor audio machine driver */ -static struct snd_soc_card snd_soc_migor = { - .name = "Migo-R", - .owner = THIS_MODULE, - .dai_link = &migor_dai, - .num_links = 1, - - .dapm_widgets = migor_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(migor_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), -}; - -static struct platform_device *migor_snd_device; - -static int __init migor_init(void) -{ - int ret; - - ret = clk_register(&siumckb_clk); - if (ret < 0) - return ret; - - siumckb_lookup = clkdev_create(&siumckb_clk, "siumckb_clk", NULL); - if (!siumckb_lookup) { - ret = -ENOMEM; - goto eclkdevalloc; - } - - /* Port number used on this machine: port B */ - migor_snd_device = platform_device_alloc("soc-audio", 1); - if (!migor_snd_device) { - ret = -ENOMEM; - goto epdevalloc; - } - - platform_set_drvdata(migor_snd_device, &snd_soc_migor); - - ret = platform_device_add(migor_snd_device); - if (ret) - goto epdevadd; - - return 0; - -epdevadd: - platform_device_put(migor_snd_device); -epdevalloc: - clkdev_drop(siumckb_lookup); -eclkdevalloc: - clk_unregister(&siumckb_clk); - return ret; -} - -static void __exit migor_exit(void) -{ - clkdev_drop(siumckb_lookup); - clk_unregister(&siumckb_clk); - platform_device_unregister(migor_snd_device); -} - -module_init(migor_init); -module_exit(migor_exit); - -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_DESCRIPTION("ALSA SoC Migor"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile deleted file mode 100644 index 45eb875a912a..000000000000 --- a/sound/soc/sh/rcar/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -snd-soc-rcar-y := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o debugfs.o -obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c deleted file mode 100644 index 0f190abf00e7..000000000000 --- a/sound/soc/sh/rcar/adg.c +++ /dev/null @@ -1,775 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Helper routines for R-Car sound ADG. -// -// Copyright (C) 2013 Kuninori Morimoto -#include -#include -#include "rsnd.h" - -#define CLKA 0 -#define CLKB 1 -#define CLKC 2 -#define CLKI 3 -#define CLKINMAX 4 - -#define CLKOUT 0 -#define CLKOUT1 1 -#define CLKOUT2 2 -#define CLKOUT3 3 -#define CLKOUTMAX 4 - -#define BRRx_MASK(x) (0x3FF & x) - -static struct rsnd_mod_ops adg_ops = { - .name = "adg", -}; - -#define ADG_HZ_441 0 -#define ADG_HZ_48 1 -#define ADG_HZ_SIZE 2 - -struct rsnd_adg { - struct clk *clkin[CLKINMAX]; - struct clk *clkout[CLKOUTMAX]; - struct clk *null_clk; - struct clk_onecell_data onecell; - struct rsnd_mod mod; - int clkin_rate[CLKINMAX]; - int clkin_size; - int clkout_size; - u32 ckr; - u32 brga; - u32 brgb; - - int brg_rate[ADG_HZ_SIZE]; /* BRGA / BRGB */ -}; - -#define for_each_rsnd_clkin(pos, adg, i) \ - for (i = 0; \ - (i < adg->clkin_size) && \ - ((pos) = adg->clkin[i]); \ - i++) -#define for_each_rsnd_clkout(pos, adg, i) \ - for (i = 0; \ - (i < adg->clkout_size) && \ - ((pos) = adg->clkout[i]); \ - i++) -#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) - -static const char * const clkin_name_gen4[] = { - [CLKA] = "clkin", -}; - -static const char * const clkin_name_gen2[] = { - [CLKA] = "clk_a", - [CLKB] = "clk_b", - [CLKC] = "clk_c", - [CLKI] = "clk_i", -}; - -static const char * const clkout_name_gen2[] = { - [CLKOUT] = "audio_clkout", - [CLKOUT1] = "audio_clkout1", - [CLKOUT2] = "audio_clkout2", - [CLKOUT3] = "audio_clkout3", -}; - -static u32 rsnd_adg_calculate_brgx(unsigned long div) -{ - int i; - - if (!div) - return 0; - - for (i = 3; i >= 0; i--) { - int ratio = 2 << (i * 2); - if (0 == (div % ratio)) - return (u32)((i << 8) | ((div / ratio) - 1)); - } - - return ~0; -} - -static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) -{ - struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); - int id = rsnd_mod_id(ssi_mod); - int ws = id; - - if (rsnd_ssi_is_pin_sharing(io)) { - switch (id) { - case 1: - case 2: - case 9: - ws = 0; - break; - case 4: - ws = 3; - break; - case 8: - ws = 7; - break; - } - } else { - /* - * SSI8 is not connected to ADG. - * Thus SSI9 is using ws = 8 - */ - if (id == 9) - ws = 8; - } - - return (0x6 + ws) << 8; -} - -static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - unsigned int target_rate, - unsigned int *target_val, - unsigned int *target_en) -{ - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct device *dev = rsnd_priv_to_dev(priv); - int sel; - unsigned int val, en; - unsigned int min, diff; - unsigned int sel_rate[] = { - adg->clkin_rate[CLKA], /* 0000: CLKA */ - adg->clkin_rate[CLKB], /* 0001: CLKB */ - adg->clkin_rate[CLKC], /* 0010: CLKC */ - adg->brg_rate[ADG_HZ_441], /* 0011: BRGA */ - adg->brg_rate[ADG_HZ_48], /* 0100: BRGB */ - }; - - min = ~0; - val = 0; - en = 0; - for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { - int idx = 0; - int step = 2; - int div; - - if (!sel_rate[sel]) - continue; - - for (div = 2; div <= 98304; div += step) { - diff = abs(target_rate - sel_rate[sel] / div); - if (min > diff) { - val = (sel << 8) | idx; - min = diff; - en = 1 << (sel + 1); /* fixme */ - } - - /* - * step of 0_0000 / 0_0001 / 0_1101 - * are out of order - */ - if ((idx > 2) && (idx % 2)) - step *= 2; - if (idx == 0x1c) { - div += step; - step *= 2; - } - idx++; - } - } - - if (min == ~0) { - dev_err(dev, "no Input clock\n"); - return; - } - - *target_val = val; - if (target_en) - *target_en = en; -} - -static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - unsigned int in_rate, - unsigned int out_rate, - u32 *in, u32 *out, u32 *en) -{ - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - unsigned int target_rate; - u32 *target_val; - u32 _in; - u32 _out; - u32 _en; - - /* default = SSI WS */ - _in = - _out = rsnd_adg_ssi_ws_timing_gen2(io); - - target_rate = 0; - target_val = NULL; - _en = 0; - if (runtime->rate != in_rate) { - target_rate = out_rate; - target_val = &_out; - } else if (runtime->rate != out_rate) { - target_rate = in_rate; - target_val = &_in; - } - - if (target_rate) - __rsnd_adg_get_timesel_ratio(priv, io, - target_rate, - target_val, &_en); - - if (in) - *in = _in; - if (out) - *out = _out; - if (en) - *en = _en; -} - -int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod); - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int id = rsnd_mod_id(cmd_mod); - int shift = (id % 2) ? 16 : 0; - u32 mask, val; - - rsnd_adg_get_timesel_ratio(priv, io, - rsnd_src_get_in_rate(priv, io), - rsnd_src_get_out_rate(priv, io), - NULL, &val, NULL); - - val = val << shift; - mask = 0x0f1f << shift; - - rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val); - - return 0; -} - -int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io, - unsigned int in_rate, - unsigned int out_rate) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - u32 in, out; - u32 mask, en; - int id = rsnd_mod_id(src_mod); - int shift = (id % 2) ? 16 : 0; - - rsnd_mod_make_sure(src_mod, RSND_MOD_SRC); - - rsnd_adg_get_timesel_ratio(priv, io, - in_rate, out_rate, - &in, &out, &en); - - in = in << shift; - out = out << shift; - mask = 0x0f1f << shift; - - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL(id / 2), mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL(id / 2), mask, out); - - if (en) - rsnd_mod_bset(adg_mod, DIV_EN, en, en); - - return 0; -} - -static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - struct device *dev = rsnd_priv_to_dev(priv); - int id = rsnd_mod_id(ssi_mod); - int shift = (id % 4) * 8; - u32 mask = 0xFF << shift; - - rsnd_mod_make_sure(ssi_mod, RSND_MOD_SSI); - - val = val << shift; - - /* - * SSI 8 is not connected to ADG. - * it works with SSI 7 - */ - if (id == 8) - return; - - rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL(id / 4), mask, val); - - dev_dbg(dev, "AUDIO_CLK_SEL is 0x%x\n", val); -} - -int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) -{ - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct clk *clk; - int i; - int sel_table[] = { - [CLKA] = 0x1, - [CLKB] = 0x2, - [CLKC] = 0x3, - [CLKI] = 0x0, - }; - - /* - * find suitable clock from - * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. - */ - for_each_rsnd_clkin(clk, adg, i) - if (rate == adg->clkin_rate[i]) - return sel_table[i]; - - /* - * find divided clock from BRGA/BRGB - */ - if (rate == adg->brg_rate[ADG_HZ_441]) - return 0x10; - - if (rate == adg->brg_rate[ADG_HZ_48]) - return 0x20; - - return -EIO; -} - -int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod) -{ - rsnd_adg_set_ssi_clk(ssi_mod, 0); - - return 0; -} - -int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int data; - u32 ckr = 0; - - data = rsnd_adg_clk_query(priv, rate); - if (data < 0) - return data; - - rsnd_adg_set_ssi_clk(ssi_mod, data); - - if (0 == (rate % 8000)) - ckr = 0x80000000; /* BRGB output = 48kHz */ - - rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr); - - dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n", - (ckr) ? 'B' : 'A', - (ckr) ? adg->brg_rate[ADG_HZ_48] : - adg->brg_rate[ADG_HZ_441]); - - return 0; -} - -void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) -{ - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - struct clk *clk; - int i; - - if (enable) { - rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr); - rsnd_mod_write(adg_mod, BRRA, adg->brga); - rsnd_mod_write(adg_mod, BRRB, adg->brgb); - } - - for_each_rsnd_clkin(clk, adg, i) { - if (enable) { - clk_prepare_enable(clk); - - /* - * We shouldn't use clk_get_rate() under - * atomic context. Let's keep it when - * rsnd_adg_clk_enable() was called - */ - adg->clkin_rate[i] = clk_get_rate(clk); - } else { - clk_disable_unprepare(clk); - } - } -} - -static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv, - const char * const name, - const char *parent) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct clk *clk; - - clk = clk_register_fixed_rate(dev, name, parent, 0, 0); - if (IS_ERR_OR_NULL(clk)) { - dev_err(dev, "create null clk error\n"); - return ERR_CAST(clk); - } - - return clk; -} - -static struct clk *rsnd_adg_null_clk_get(struct rsnd_priv *priv) -{ - struct rsnd_adg *adg = priv->adg; - - if (!adg->null_clk) { - static const char * const name = "rsnd_adg_null"; - - adg->null_clk = rsnd_adg_create_null_clk(priv, name, NULL); - } - - return adg->null_clk; -} - -static void rsnd_adg_null_clk_clean(struct rsnd_priv *priv) -{ - struct rsnd_adg *adg = priv->adg; - - if (adg->null_clk) - clk_unregister_fixed_rate(adg->null_clk); -} - -static int rsnd_adg_get_clkin(struct rsnd_priv *priv) -{ - struct rsnd_adg *adg = priv->adg; - struct device *dev = rsnd_priv_to_dev(priv); - struct clk *clk; - const char * const *clkin_name; - int clkin_size; - int i; - - clkin_name = clkin_name_gen2; - clkin_size = ARRAY_SIZE(clkin_name_gen2); - if (rsnd_is_gen4(priv)) { - clkin_name = clkin_name_gen4; - clkin_size = ARRAY_SIZE(clkin_name_gen4); - } - - for (i = 0; i < clkin_size; i++) { - clk = devm_clk_get(dev, clkin_name[i]); - - if (IS_ERR_OR_NULL(clk)) - clk = rsnd_adg_null_clk_get(priv); - if (IS_ERR_OR_NULL(clk)) - goto err; - - adg->clkin[i] = clk; - } - - adg->clkin_size = clkin_size; - - return 0; - -err: - dev_err(dev, "adg clock IN get failed\n"); - - rsnd_adg_null_clk_clean(priv); - - return -EIO; -} - -static void rsnd_adg_unregister_clkout(struct rsnd_priv *priv) -{ - struct rsnd_adg *adg = priv->adg; - struct clk *clk; - int i; - - for_each_rsnd_clkout(clk, adg, i) - clk_unregister_fixed_rate(clk); -} - -static int rsnd_adg_get_clkout(struct rsnd_priv *priv) -{ - struct rsnd_adg *adg = priv->adg; - struct clk *clk; - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np = dev->of_node; - struct property *prop; - u32 ckr, brgx, brga, brgb; - u32 req_rate[ADG_HZ_SIZE] = {}; - uint32_t count = 0; - unsigned long req_Hz[ADG_HZ_SIZE]; - int clkout_size; - int i, req_size; - int approximate = 0; - const char *parent_clk_name = NULL; - const char * const *clkout_name; - int brg_table[] = { - [CLKA] = 0x0, - [CLKB] = 0x1, - [CLKC] = 0x4, - [CLKI] = 0x2, - }; - - ckr = 0; - brga = 0xff; /* default */ - brgb = 0xff; /* default */ - - /* - * ADG supports BRRA/BRRB output only - * this means all clkout0/1/2/3 will be same rate - */ - prop = of_find_property(np, "clock-frequency", NULL); - if (!prop) - goto rsnd_adg_get_clkout_end; - - req_size = prop->length / sizeof(u32); - if (req_size > ADG_HZ_SIZE) { - dev_err(dev, "too many clock-frequency\n"); - return -EINVAL; - } - - of_property_read_u32_array(np, "clock-frequency", req_rate, req_size); - req_Hz[ADG_HZ_48] = 0; - req_Hz[ADG_HZ_441] = 0; - for (i = 0; i < req_size; i++) { - if (0 == (req_rate[i] % 44100)) - req_Hz[ADG_HZ_441] = req_rate[i]; - if (0 == (req_rate[i] % 48000)) - req_Hz[ADG_HZ_48] = req_rate[i]; - } - - /* - * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC - * have 44.1kHz or 48kHz base clocks for now. - * - * SSI itself can divide parent clock by 1/1 - 1/16 - * see - * rsnd_adg_ssi_clk_try_start() - * rsnd_ssi_master_clk_start() - */ - - /* - * [APPROXIMATE] - * - * clk_i (internal clock) can't create accurate rate, it will be approximate rate. - * - * - * - * clk_i needs x2 of required maximum rate. - * see - * - Minimum division of BRRA/BRRB - * - rsnd_ssi_clk_query() - * - * Sample Settings for TDM 8ch, 32bit width - * - * 8(ch) x 32(bit) x 44100(Hz) x 2 = 22579200 - * 8(ch) x 32(bit) x 48000(Hz) x 2 = 24576000 - * - * clock-frequency = <22579200 24576000>; - */ - for_each_rsnd_clkin(clk, adg, i) { - u32 rate, div; - - rate = clk_get_rate(clk); - - if (0 == rate) /* not used */ - continue; - - /* BRGA */ - - if (i == CLKI) - /* see [APPROXIMATE] */ - rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_441]) * req_Hz[ADG_HZ_441]; - if (!adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441] && (0 == rate % 44100)) { - div = rate / req_Hz[ADG_HZ_441]; - brgx = rsnd_adg_calculate_brgx(div); - if (BRRx_MASK(brgx) == brgx) { - brga = brgx; - adg->brg_rate[ADG_HZ_441] = rate / div; - ckr |= brg_table[i] << 20; - if (req_Hz[ADG_HZ_441]) - parent_clk_name = __clk_get_name(clk); - if (i == CLKI) - approximate = 1; - } - } - - /* BRGB */ - - if (i == CLKI) - /* see [APPROXIMATE] */ - rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_48]) * req_Hz[ADG_HZ_48]; - if (!adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48] && (0 == rate % 48000)) { - div = rate / req_Hz[ADG_HZ_48]; - brgx = rsnd_adg_calculate_brgx(div); - if (BRRx_MASK(brgx) == brgx) { - brgb = brgx; - adg->brg_rate[ADG_HZ_48] = rate / div; - ckr |= brg_table[i] << 16; - if (req_Hz[ADG_HZ_48]) - parent_clk_name = __clk_get_name(clk); - if (i == CLKI) - approximate = 1; - } - } - } - - if (!(adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48]) && - !(adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441])) - goto rsnd_adg_get_clkout_end; - - if (approximate) - dev_info(dev, "It uses CLK_I as approximate rate"); - - clkout_name = clkout_name_gen2; - clkout_size = ARRAY_SIZE(clkout_name_gen2); - if (rsnd_is_gen4(priv)) - clkout_size = 1; /* reuse clkout_name_gen2[] */ - - /* - * ADG supports BRRA/BRRB output only. - * this means all clkout0/1/2/3 will be * same rate - */ - - of_property_read_u32(np, "#clock-cells", &count); - /* - * for clkout - */ - if (!count) { - clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT], - parent_clk_name, 0, req_rate[0]); - if (IS_ERR_OR_NULL(clk)) - goto err; - - adg->clkout[CLKOUT] = clk; - adg->clkout_size = 1; - of_clk_add_provider(np, of_clk_src_simple_get, clk); - } - /* - * for clkout0/1/2/3 - */ - else { - for (i = 0; i < clkout_size; i++) { - clk = clk_register_fixed_rate(dev, clkout_name[i], - parent_clk_name, 0, - req_rate[0]); - if (IS_ERR_OR_NULL(clk)) - goto err; - - adg->clkout[i] = clk; - } - adg->onecell.clks = adg->clkout; - adg->onecell.clk_num = clkout_size; - adg->clkout_size = clkout_size; - of_clk_add_provider(np, of_clk_src_onecell_get, - &adg->onecell); - } - -rsnd_adg_get_clkout_end: - adg->ckr = ckr; - adg->brga = brga; - adg->brgb = brgb; - - return 0; - -err: - dev_err(dev, "adg clock OUT get failed\n"); - - rsnd_adg_unregister_clkout(priv); - - return -EIO; -} - -#if defined(DEBUG) || defined(CONFIG_DEBUG_FS) -__printf(3, 4) -static void dbg_msg(struct device *dev, struct seq_file *m, - const char *fmt, ...) -{ - char msg[128]; - va_list args; - - va_start(args, fmt); - vsnprintf(msg, sizeof(msg), fmt, args); - va_end(args); - - if (m) - seq_puts(m, msg); - else - dev_dbg(dev, "%s", msg); -} - -void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m) -{ - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct device *dev = rsnd_priv_to_dev(priv); - struct clk *clk; - int i; - - for_each_rsnd_clkin(clk, adg, i) - dbg_msg(dev, m, "%-18s : %pa : %ld\n", - __clk_get_name(clk), clk, clk_get_rate(clk)); - - dbg_msg(dev, m, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", - adg->ckr, adg->brga, adg->brgb); - dbg_msg(dev, m, "BRGA (for 44100 base) = %d\n", adg->brg_rate[ADG_HZ_441]); - dbg_msg(dev, m, "BRGB (for 48000 base) = %d\n", adg->brg_rate[ADG_HZ_48]); - - /* - * Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start() - * by BRGCKR::BRGCKR_31 - */ - for_each_rsnd_clkout(clk, adg, i) - dbg_msg(dev, m, "%-18s : %pa : %ld\n", - __clk_get_name(clk), clk, clk_get_rate(clk)); -} -#else -#define rsnd_adg_clk_dbg_info(priv, m) -#endif - -int rsnd_adg_probe(struct rsnd_priv *priv) -{ - struct rsnd_adg *adg; - struct device *dev = rsnd_priv_to_dev(priv); - int ret; - - adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); - if (!adg) - return -ENOMEM; - - ret = rsnd_mod_init(priv, &adg->mod, &adg_ops, - NULL, 0, 0); - if (ret) - return ret; - - priv->adg = adg; - - ret = rsnd_adg_get_clkin(priv); - if (ret) - return ret; - - ret = rsnd_adg_get_clkout(priv); - if (ret) - return ret; - - rsnd_adg_clk_enable(priv); - rsnd_adg_clk_dbg_info(priv, NULL); - - return 0; -} - -void rsnd_adg_remove(struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np = dev->of_node; - - rsnd_adg_unregister_clkout(priv); - - of_clk_del_provider(np); - - rsnd_adg_clk_disable(priv); - - /* It should be called after rsnd_adg_clk_disable() */ - rsnd_adg_null_clk_clean(priv); -} diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c deleted file mode 100644 index 8d9a1e345a22..000000000000 --- a/sound/soc/sh/rcar/cmd.c +++ /dev/null @@ -1,191 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Renesas R-Car CMD support -// -// Copyright (C) 2015 Renesas Solutions Corp. -// Kuninori Morimoto - -#include "rsnd.h" - -struct rsnd_cmd { - struct rsnd_mod mod; -}; - -#define CMD_NAME "cmd" - -#define rsnd_cmd_nr(priv) ((priv)->cmd_nr) -#define for_each_rsnd_cmd(pos, priv, i) \ - for ((i) = 0; \ - ((i) < rsnd_cmd_nr(priv)) && \ - ((pos) = (struct rsnd_cmd *)(priv)->cmd + i); \ - i++) - -static int rsnd_cmd_init(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); - struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); - struct device *dev = rsnd_priv_to_dev(priv); - u32 data; - static const u32 path[] = { - [1] = 1 << 0, - [5] = 1 << 8, - [6] = 1 << 12, - [9] = 1 << 15, - }; - - if (!mix && !dvc) - return 0; - - if (ARRAY_SIZE(path) < rsnd_mod_id(mod) + 1) - return -ENXIO; - - if (mix) { - struct rsnd_dai *rdai; - int i; - - /* - * it is assuming that integrater is well understanding about - * data path. Here doesn't check impossible connection, - * like src2 + src5 - */ - data = 0; - for_each_rsnd_dai(rdai, priv, i) { - struct rsnd_dai_stream *tio = &rdai->playback; - struct rsnd_mod *src = rsnd_io_to_mod_src(tio); - - if (mix == rsnd_io_to_mod_mix(tio)) - data |= path[rsnd_mod_id(src)]; - - tio = &rdai->capture; - src = rsnd_io_to_mod_src(tio); - if (mix == rsnd_io_to_mod_mix(tio)) - data |= path[rsnd_mod_id(src)]; - } - - } else { - struct rsnd_mod *src = rsnd_io_to_mod_src(io); - - static const u8 cmd_case[] = { - [0] = 0x3, - [1] = 0x3, - [2] = 0x4, - [3] = 0x1, - [4] = 0x2, - [5] = 0x4, - [6] = 0x1, - [9] = 0x2, - }; - - if (unlikely(!src)) - return -EIO; - - data = path[rsnd_mod_id(src)] | - cmd_case[rsnd_mod_id(src)] << 16; - } - - dev_dbg(dev, "ctu/mix path = 0x%08x\n", data); - - rsnd_mod_write(mod, CMD_ROUTE_SLCT, data); - rsnd_mod_write(mod, CMD_BUSIF_MODE, rsnd_get_busif_shift(io, mod) | 1); - rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); - - rsnd_adg_set_cmd_timsel_gen2(mod, io); - - return 0; -} - -static int rsnd_cmd_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - rsnd_mod_write(mod, CMD_CTRL, 0x10); - - return 0; -} - -static int rsnd_cmd_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - rsnd_mod_write(mod, CMD_CTRL, 0); - - return 0; -} - -#ifdef CONFIG_DEBUG_FS -static void rsnd_cmd_debug_info(struct seq_file *m, - struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, - 0x180 + rsnd_mod_id_raw(mod) * 0x20, 0x30); -} -#define DEBUG_INFO .debug_info = rsnd_cmd_debug_info -#else -#define DEBUG_INFO -#endif - -static struct rsnd_mod_ops rsnd_cmd_ops = { - .name = CMD_NAME, - .init = rsnd_cmd_init, - .start = rsnd_cmd_start, - .stop = rsnd_cmd_stop, - .get_status = rsnd_mod_get_status, - DEBUG_INFO -}; - -static struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id) -{ - if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv))) - id = 0; - - return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id); -} -int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id) -{ - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct rsnd_mod *mod = rsnd_cmd_mod_get(priv, id); - - return rsnd_dai_connect(mod, io, mod->type); -} - -int rsnd_cmd_probe(struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_cmd *cmd; - int i, nr; - - /* same number as DVC */ - nr = priv->dvc_nr; - if (!nr) - return 0; - - cmd = devm_kcalloc(dev, nr, sizeof(*cmd), GFP_KERNEL); - if (!cmd) - return -ENOMEM; - - priv->cmd_nr = nr; - priv->cmd = cmd; - - for_each_rsnd_cmd(cmd, priv, i) { - int ret = rsnd_mod_init(priv, rsnd_mod_get(cmd), - &rsnd_cmd_ops, NULL, - RSND_MOD_CMD, i); - if (ret) - return ret; - } - - return 0; -} - -void rsnd_cmd_remove(struct rsnd_priv *priv) -{ - struct rsnd_cmd *cmd; - int i; - - for_each_rsnd_cmd(cmd, priv, i) { - rsnd_mod_quit(rsnd_mod_get(cmd)); - } -} diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c deleted file mode 100644 index c32e88d6a141..000000000000 --- a/sound/soc/sh/rcar/core.c +++ /dev/null @@ -1,2112 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Renesas R-Car SRU/SCU/SSIU/SSI support -// -// Copyright (C) 2013 Renesas Solutions Corp. -// Kuninori Morimoto -// -// Based on fsi.c -// Kuninori Morimoto - -/* - * Renesas R-Car sound device structure - * - * Gen1 - * - * SRU : Sound Routing Unit - * - SRC : Sampling Rate Converter - * - CMD - * - CTU : Channel Count Conversion Unit - * - MIX : Mixer - * - DVC : Digital Volume and Mute Function - * - SSI : Serial Sound Interface - * - * Gen2 - * - * SCU : Sampling Rate Converter Unit - * - SRC : Sampling Rate Converter - * - CMD - * - CTU : Channel Count Conversion Unit - * - MIX : Mixer - * - DVC : Digital Volume and Mute Function - * SSIU : Serial Sound Interface Unit - * - SSI : Serial Sound Interface - */ - -/* - * driver data Image - * - * rsnd_priv - * | - * | ** this depends on Gen1/Gen2 - * | - * +- gen - * | - * | ** these depend on data path - * | ** gen and platform data control it - * | - * +- rdai[0] - * | | sru ssiu ssi - * | +- playback -> [mod] -> [mod] -> [mod] -> ... - * | | - * | | sru ssiu ssi - * | +- capture -> [mod] -> [mod] -> [mod] -> ... - * | - * +- rdai[1] - * | | sru ssiu ssi - * | +- playback -> [mod] -> [mod] -> [mod] -> ... - * | | - * | | sru ssiu ssi - * | +- capture -> [mod] -> [mod] -> [mod] -> ... - * ... - * | - * | ** these control ssi - * | - * +- ssi - * | | - * | +- ssi[0] - * | +- ssi[1] - * | +- ssi[2] - * | ... - * | - * | ** these control src - * | - * +- src - * | - * +- src[0] - * +- src[1] - * +- src[2] - * ... - * - * - * for_each_rsnd_dai(xx, priv, xx) - * rdai[0] => rdai[1] => rdai[2] => ... - * - * for_each_rsnd_mod(xx, rdai, xx) - * [mod] => [mod] => [mod] => ... - * - * rsnd_dai_call(xxx, fn ) - * [mod]->fn() -> [mod]->fn() -> [mod]->fn()... - * - */ - -#include -#include -#include "rsnd.h" - -#define RSND_RATES SNDRV_PCM_RATE_8000_192000 -#define RSND_FMTS (SNDRV_PCM_FMTBIT_S8 |\ - SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE) - -static const struct of_device_id rsnd_of_match[] = { - { .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 }, - { .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 }, - { .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN3 }, - { .compatible = "renesas,rcar_sound-gen4", .data = (void *)RSND_GEN4 }, - /* Special Handling */ - { .compatible = "renesas,rcar_sound-r8a77990", .data = (void *)(RSND_GEN3 | RSND_SOC_E) }, - {}, -}; -MODULE_DEVICE_TABLE(of, rsnd_of_match); - -/* - * rsnd_mod functions - */ -void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) -{ - if (mod->type != type) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_warn(dev, "%s is not your expected module\n", - rsnd_mod_name(mod)); - } -} - -struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - if (!mod || !mod->ops || !mod->ops->dma_req) - return NULL; - - return mod->ops->dma_req(io, mod); -} - -#define MOD_NAME_NUM 5 -#define MOD_NAME_SIZE 16 -char *rsnd_mod_name(struct rsnd_mod *mod) -{ - static char names[MOD_NAME_NUM][MOD_NAME_SIZE]; - static int num; - char *name = names[num]; - - num++; - if (num >= MOD_NAME_NUM) - num = 0; - - /* - * Let's use same char to avoid pointlessness memory - * Thus, rsnd_mod_name() should be used immediately - * Don't keep pointer - */ - if ((mod)->ops->id_sub) { - snprintf(name, MOD_NAME_SIZE, "%s[%d%d]", - mod->ops->name, - rsnd_mod_id(mod), - rsnd_mod_id_sub(mod)); - } else { - snprintf(name, MOD_NAME_SIZE, "%s[%d]", - mod->ops->name, - rsnd_mod_id(mod)); - } - - return name; -} - -u32 *rsnd_mod_get_status(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - enum rsnd_mod_type type) -{ - return &mod->status; -} - -int rsnd_mod_id_raw(struct rsnd_mod *mod) -{ - return mod->id; -} - -int rsnd_mod_id(struct rsnd_mod *mod) -{ - if ((mod)->ops->id) - return (mod)->ops->id(mod); - - return rsnd_mod_id_raw(mod); -} - -int rsnd_mod_id_sub(struct rsnd_mod *mod) -{ - if ((mod)->ops->id_sub) - return (mod)->ops->id_sub(mod); - - return 0; -} - -int rsnd_mod_init(struct rsnd_priv *priv, - struct rsnd_mod *mod, - struct rsnd_mod_ops *ops, - struct clk *clk, - enum rsnd_mod_type type, - int id) -{ - int ret = clk_prepare(clk); - - if (ret) - return ret; - - mod->id = id; - mod->ops = ops; - mod->type = type; - mod->clk = clk; - mod->priv = priv; - - return 0; -} - -void rsnd_mod_quit(struct rsnd_mod *mod) -{ - clk_unprepare(mod->clk); - mod->clk = NULL; -} - -void rsnd_mod_interrupt(struct rsnd_mod *mod, - void (*callback)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io)) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai *rdai; - int i; - - for_each_rsnd_dai(rdai, priv, i) { - struct rsnd_dai_stream *io = &rdai->playback; - - if (mod == io->mod[mod->type]) - callback(mod, io); - - io = &rdai->capture; - if (mod == io->mod[mod->type]) - callback(mod, io); - } -} - -int rsnd_io_is_working(struct rsnd_dai_stream *io) -{ - /* see rsnd_dai_stream_init/quit() */ - if (io->substream) - return snd_pcm_running(io->substream); - - return 0; -} - -int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io, - struct snd_pcm_hw_params *params) -{ - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - - /* - * params will be added when refine - * see - * __rsnd_soc_hw_rule_rate() - * __rsnd_soc_hw_rule_channels() - */ - if (params) - return params_channels(params); - else if (runtime) - return runtime->channels; - return 0; -} - -int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, - struct snd_pcm_hw_params *params) -{ - int chan = rsnd_runtime_channel_original_with_params(io, params); - struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io); - - if (ctu_mod) { - u32 converted_chan = rsnd_io_converted_chan(io); - - /* - * !! Note !! - * - * converted_chan will be used for CTU, - * or TDM Split mode. - * User shouldn't use CTU with TDM Split mode. - */ - if (rsnd_runtime_is_tdm_split(io)) { - struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); - - dev_err(dev, "CTU and TDM Split should be used\n"); - } - - if (converted_chan) - return converted_chan; - } - - return chan; -} - -int rsnd_channel_normalization(int chan) -{ - if (WARN_ON((chan > 8) || (chan < 0))) - return 0; - - /* TDM Extend Mode needs 8ch */ - if (chan == 6) - chan = 8; - - return chan; -} - -int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, - struct snd_pcm_hw_params *params) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - int chan = rsnd_io_is_play(io) ? - rsnd_runtime_channel_after_ctu_with_params(io, params) : - rsnd_runtime_channel_original_with_params(io, params); - - /* Use Multi SSI */ - if (rsnd_runtime_is_multi_ssi(io)) - chan /= rsnd_rdai_ssi_lane_get(rdai); - - return rsnd_channel_normalization(chan); -} - -int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - int lane = rsnd_rdai_ssi_lane_get(rdai); - int chan = rsnd_io_is_play(io) ? - rsnd_runtime_channel_after_ctu(io) : - rsnd_runtime_channel_original(io); - - return (chan > 2) && (lane > 1); -} - -int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io) -{ - return rsnd_runtime_channel_for_ssi(io) >= 6; -} - -int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io) -{ - return !!rsnd_flags_has(io, RSND_STREAM_TDM_SPLIT); -} - -/* - * ADINR function - */ -u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct device *dev = rsnd_priv_to_dev(priv); - - switch (snd_pcm_format_width(runtime->format)) { - case 8: - return 16 << 16; - case 16: - return 8 << 16; - case 24: - return 0 << 16; - } - - dev_warn(dev, "not supported sample bits\n"); - - return 0; -} - -/* - * DALIGN function - */ -u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) -{ - static const u32 dalign_values[8] = { - 0x76543210, 0x00000032, 0x00007654, 0x00000076, - 0xfedcba98, 0x000000ba, 0x0000fedc, 0x000000fe, - }; - int id = 0; - struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); - struct rsnd_mod *target; - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 dalign; - - /* - * *Hardware* L/R and *Software* L/R are inverted for 16bit data. - * 31..16 15...0 - * HW: [L ch] [R ch] - * SW: [R ch] [L ch] - * We need to care about inversion timing to control - * Playback/Capture correctly. - * The point is [DVC] needs *Hardware* L/R, [MEM] needs *Software* L/R - * - * sL/R : software L/R - * hL/R : hardware L/R - * (*) : conversion timing - * - * Playback - * sL/R (*) hL/R hL/R hL/R hL/R hL/R - * [MEM] -> [SRC] -> [DVC] -> [CMD] -> [SSIU] -> [SSI] -> codec - * - * Capture - * hL/R hL/R hL/R hL/R hL/R (*) sL/R - * codec -> [SSI] -> [SSIU] -> [SRC] -> [DVC] -> [CMD] -> [MEM] - */ - if (rsnd_io_is_play(io)) { - struct rsnd_mod *src = rsnd_io_to_mod_src(io); - - target = src ? src : ssiu; - } else { - struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io); - - target = cmd ? cmd : ssiu; - } - - if (mod == ssiu) - id = rsnd_mod_id_sub(mod); - - dalign = dalign_values[id]; - - if (mod == target && snd_pcm_format_width(runtime->format) == 16) { - /* Target mod needs inverted DALIGN when 16bit */ - dalign = (dalign & 0xf0f0f0f0) >> 4 | - (dalign & 0x0f0f0f0f) << 4; - } - - return dalign; -} - -u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod) -{ - static const enum rsnd_mod_type playback_mods[] = { - RSND_MOD_SRC, - RSND_MOD_CMD, - RSND_MOD_SSIU, - }; - static const enum rsnd_mod_type capture_mods[] = { - RSND_MOD_CMD, - RSND_MOD_SRC, - RSND_MOD_SSIU, - }; - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_mod *tmod = NULL; - const enum rsnd_mod_type *mods = - rsnd_io_is_play(io) ? - playback_mods : capture_mods; - int i; - - /* - * This is needed for 24bit data - * We need to shift 8bit - * - * Linux 24bit data is located as 0x00****** - * HW 24bit data is located as 0x******00 - * - */ - if (snd_pcm_format_width(runtime->format) != 24) - return 0; - - for (i = 0; i < ARRAY_SIZE(playback_mods); i++) { - tmod = rsnd_io_to_mod(io, mods[i]); - if (tmod) - break; - } - - if (tmod != mod) - return 0; - - if (rsnd_io_is_play(io)) - return (0 << 20) | /* shift to Left */ - (8 << 16); /* 8bit */ - else - return (1 << 20) | /* shift to Right */ - (8 << 16); /* 8bit */ -} - -/* - * rsnd_dai functions - */ -struct rsnd_mod *rsnd_mod_next(int *iterator, - struct rsnd_dai_stream *io, - enum rsnd_mod_type *array, - int array_size) -{ - int max = array ? array_size : RSND_MOD_MAX; - - for (; *iterator < max; (*iterator)++) { - enum rsnd_mod_type type = (array) ? array[*iterator] : *iterator; - struct rsnd_mod *mod = rsnd_io_to_mod(io, type); - - if (mod) - return mod; - } - - return NULL; -} - -static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = { - { - /* CAPTURE */ - RSND_MOD_AUDMAPP, - RSND_MOD_AUDMA, - RSND_MOD_DVC, - RSND_MOD_MIX, - RSND_MOD_CTU, - RSND_MOD_CMD, - RSND_MOD_SRC, - RSND_MOD_SSIU, - RSND_MOD_SSIM3, - RSND_MOD_SSIM2, - RSND_MOD_SSIM1, - RSND_MOD_SSIP, - RSND_MOD_SSI, - }, { - /* PLAYBACK */ - RSND_MOD_AUDMAPP, - RSND_MOD_AUDMA, - RSND_MOD_SSIM3, - RSND_MOD_SSIM2, - RSND_MOD_SSIM1, - RSND_MOD_SSIP, - RSND_MOD_SSI, - RSND_MOD_SSIU, - RSND_MOD_DVC, - RSND_MOD_MIX, - RSND_MOD_CTU, - RSND_MOD_CMD, - RSND_MOD_SRC, - }, -}; - -static int rsnd_status_update(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, enum rsnd_mod_type type, - int shift, int add, int timing) -{ - u32 *status = mod->ops->get_status(mod, io, type); - u32 mask = 0xF << shift; - u8 val = (*status >> shift) & 0xF; - u8 next_val = (val + add) & 0xF; - int func_call = (val == timing); - - /* no status update */ - if (add == 0 || shift == 28) - return 1; - - if (next_val == 0xF) /* underflow case */ - func_call = -1; - else - *status = (*status & ~mask) + (next_val << shift); - - return func_call; -} - -#define rsnd_dai_call(fn, io, param...) \ -({ \ - struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); \ - struct rsnd_mod *mod; \ - int is_play = rsnd_io_is_play(io); \ - int ret = 0, i; \ - enum rsnd_mod_type *types = rsnd_mod_sequence[is_play]; \ - for_each_rsnd_mod_arrays(i, mod, io, types, RSND_MOD_MAX) { \ - int tmp = 0; \ - int func_call = rsnd_status_update(io, mod, types[i], \ - __rsnd_mod_shift_##fn, \ - __rsnd_mod_add_##fn, \ - __rsnd_mod_call_##fn); \ - if (func_call > 0 && (mod)->ops->fn) \ - tmp = (mod)->ops->fn(mod, io, param); \ - if (unlikely(func_call < 0) || \ - unlikely(tmp && (tmp != -EPROBE_DEFER))) \ - dev_err(dev, "%s : %s error (%d, %d)\n", \ - rsnd_mod_name(mod), #fn, tmp, func_call);\ - ret |= tmp; \ - } \ - ret; \ -}) - -int rsnd_dai_connect(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - enum rsnd_mod_type type) -{ - struct rsnd_priv *priv; - struct device *dev; - - if (!mod) - return -EIO; - - if (io->mod[type] == mod) - return 0; - - if (io->mod[type]) - return -EINVAL; - - priv = rsnd_mod_to_priv(mod); - dev = rsnd_priv_to_dev(priv); - - io->mod[type] = mod; - - dev_dbg(dev, "%s is connected to io (%s)\n", - rsnd_mod_name(mod), - rsnd_io_is_play(io) ? "Playback" : "Capture"); - - return 0; -} - -static void rsnd_dai_disconnect(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - enum rsnd_mod_type type) -{ - io->mod[type] = NULL; -} - -int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai, - int max_channels) -{ - if (max_channels > 0) - rdai->max_channels = max_channels; - - return rdai->max_channels; -} - -int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai, - int ssi_lane) -{ - if (ssi_lane > 0) - rdai->ssi_lane = ssi_lane; - - return rdai->ssi_lane; -} - -int rsnd_rdai_width_ctrl(struct rsnd_dai *rdai, int width) -{ - if (width > 0) - rdai->chan_width = width; - - return rdai->chan_width; -} - -struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id) -{ - if ((id < 0) || (id >= rsnd_rdai_nr(priv))) - return NULL; - - return priv->rdai + id; -} - -static struct snd_soc_dai_driver -*rsnd_daidrv_get(struct rsnd_priv *priv, int id) -{ - if ((id < 0) || (id >= rsnd_rdai_nr(priv))) - return NULL; - - return priv->daidrv + id; -} - -#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai) -static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) -{ - struct rsnd_priv *priv = rsnd_dai_to_priv(dai); - - return rsnd_rdai_get(priv, dai->id); -} - -static void rsnd_dai_stream_init(struct rsnd_dai_stream *io, - struct snd_pcm_substream *substream) -{ - io->substream = substream; -} - -static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io) -{ - io->substream = NULL; -} - -static -struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - - return snd_soc_rtd_to_cpu(rtd, 0); -} - -static -struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai, - struct snd_pcm_substream *substream) -{ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - return &rdai->playback; - else - return &rdai->capture; -} - -static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct rsnd_priv *priv = rsnd_dai_to_priv(dai); - struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); - struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - int ret; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - ret = rsnd_dai_call(init, io, priv); - if (ret < 0) - goto dai_trigger_end; - - ret = rsnd_dai_call(start, io, priv); - if (ret < 0) - goto dai_trigger_end; - - ret = rsnd_dai_call(irq, io, priv, 1); - if (ret < 0) - goto dai_trigger_end; - - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - ret = rsnd_dai_call(irq, io, priv, 0); - - ret |= rsnd_dai_call(stop, io, priv); - - ret |= rsnd_dai_call(quit, io, priv); - - break; - default: - ret = -EINVAL; - } - -dai_trigger_end: - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; -} - -static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -{ - struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); - - /* set clock master for audio interface */ - switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { - case SND_SOC_DAIFMT_BC_FC: - rdai->clk_master = 0; - break; - case SND_SOC_DAIFMT_BP_FP: - rdai->clk_master = 1; /* cpu is master */ - break; - default: - return -EINVAL; - } - - /* set format */ - rdai->bit_clk_inv = 0; - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - rdai->sys_delay = 0; - rdai->data_alignment = 0; - rdai->frm_clk_inv = 0; - break; - case SND_SOC_DAIFMT_LEFT_J: - case SND_SOC_DAIFMT_DSP_B: - rdai->sys_delay = 1; - rdai->data_alignment = 0; - rdai->frm_clk_inv = 1; - break; - case SND_SOC_DAIFMT_RIGHT_J: - rdai->sys_delay = 1; - rdai->data_alignment = 1; - rdai->frm_clk_inv = 1; - break; - case SND_SOC_DAIFMT_DSP_A: - rdai->sys_delay = 0; - rdai->data_alignment = 0; - rdai->frm_clk_inv = 1; - break; - } - - /* set clock inversion */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_IF: - rdai->frm_clk_inv = !rdai->frm_clk_inv; - break; - case SND_SOC_DAIFMT_IB_NF: - rdai->bit_clk_inv = !rdai->bit_clk_inv; - break; - case SND_SOC_DAIFMT_IB_IF: - rdai->bit_clk_inv = !rdai->bit_clk_inv; - rdai->frm_clk_inv = !rdai->frm_clk_inv; - break; - case SND_SOC_DAIFMT_NB_NF: - default: - break; - } - - return 0; -} - -static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, - u32 tx_mask, u32 rx_mask, - int slots, int slot_width) -{ - struct rsnd_priv *priv = rsnd_dai_to_priv(dai); - struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); - struct device *dev = rsnd_priv_to_dev(priv); - - switch (slot_width) { - case 16: - case 24: - case 32: - break; - default: - /* use default */ - /* - * Indicate warning if DT has "dai-tdm-slot-width" - * but the value was not expected. - */ - if (slot_width) - dev_warn(dev, "unsupported TDM slot width (%d), force to use default 32\n", - slot_width); - slot_width = 32; - } - - switch (slots) { - case 2: - /* TDM Split Mode */ - case 6: - case 8: - /* TDM Extend Mode */ - rsnd_rdai_channels_set(rdai, slots); - rsnd_rdai_ssi_lane_set(rdai, 1); - rsnd_rdai_width_set(rdai, slot_width); - break; - default: - dev_err(dev, "unsupported TDM slots (%d)\n", slots); - return -EINVAL; - } - - return 0; -} - -static unsigned int rsnd_soc_hw_channels_list[] = { - 2, 6, 8, -}; - -static unsigned int rsnd_soc_hw_rate_list[] = { - 8000, - 11025, - 16000, - 22050, - 32000, - 44100, - 48000, - 64000, - 88200, - 96000, - 176400, - 192000, -}; - -static int rsnd_soc_hw_rule(struct rsnd_dai *rdai, - unsigned int *list, int list_num, - struct snd_interval *baseline, struct snd_interval *iv, - struct rsnd_dai_stream *io, char *unit) -{ - struct snd_interval p; - unsigned int rate; - int i; - - snd_interval_any(&p); - p.min = UINT_MAX; - p.max = 0; - - for (i = 0; i < list_num; i++) { - - if (!snd_interval_test(iv, list[i])) - continue; - - rate = rsnd_ssi_clk_query(rdai, - baseline->min, list[i], NULL); - if (rate > 0) { - p.min = min(p.min, list[i]); - p.max = max(p.max, list[i]); - } - - rate = rsnd_ssi_clk_query(rdai, - baseline->max, list[i], NULL); - if (rate > 0) { - p.min = min(p.min, list[i]); - p.max = max(p.max, list[i]); - } - } - - /* Indicate error once if it can't handle */ - if (!rsnd_flags_has(io, RSND_HW_RULE_ERR) && (p.min > p.max)) { - struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_warn(dev, "It can't handle %d %s <-> %d %s\n", - baseline->min, unit, baseline->max, unit); - rsnd_flags_set(io, RSND_HW_RULE_ERR); - } - - return snd_interval_refine(iv, &p); -} - -static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval ic; - struct rsnd_dai_stream *io = rule->private; - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - - /* - * possible sampling rate limitation is same as - * 2ch if it supports multi ssi - * and same as 8ch if TDM 6ch (see rsnd_ssi_config_init()) - */ - ic = *ic_; - ic.min = - ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params); - - return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_rate_list, - ARRAY_SIZE(rsnd_soc_hw_rate_list), - &ic, ir, io, "ch"); -} - -static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval ic; - struct rsnd_dai_stream *io = rule->private; - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - - /* - * possible sampling rate limitation is same as - * 2ch if it supports multi ssi - * and same as 8ch if TDM 6ch (see rsnd_ssi_config_init()) - */ - ic = *ic_; - ic.min = - ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params); - - return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_channels_list, - ARRAY_SIZE(rsnd_soc_hw_channels_list), - ir, &ic, io, "Hz"); -} - -static const struct snd_pcm_hardware rsnd_pcm_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, - .buffer_bytes_max = 64 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 32, - .fifo_size = 256, -}; - -static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); - struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - struct snd_pcm_hw_constraint_list *constraint = &rdai->constraint; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int max_channels = rsnd_rdai_channels_get(rdai); - int i; - - rsnd_flags_del(io, RSND_HW_RULE_ERR); - - rsnd_dai_stream_init(io, substream); - - /* - * Channel Limitation - * It depends on Platform design - */ - constraint->list = rsnd_soc_hw_channels_list; - constraint->count = 0; - constraint->mask = 0; - - for (i = 0; i < ARRAY_SIZE(rsnd_soc_hw_channels_list); i++) { - if (rsnd_soc_hw_channels_list[i] > max_channels) - break; - constraint->count = i + 1; - } - - snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware); - - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, constraint); - - snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - - /* - * Sampling Rate / Channel Limitation - * It depends on Clock Master Mode - */ - if (rsnd_rdai_is_clk_master(rdai)) { - int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - rsnd_soc_hw_rule_rate, - is_play ? &rdai->playback : &rdai->capture, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - rsnd_soc_hw_rule_channels, - is_play ? &rdai->playback : &rdai->capture, - SNDRV_PCM_HW_PARAM_RATE, -1); - } - - return 0; -} - -static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); - struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); - struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - - /* - * call rsnd_dai_call without spinlock - */ - rsnd_dai_call(cleanup, io, priv); - - rsnd_dai_stream_quit(io); -} - -static int rsnd_soc_dai_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct rsnd_priv *priv = rsnd_dai_to_priv(dai); - struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); - struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - - return rsnd_dai_call(prepare, io, priv); -} - -static const u64 rsnd_soc_dai_formats[] = { - /* - * 1st Priority - * - * Well tested formats. - * Select below from Sound Card, not auto - * SND_SOC_DAIFMT_CBC_CFC - * SND_SOC_DAIFMT_CBP_CFP - */ - SND_SOC_POSSIBLE_DAIFMT_I2S | - SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | - SND_SOC_POSSIBLE_DAIFMT_LEFT_J | - SND_SOC_POSSIBLE_DAIFMT_NB_NF | - SND_SOC_POSSIBLE_DAIFMT_NB_IF | - SND_SOC_POSSIBLE_DAIFMT_IB_NF | - SND_SOC_POSSIBLE_DAIFMT_IB_IF, - /* - * 2nd Priority - * - * Supported, but not well tested - */ - SND_SOC_POSSIBLE_DAIFMT_DSP_A | - SND_SOC_POSSIBLE_DAIFMT_DSP_B, -}; - -static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct device_node *dai_np) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *ssiu_np = rsnd_ssiu_of_node(priv); - struct device_node *np; - int is_play = rsnd_io_is_play(io); - int i; - - if (!ssiu_np) - return; - - /* - * This driver assumes that it is TDM Split mode - * if it includes ssiu node - */ - for (i = 0;; i++) { - struct device_node *node = is_play ? - of_parse_phandle(dai_np, "playback", i) : - of_parse_phandle(dai_np, "capture", i); - - if (!node) - break; - - for_each_child_of_node(ssiu_np, np) { - if (np == node) { - rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT); - dev_dbg(dev, "%s is part of TDM Split\n", io->name); - } - } - - of_node_put(node); - } - - of_node_put(ssiu_np); -} - -static void rsnd_parse_connect_simple(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct device_node *dai_np) -{ - if (!rsnd_io_to_mod_ssi(io)) - return; - - rsnd_parse_tdm_split_mode(priv, io, dai_np); -} - -static void rsnd_parse_connect_graph(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct device_node *endpoint) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *remote_node; - - if (!rsnd_io_to_mod_ssi(io)) - return; - - remote_node = of_graph_get_remote_port_parent(endpoint); - - /* HDMI0 */ - if (strstr(remote_node->full_name, "hdmi@fead0000")) { - rsnd_flags_set(io, RSND_STREAM_HDMI0); - dev_dbg(dev, "%s connected to HDMI0\n", io->name); - } - - /* HDMI1 */ - if (strstr(remote_node->full_name, "hdmi@feae0000")) { - rsnd_flags_set(io, RSND_STREAM_HDMI1); - dev_dbg(dev, "%s connected to HDMI1\n", io->name); - } - - rsnd_parse_tdm_split_mode(priv, io, endpoint); - - of_node_put(remote_node); -} - -void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name, - struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), - struct device_node *node, - struct device_node *playback, - struct device_node *capture) -{ - struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np; - int i; - - if (!node) - return; - - i = 0; - for_each_child_of_node(node, np) { - struct rsnd_mod *mod; - - i = rsnd_node_fixed_index(dev, np, name, i); - if (i < 0) { - of_node_put(np); - break; - } - - mod = mod_get(priv, i); - - if (np == playback) - rsnd_dai_connect(mod, &rdai->playback, mod->type); - if (np == capture) - rsnd_dai_connect(mod, &rdai->capture, mod->type); - i++; - } - - of_node_put(node); -} - -int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx) -{ - char node_name[16]; - - /* - * rsnd is assuming each device nodes are sequential numbering, - * but some of them are not. - * This function adjusts index for it. - * - * ex) - * Normal case, special case - * ssi-0 - * ssi-1 - * ssi-2 - * ssi-3 ssi-3 - * ssi-4 ssi-4 - * ... - * - * assume Max 64 node - */ - for (; idx < 64; idx++) { - snprintf(node_name, sizeof(node_name), "%s-%d", name, idx); - - if (strncmp(node_name, of_node_full_name(node), sizeof(node_name)) == 0) - return idx; - } - - dev_err(dev, "strange node numbering (%s)", - of_node_full_name(node)); - return -EINVAL; -} - -int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np; - int i; - - i = 0; - for_each_child_of_node(node, np) { - i = rsnd_node_fixed_index(dev, np, name, i); - if (i < 0) { - of_node_put(np); - return 0; - } - i++; - } - - return i; -} - -static struct device_node* - rsnd_pick_endpoint_node_for_ports(struct device_node *e_ports, - struct device_node *e_port) -{ - if (of_node_name_eq(e_ports, "ports")) - return e_ports; - - if (of_node_name_eq(e_ports, "port")) - return e_port; - - return NULL; -} - -static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np = dev->of_node; - struct device_node *ports, *node; - int nr = 0; - int i = 0; - - *is_graph = 0; - - /* - * parse both previous dai (= rcar_sound,dai), and - * graph dai (= ports/port) - */ - - /* - * Simple-Card - */ - node = of_get_child_by_name(np, RSND_NODE_DAI); - if (!node) - goto audio_graph; - - of_node_put(node); - - for_each_child_of_node(np, node) { - if (!of_node_name_eq(node, RSND_NODE_DAI)) - continue; - - priv->component_dais[i] = of_get_child_count(node); - nr += priv->component_dais[i]; - i++; - if (i >= RSND_MAX_COMPONENT) { - dev_info(dev, "reach to max component\n"); - of_node_put(node); - break; - } - } - - return nr; - -audio_graph: - /* - * Audio-Graph-Card - */ - for_each_child_of_node(np, ports) { - node = rsnd_pick_endpoint_node_for_ports(ports, np); - if (!node) - continue; - priv->component_dais[i] = of_graph_get_endpoint_count(node); - nr += priv->component_dais[i]; - i++; - if (i >= RSND_MAX_COMPONENT) { - dev_info(dev, "reach to max component\n"); - of_node_put(ports); - break; - } - } - - *is_graph = 1; - - return nr; -} - - -#define PREALLOC_BUFFER (32 * 1024) -#define PREALLOC_BUFFER_MAX (32 * 1024) - -static int rsnd_preallocate_pages(struct snd_soc_pcm_runtime *rtd, - struct rsnd_dai_stream *io, - int stream) -{ - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct device *dev = rsnd_priv_to_dev(priv); - struct snd_pcm_substream *substream; - - /* - * use Audio-DMAC dev if we can use IPMMU - * see - * rsnd_dmaen_attach() - */ - if (io->dmac_dev) - dev = io->dmac_dev; - - for (substream = rtd->pcm->streams[stream].substream; - substream; - substream = substream->next) { - snd_pcm_set_managed_buffer(substream, - SNDRV_DMA_TYPE_DEV, - dev, - PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); - } - - return 0; -} - -static int rsnd_soc_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) -{ - struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); - int ret; - - ret = rsnd_dai_call(pcm_new, &rdai->playback, rtd); - if (ret) - return ret; - - ret = rsnd_dai_call(pcm_new, &rdai->capture, rtd); - if (ret) - return ret; - - ret = rsnd_preallocate_pages(rtd, &rdai->playback, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - return ret; - - ret = rsnd_preallocate_pages(rtd, &rdai->capture, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - return ret; - - return 0; -} - -static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { - .pcm_new = rsnd_soc_dai_pcm_new, - .startup = rsnd_soc_dai_startup, - .shutdown = rsnd_soc_dai_shutdown, - .trigger = rsnd_soc_dai_trigger, - .set_fmt = rsnd_soc_dai_set_fmt, - .set_tdm_slot = rsnd_soc_set_dai_tdm_slot, - .prepare = rsnd_soc_dai_prepare, - .auto_selectable_formats = rsnd_soc_dai_formats, - .num_auto_selectable_formats = ARRAY_SIZE(rsnd_soc_dai_formats), -}; - -static void __rsnd_dai_probe(struct rsnd_priv *priv, - struct device_node *dai_np, - struct device_node *node_np, - uint32_t node_arg, - int dai_i) -{ - struct rsnd_dai_stream *io_playback; - struct rsnd_dai_stream *io_capture; - struct snd_soc_dai_driver *drv; - struct rsnd_dai *rdai; - struct device *dev = rsnd_priv_to_dev(priv); - int playback_exist = 0, capture_exist = 0; - int io_i; - - rdai = rsnd_rdai_get(priv, dai_i); - drv = rsnd_daidrv_get(priv, dai_i); - io_playback = &rdai->playback; - io_capture = &rdai->capture; - - snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); - - /* for multi Component */ - rdai->dai_args.np = node_np; - rdai->dai_args.args_count = 1; - rdai->dai_args.args[0] = node_arg; - - rdai->priv = priv; - drv->name = rdai->name; - drv->ops = &rsnd_soc_dai_ops; - drv->id = dai_i; - drv->dai_args = &rdai->dai_args; - - io_playback->rdai = rdai; - io_capture->rdai = rdai; - rsnd_rdai_channels_set(rdai, 2); /* default 2ch */ - rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */ - rsnd_rdai_width_set(rdai, 32); /* default 32bit width */ - - for (io_i = 0;; io_i++) { - struct device_node *playback = of_parse_phandle(dai_np, "playback", io_i); - struct device_node *capture = of_parse_phandle(dai_np, "capture", io_i); - - if (!playback && !capture) - break; - - if (io_i == 0) { - /* check whether playback/capture property exists */ - if (playback) - playback_exist = 1; - if (capture) - capture_exist = 1; - } - - rsnd_parse_connect_ssi(rdai, playback, capture); - rsnd_parse_connect_ssiu(rdai, playback, capture); - rsnd_parse_connect_src(rdai, playback, capture); - rsnd_parse_connect_ctu(rdai, playback, capture); - rsnd_parse_connect_mix(rdai, playback, capture); - rsnd_parse_connect_dvc(rdai, playback, capture); - - of_node_put(playback); - of_node_put(capture); - } - - if (playback_exist) { - snprintf(io_playback->name, RSND_DAI_NAME_SIZE, "DAI%d Playback", dai_i); - drv->playback.rates = RSND_RATES; - drv->playback.formats = RSND_FMTS; - drv->playback.channels_min = 2; - drv->playback.channels_max = 8; - drv->playback.stream_name = io_playback->name; - } - if (capture_exist) { - snprintf(io_capture->name, RSND_DAI_NAME_SIZE, "DAI%d Capture", dai_i); - drv->capture.rates = RSND_RATES; - drv->capture.formats = RSND_FMTS; - drv->capture.channels_min = 2; - drv->capture.channels_max = 8; - drv->capture.stream_name = io_capture->name; - } - - if (rsnd_ssi_is_pin_sharing(io_capture) || - rsnd_ssi_is_pin_sharing(io_playback)) { - /* should have symmetric_rate if pin sharing */ - drv->symmetric_rate = 1; - } - - dev_dbg(dev, "%s (%s/%s)\n", rdai->name, - rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", - rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); -} - -static int rsnd_dai_probe(struct rsnd_priv *priv) -{ - struct snd_soc_dai_driver *rdrv; - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np = dev->of_node; - struct rsnd_dai *rdai; - int nr = 0; - int is_graph; - int dai_i; - - nr = rsnd_dai_of_node(priv, &is_graph); - if (!nr) - return -EINVAL; - - rdrv = devm_kcalloc(dev, nr, sizeof(*rdrv), GFP_KERNEL); - rdai = devm_kcalloc(dev, nr, sizeof(*rdai), GFP_KERNEL); - if (!rdrv || !rdai) - return -ENOMEM; - - priv->rdai_nr = nr; - priv->daidrv = rdrv; - priv->rdai = rdai; - - /* - * parse all dai - */ - dai_i = 0; - if (is_graph) { - struct device_node *dai_np_port; - struct device_node *ports; - struct device_node *dai_np; - - for_each_child_of_node(np, ports) { - dai_np_port = rsnd_pick_endpoint_node_for_ports(ports, np); - if (!dai_np_port) - continue; - - for_each_endpoint_of_node(dai_np_port, dai_np) { - __rsnd_dai_probe(priv, dai_np, dai_np, 0, dai_i); - if (!rsnd_is_gen1(priv) && !rsnd_is_gen2(priv)) { - rdai = rsnd_rdai_get(priv, dai_i); - - rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); - rsnd_parse_connect_graph(priv, &rdai->capture, dai_np); - } - dai_i++; - } - } - } else { - struct device_node *node; - struct device_node *dai_np; - - for_each_child_of_node(np, node) { - if (!of_node_name_eq(node, RSND_NODE_DAI)) - continue; - - for_each_child_of_node(node, dai_np) { - __rsnd_dai_probe(priv, dai_np, np, dai_i, dai_i); - if (!rsnd_is_gen1(priv) && !rsnd_is_gen2(priv)) { - rdai = rsnd_rdai_get(priv, dai_i); - - rsnd_parse_connect_simple(priv, &rdai->playback, dai_np); - rsnd_parse_connect_simple(priv, &rdai->capture, dai_np); - } - dai_i++; - } - } - } - - return 0; -} - -/* - * pcm ops - */ -static int rsnd_hw_update(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); - struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); - struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - struct rsnd_priv *priv = rsnd_io_to_priv(io); - unsigned long flags; - int ret; - - spin_lock_irqsave(&priv->lock, flags); - if (hw_params) - ret = rsnd_dai_call(hw_params, io, substream, hw_params); - else - ret = rsnd_dai_call(hw_free, io, substream); - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; -} - -static int rsnd_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); - struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); - struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); - - /* - * rsnd assumes that it might be used under DPCM if user want to use - * channel / rate convert. Then, rsnd should be FE. - * And then, this function will be called *after* BE settings. - * this means, each BE already has fixuped hw_params. - * see - * dpcm_fe_dai_hw_params() - * dpcm_be_dai_hw_params() - */ - io->converted_rate = 0; - io->converted_chan = 0; - if (fe->dai_link->dynamic) { - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct device *dev = rsnd_priv_to_dev(priv); - struct snd_soc_dpcm *dpcm; - int stream = substream->stream; - - for_each_dpcm_be(fe, stream, dpcm) { - struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_pcm_hw_params *be_params = &be->dpcm[stream].hw_params; - - if (params_channels(hw_params) != params_channels(be_params)) - io->converted_chan = params_channels(be_params); - if (params_rate(hw_params) != params_rate(be_params)) - io->converted_rate = params_rate(be_params); - } - if (io->converted_chan) - dev_dbg(dev, "convert channels = %d\n", io->converted_chan); - if (io->converted_rate) { - /* - * SRC supports convert rates from params_rate(hw_params)/k_down - * to params_rate(hw_params)*k_up, where k_up is always 6, and - * k_down depends on number of channels and SRC unit. - * So all SRC units can upsample audio up to 6 times regardless - * its number of channels. And all SRC units can downsample - * 2 channel audio up to 6 times too. - */ - int k_up = 6; - int k_down = 6; - int channel; - struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - - dev_dbg(dev, "convert rate = %d\n", io->converted_rate); - - channel = io->converted_chan ? io->converted_chan : - params_channels(hw_params); - - switch (rsnd_mod_id(src_mod)) { - /* - * SRC0 can downsample 4, 6 and 8 channel audio up to 4 times. - * SRC1, SRC3 and SRC4 can downsample 4 channel audio - * up to 4 times. - * SRC1, SRC3 and SRC4 can downsample 6 and 8 channel audio - * no more than twice. - */ - case 1: - case 3: - case 4: - if (channel > 4) { - k_down = 2; - break; - } - fallthrough; - case 0: - if (channel > 2) - k_down = 4; - break; - - /* Other SRC units do not support more than 2 channels */ - default: - if (channel > 2) - return -EINVAL; - } - - if (params_rate(hw_params) > io->converted_rate * k_down) { - hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->min = - io->converted_rate * k_down; - hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->max = - io->converted_rate * k_down; - hw_params->cmask |= SNDRV_PCM_HW_PARAM_RATE; - } else if (params_rate(hw_params) * k_up < io->converted_rate) { - hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->min = - DIV_ROUND_UP(io->converted_rate, k_up); - hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->max = - DIV_ROUND_UP(io->converted_rate, k_up); - hw_params->cmask |= SNDRV_PCM_HW_PARAM_RATE; - } - - /* - * TBD: Max SRC input and output rates also depend on number - * of channels and SRC unit: - * SRC1, SRC3 and SRC4 do not support more than 128kHz - * for 6 channel and 96kHz for 8 channel audio. - * Perhaps this function should return EINVAL if the input or - * the output rate exceeds the limitation. - */ - } - } - - return rsnd_hw_update(substream, hw_params); -} - -static int rsnd_hw_free(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - return rsnd_hw_update(substream, NULL); -} - -static snd_pcm_uframes_t rsnd_pointer(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); - struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); - struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - snd_pcm_uframes_t pointer = 0; - - rsnd_dai_call(pointer, io, &pointer); - - return pointer; -} - -/* - * snd_kcontrol - */ -static int rsnd_kctrl_info(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_info *uinfo) -{ - struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl); - - if (cfg->texts) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = cfg->size; - uinfo->value.enumerated.items = cfg->max; - if (uinfo->value.enumerated.item >= cfg->max) - uinfo->value.enumerated.item = cfg->max - 1; - strscpy(uinfo->value.enumerated.name, - cfg->texts[uinfo->value.enumerated.item], - sizeof(uinfo->value.enumerated.name)); - } else { - uinfo->count = cfg->size; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = cfg->max; - uinfo->type = (cfg->max == 1) ? - SNDRV_CTL_ELEM_TYPE_BOOLEAN : - SNDRV_CTL_ELEM_TYPE_INTEGER; - } - - return 0; -} - -static int rsnd_kctrl_get(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_value *uc) -{ - struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl); - int i; - - for (i = 0; i < cfg->size; i++) - if (cfg->texts) - uc->value.enumerated.item[i] = cfg->val[i]; - else - uc->value.integer.value[i] = cfg->val[i]; - - return 0; -} - -static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_value *uc) -{ - struct rsnd_kctrl_cfg *cfg = snd_kcontrol_chip(kctrl); - int i, change = 0; - - if (!cfg->accept(cfg->io)) - return 0; - - for (i = 0; i < cfg->size; i++) { - if (cfg->texts) { - change |= (uc->value.enumerated.item[i] != cfg->val[i]); - cfg->val[i] = uc->value.enumerated.item[i]; - } else { - change |= (uc->value.integer.value[i] != cfg->val[i]); - cfg->val[i] = uc->value.integer.value[i]; - } - } - - if (change && cfg->update) - cfg->update(cfg->io, cfg->mod); - - return change; -} - -int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io) -{ - return 1; -} - -int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io) -{ - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct device *dev = rsnd_priv_to_dev(priv); - - if (!runtime) { - dev_warn(dev, "Can't update kctrl when idle\n"); - return 0; - } - - return 1; -} - -struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg) -{ - cfg->cfg.val = cfg->val; - - return &cfg->cfg; -} - -struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg) -{ - cfg->cfg.val = &cfg->val; - - return &cfg->cfg; -} - -const char * const volume_ramp_rate[] = { - "128 dB/1 step", /* 00000 */ - "64 dB/1 step", /* 00001 */ - "32 dB/1 step", /* 00010 */ - "16 dB/1 step", /* 00011 */ - "8 dB/1 step", /* 00100 */ - "4 dB/1 step", /* 00101 */ - "2 dB/1 step", /* 00110 */ - "1 dB/1 step", /* 00111 */ - "0.5 dB/1 step", /* 01000 */ - "0.25 dB/1 step", /* 01001 */ - "0.125 dB/1 step", /* 01010 = VOLUME_RAMP_MAX_MIX */ - "0.125 dB/2 steps", /* 01011 */ - "0.125 dB/4 steps", /* 01100 */ - "0.125 dB/8 steps", /* 01101 */ - "0.125 dB/16 steps", /* 01110 */ - "0.125 dB/32 steps", /* 01111 */ - "0.125 dB/64 steps", /* 10000 */ - "0.125 dB/128 steps", /* 10001 */ - "0.125 dB/256 steps", /* 10010 */ - "0.125 dB/512 steps", /* 10011 */ - "0.125 dB/1024 steps", /* 10100 */ - "0.125 dB/2048 steps", /* 10101 */ - "0.125 dB/4096 steps", /* 10110 */ - "0.125 dB/8192 steps", /* 10111 = VOLUME_RAMP_MAX_DVC */ -}; - -int rsnd_kctrl_new(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_soc_pcm_runtime *rtd, - const unsigned char *name, - int (*accept)(struct rsnd_dai_stream *io), - void (*update)(struct rsnd_dai_stream *io, - struct rsnd_mod *mod), - struct rsnd_kctrl_cfg *cfg, - const char * const *texts, - int size, - u32 max) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_kcontrol *kctrl; - struct snd_kcontrol_new knew = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = name, - .info = rsnd_kctrl_info, - .index = rtd->num, - .get = rsnd_kctrl_get, - .put = rsnd_kctrl_put, - }; - int ret; - - /* - * 1) Avoid duplicate register for DVC with MIX case - * 2) Allow duplicate register for MIX - * 3) re-register if card was rebinded - */ - list_for_each_entry(kctrl, &card->controls, list) { - struct rsnd_kctrl_cfg *c = kctrl->private_data; - - if (c == cfg) - return 0; - } - - if (size > RSND_MAX_CHANNELS) - return -EINVAL; - - kctrl = snd_ctl_new1(&knew, cfg); - if (!kctrl) - return -ENOMEM; - - ret = snd_ctl_add(card, kctrl); - if (ret < 0) - return ret; - - cfg->texts = texts; - cfg->max = max; - cfg->size = size; - cfg->accept = accept; - cfg->update = update; - cfg->card = card; - cfg->kctrl = kctrl; - cfg->io = io; - cfg->mod = mod; - - return 0; -} - -/* - * snd_soc_component - */ -static const struct snd_soc_component_driver rsnd_soc_component = { - .name = "rsnd", - .probe = rsnd_debugfs_probe, - .hw_params = rsnd_hw_params, - .hw_free = rsnd_hw_free, - .pointer = rsnd_pointer, - .legacy_dai_naming = 1, -}; - -static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, - struct rsnd_dai_stream *io) -{ - int ret; - - ret = rsnd_dai_call(probe, io, priv); - if (ret == -EAGAIN) { - struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); - struct rsnd_mod *mod; - int i; - - /* - * Fallback to PIO mode - */ - - /* - * call "remove" for SSI/SRC/DVC - * SSI will be switch to PIO mode if it was DMA mode - * see - * rsnd_dma_init() - * rsnd_ssi_fallback() - */ - rsnd_dai_call(remove, io, priv); - - /* - * remove all mod from io - * and, re connect ssi - */ - for_each_rsnd_mod(i, mod, io) - rsnd_dai_disconnect(mod, io, i); - rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI); - - /* - * fallback - */ - rsnd_dai_call(fallback, io, priv); - - /* - * retry to "probe". - * DAI has SSI which is PIO mode only now. - */ - ret = rsnd_dai_call(probe, io, priv); - } - - return ret; -} - -/* - * rsnd probe - */ -static int rsnd_probe(struct platform_device *pdev) -{ - struct rsnd_priv *priv; - struct device *dev = &pdev->dev; - struct rsnd_dai *rdai; - int (*probe_func[])(struct rsnd_priv *priv) = { - rsnd_gen_probe, - rsnd_dma_probe, - rsnd_ssi_probe, - rsnd_ssiu_probe, - rsnd_src_probe, - rsnd_ctu_probe, - rsnd_mix_probe, - rsnd_dvc_probe, - rsnd_cmd_probe, - rsnd_adg_probe, - rsnd_dai_probe, - }; - int ret, i; - int ci; - - /* - * init priv data - */ - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENODEV; - - priv->pdev = pdev; - priv->flags = (unsigned long)of_device_get_match_data(dev); - spin_lock_init(&priv->lock); - - /* - * init each module - */ - for (i = 0; i < ARRAY_SIZE(probe_func); i++) { - ret = probe_func[i](priv); - if (ret) - return ret; - } - - for_each_rsnd_dai(rdai, priv, i) { - ret = rsnd_rdai_continuance_probe(priv, &rdai->playback); - if (ret) - goto exit_snd_probe; - - ret = rsnd_rdai_continuance_probe(priv, &rdai->capture); - if (ret) - goto exit_snd_probe; - } - - dev_set_drvdata(dev, priv); - - /* - * asoc register - */ - ci = 0; - for (i = 0; priv->component_dais[i] > 0; i++) { - int nr = priv->component_dais[i]; - - ret = devm_snd_soc_register_component(dev, &rsnd_soc_component, - priv->daidrv + ci, nr); - if (ret < 0) { - dev_err(dev, "cannot snd component register\n"); - goto exit_snd_probe; - } - - ci += nr; - } - - pm_runtime_enable(dev); - - dev_info(dev, "probed\n"); - return ret; - -exit_snd_probe: - for_each_rsnd_dai(rdai, priv, i) { - rsnd_dai_call(remove, &rdai->playback, priv); - rsnd_dai_call(remove, &rdai->capture, priv); - } - - /* - * adg is very special mod which can't use rsnd_dai_call(remove), - * and it registers ADG clock on probe. - * It should be unregister if probe failed. - * Mainly it is assuming -EPROBE_DEFER case - */ - rsnd_adg_remove(priv); - - return ret; -} - -static void rsnd_remove(struct platform_device *pdev) -{ - struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); - struct rsnd_dai *rdai; - void (*remove_func[])(struct rsnd_priv *priv) = { - rsnd_ssi_remove, - rsnd_ssiu_remove, - rsnd_src_remove, - rsnd_ctu_remove, - rsnd_mix_remove, - rsnd_dvc_remove, - rsnd_cmd_remove, - rsnd_adg_remove, - }; - int i; - - pm_runtime_disable(&pdev->dev); - - for_each_rsnd_dai(rdai, priv, i) { - int ret; - - ret = rsnd_dai_call(remove, &rdai->playback, priv); - if (ret) - dev_warn(&pdev->dev, "Failed to remove playback dai #%d\n", i); - - ret = rsnd_dai_call(remove, &rdai->capture, priv); - if (ret) - dev_warn(&pdev->dev, "Failed to remove capture dai #%d\n", i); - } - - for (i = 0; i < ARRAY_SIZE(remove_func); i++) - remove_func[i](priv); -} - -static int __maybe_unused rsnd_suspend(struct device *dev) -{ - struct rsnd_priv *priv = dev_get_drvdata(dev); - - rsnd_adg_clk_disable(priv); - - return 0; -} - -static int __maybe_unused rsnd_resume(struct device *dev) -{ - struct rsnd_priv *priv = dev_get_drvdata(dev); - - rsnd_adg_clk_enable(priv); - - return 0; -} - -static const struct dev_pm_ops rsnd_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(rsnd_suspend, rsnd_resume) -}; - -static struct platform_driver rsnd_driver = { - .driver = { - .name = "rcar_sound", - .pm = &rsnd_pm_ops, - .of_match_table = rsnd_of_match, - }, - .probe = rsnd_probe, - .remove = rsnd_remove, -}; -module_platform_driver(rsnd_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Renesas R-Car audio driver"); -MODULE_AUTHOR("Kuninori Morimoto "); -MODULE_ALIAS("platform:rcar-pcm-audio"); diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c deleted file mode 100644 index a26ec7b780cd..000000000000 --- a/sound/soc/sh/rcar/ctu.c +++ /dev/null @@ -1,389 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// ctu.c -// -// Copyright (c) 2015 Kuninori Morimoto - -#include "rsnd.h" - -#define CTU_NAME_SIZE 16 -#define CTU_NAME "ctu" - -/* - * User needs to setup CTU by amixer, and its settings are - * based on below registers - * - * CTUn_CPMDR : amixser set "CTU Pass" - * CTUn_SV0xR : amixser set "CTU SV0" - * CTUn_SV1xR : amixser set "CTU SV1" - * CTUn_SV2xR : amixser set "CTU SV2" - * CTUn_SV3xR : amixser set "CTU SV3" - * - * [CTU Pass] - * 0000: default - * 0001: Connect input data of channel 0 - * 0010: Connect input data of channel 1 - * 0011: Connect input data of channel 2 - * 0100: Connect input data of channel 3 - * 0101: Connect input data of channel 4 - * 0110: Connect input data of channel 5 - * 0111: Connect input data of channel 6 - * 1000: Connect input data of channel 7 - * 1001: Connect calculated data by scale values of matrix row 0 - * 1010: Connect calculated data by scale values of matrix row 1 - * 1011: Connect calculated data by scale values of matrix row 2 - * 1100: Connect calculated data by scale values of matrix row 3 - * - * [CTU SVx] - * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07] - * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17] - * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27] - * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37] - * [Output4] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] - * [Output5] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] - * [Output6] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] - * [Output7] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] - * - * [SVxx] - * Plus Minus - * value time dB value time dB - * ----------------------------------------------------------------------- - * H'7F_FFFF 2 6 H'80_0000 2 6 - * ... - * H'40_0000 1 0 H'C0_0000 1 0 - * ... - * H'00_0001 2.38 x 10^-7 -132 - * H'00_0000 0 Mute H'FF_FFFF 2.38 x 10^-7 -132 - * - * - * Ex) Input ch -> Output ch - * 1ch -> 0ch - * 0ch -> 1ch - * - * amixer set "CTU Reset" on - * amixer set "CTU Pass" 9,10 - * amixer set "CTU SV0" 0,4194304 - * amixer set "CTU SV1" 4194304,0 - * or - * amixer set "CTU Reset" on - * amixer set "CTU Pass" 2,1 - */ - -struct rsnd_ctu { - struct rsnd_mod mod; - struct rsnd_kctrl_cfg_m pass; - struct rsnd_kctrl_cfg_m sv[4]; - struct rsnd_kctrl_cfg_s reset; - int channels; - u32 flags; -}; - -#define KCTRL_INITIALIZED (1 << 0) - -#define rsnd_ctu_nr(priv) ((priv)->ctu_nr) -#define for_each_rsnd_ctu(pos, priv, i) \ - for ((i) = 0; \ - ((i) < rsnd_ctu_nr(priv)) && \ - ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ - i++) - -#define rsnd_mod_to_ctu(_mod) \ - container_of((_mod), struct rsnd_ctu, mod) - -#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id) - -static void rsnd_ctu_activation(struct rsnd_mod *mod) -{ - rsnd_mod_write(mod, CTU_SWRSR, 0); - rsnd_mod_write(mod, CTU_SWRSR, 1); -} - -static void rsnd_ctu_halt(struct rsnd_mod *mod) -{ - rsnd_mod_write(mod, CTU_CTUIR, 1); - rsnd_mod_write(mod, CTU_SWRSR, 0); -} - -static int rsnd_ctu_probe_(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - return rsnd_cmd_attach(io, rsnd_mod_id(mod)); -} - -static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); - u32 cpmdr = 0; - u32 scmdr = 0; - int i, j; - - for (i = 0; i < RSND_MAX_CHANNELS; i++) { - u32 val = rsnd_kctrl_valm(ctu->pass, i); - - cpmdr |= val << (28 - (i * 4)); - - if ((val > 0x8) && (scmdr < (val - 0x8))) - scmdr = val - 0x8; - } - - rsnd_mod_write(mod, CTU_CTUIR, 1); - - rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io)); - - rsnd_mod_write(mod, CTU_CPMDR, cpmdr); - - rsnd_mod_write(mod, CTU_SCMDR, scmdr); - - for (i = 0; i < 4; i++) { - - if (i >= scmdr) - break; - - for (j = 0; j < RSND_MAX_CHANNELS; j++) - rsnd_mod_write(mod, CTU_SVxxR(i, j), rsnd_kctrl_valm(ctu->sv[i], j)); - } - - rsnd_mod_write(mod, CTU_CTUIR, 0); -} - -static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); - int i; - - if (!rsnd_kctrl_vals(ctu->reset)) - return; - - for (i = 0; i < RSND_MAX_CHANNELS; i++) { - rsnd_kctrl_valm(ctu->pass, i) = 0; - rsnd_kctrl_valm(ctu->sv[0], i) = 0; - rsnd_kctrl_valm(ctu->sv[1], i) = 0; - rsnd_kctrl_valm(ctu->sv[2], i) = 0; - rsnd_kctrl_valm(ctu->sv[3], i) = 0; - } - rsnd_kctrl_vals(ctu->reset) = 0; -} - -static int rsnd_ctu_init(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - int ret; - - ret = rsnd_mod_power_on(mod); - if (ret < 0) - return ret; - - rsnd_ctu_activation(mod); - - rsnd_ctu_value_init(io, mod); - - return 0; -} - -static int rsnd_ctu_quit(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - rsnd_ctu_halt(mod); - - rsnd_mod_power_off(mod); - - return 0; -} - -static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_soc_pcm_runtime *rtd) -{ - struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); - int ret; - - if (rsnd_flags_has(ctu, KCTRL_INITIALIZED)) - return 0; - - /* CTU Pass */ - ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", - rsnd_kctrl_accept_anytime, - NULL, - &ctu->pass, RSND_MAX_CHANNELS, - 0xC); - if (ret < 0) - return ret; - - /* ROW0 */ - ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", - rsnd_kctrl_accept_anytime, - NULL, - &ctu->sv[0], RSND_MAX_CHANNELS, - 0x00FFFFFF); - if (ret < 0) - return ret; - - /* ROW1 */ - ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", - rsnd_kctrl_accept_anytime, - NULL, - &ctu->sv[1], RSND_MAX_CHANNELS, - 0x00FFFFFF); - if (ret < 0) - return ret; - - /* ROW2 */ - ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", - rsnd_kctrl_accept_anytime, - NULL, - &ctu->sv[2], RSND_MAX_CHANNELS, - 0x00FFFFFF); - if (ret < 0) - return ret; - - /* ROW3 */ - ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", - rsnd_kctrl_accept_anytime, - NULL, - &ctu->sv[3], RSND_MAX_CHANNELS, - 0x00FFFFFF); - if (ret < 0) - return ret; - - /* Reset */ - ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset", - rsnd_kctrl_accept_anytime, - rsnd_ctu_value_reset, - &ctu->reset, 1); - - rsnd_flags_set(ctu, KCTRL_INITIALIZED); - - return ret; -} - -static int rsnd_ctu_id(struct rsnd_mod *mod) -{ - /* - * ctu00: -> 0, ctu01: -> 0, ctu02: -> 0, ctu03: -> 0 - * ctu10: -> 1, ctu11: -> 1, ctu12: -> 1, ctu13: -> 1 - */ - return mod->id / 4; -} - -static int rsnd_ctu_id_sub(struct rsnd_mod *mod) -{ - /* - * ctu00: -> 0, ctu01: -> 1, ctu02: -> 2, ctu03: -> 3 - * ctu10: -> 0, ctu11: -> 1, ctu12: -> 2, ctu13: -> 3 - */ - return mod->id % 4; -} - -#ifdef CONFIG_DEBUG_FS -static void rsnd_ctu_debug_info(struct seq_file *m, - struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, - 0x500 + rsnd_mod_id_raw(mod) * 0x100, 0x100); -} -#define DEBUG_INFO .debug_info = rsnd_ctu_debug_info -#else -#define DEBUG_INFO -#endif - -static struct rsnd_mod_ops rsnd_ctu_ops = { - .name = CTU_NAME, - .probe = rsnd_ctu_probe_, - .init = rsnd_ctu_init, - .quit = rsnd_ctu_quit, - .pcm_new = rsnd_ctu_pcm_new, - .get_status = rsnd_mod_get_status, - .id = rsnd_ctu_id, - .id_sub = rsnd_ctu_id_sub, - .id_cmd = rsnd_mod_id_raw, - DEBUG_INFO -}; - -struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) -{ - if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv))) - id = 0; - - return rsnd_mod_get(rsnd_ctu_get(priv, id)); -} - -int rsnd_ctu_probe(struct rsnd_priv *priv) -{ - struct device_node *node; - struct device_node *np; - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_ctu *ctu; - struct clk *clk; - char name[CTU_NAME_SIZE]; - int i, nr, ret; - - node = rsnd_ctu_of_node(priv); - if (!node) - return 0; /* not used is not error */ - - nr = of_get_child_count(node); - if (!nr) { - ret = -EINVAL; - goto rsnd_ctu_probe_done; - } - - ctu = devm_kcalloc(dev, nr, sizeof(*ctu), GFP_KERNEL); - if (!ctu) { - ret = -ENOMEM; - goto rsnd_ctu_probe_done; - } - - priv->ctu_nr = nr; - priv->ctu = ctu; - - i = 0; - ret = 0; - for_each_child_of_node(node, np) { - ctu = rsnd_ctu_get(priv, i); - - /* - * CTU00, CTU01, CTU02, CTU03 => CTU0 - * CTU10, CTU11, CTU12, CTU13 => CTU1 - */ - snprintf(name, CTU_NAME_SIZE, "%s.%d", - CTU_NAME, i / 4); - - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - of_node_put(np); - goto rsnd_ctu_probe_done; - } - - ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, - clk, RSND_MOD_CTU, i); - if (ret) { - of_node_put(np); - goto rsnd_ctu_probe_done; - } - - i++; - } - - -rsnd_ctu_probe_done: - of_node_put(node); - - return ret; -} - -void rsnd_ctu_remove(struct rsnd_priv *priv) -{ - struct rsnd_ctu *ctu; - int i; - - for_each_rsnd_ctu(ctu, priv, i) { - rsnd_mod_quit(rsnd_mod_get(ctu)); - } -} diff --git a/sound/soc/sh/rcar/debugfs.c b/sound/soc/sh/rcar/debugfs.c deleted file mode 100644 index 26d3b310b9db..000000000000 --- a/sound/soc/sh/rcar/debugfs.c +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// // Renesas R-Car debugfs support -// -// Copyright (c) 2021 Kuninori Morimoto -// -// > mount -t debugfs none /sys/kernel/debug -// > cd /sys/kernel/debug/asoc/rcar-sound/ec500000.sound/rdai{N}/ -// > cat playback/xxx -// > cat capture/xxx -// -#ifdef CONFIG_DEBUG_FS - -#include -#include "rsnd.h" - -static int rsnd_debugfs_show(struct seq_file *m, void *v) -{ - struct rsnd_dai_stream *io = m->private; - struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - int i; - - /* adg is out of mods */ - rsnd_adg_clk_dbg_info(priv, m); - - for_each_rsnd_mod(i, mod, io) { - u32 *status = mod->ops->get_status(mod, io, mod->type); - - seq_printf(m, "name: %s\n", rsnd_mod_name(mod)); - seq_printf(m, "status: %08x\n", *status); - - if (mod->ops->debug_info) - mod->ops->debug_info(m, io, mod); - } - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(rsnd_debugfs); - -void rsnd_debugfs_reg_show(struct seq_file *m, phys_addr_t _addr, - void __iomem *base, int offset, int size) -{ - int i, j; - - for (i = 0; i < size; i += 0x10) { - phys_addr_t addr = _addr + offset + i; - - seq_printf(m, "%pa:", &addr); - for (j = 0; j < 0x10; j += 0x4) - seq_printf(m, " %08x", __raw_readl(base + offset + i + j)); - seq_puts(m, "\n"); - } -} - -void rsnd_debugfs_mod_reg_show(struct seq_file *m, struct rsnd_mod *mod, - int reg_id, int offset, int size) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - - rsnd_debugfs_reg_show(m, - rsnd_gen_get_phy_addr(priv, reg_id), - rsnd_gen_get_base_addr(priv, reg_id), - offset, size); -} - -int rsnd_debugfs_probe(struct snd_soc_component *component) -{ - struct rsnd_priv *priv = dev_get_drvdata(component->dev); - struct rsnd_dai *rdai; - struct dentry *dir; - char name[64]; - int i; - - /* Gen1 is not supported */ - if (rsnd_is_gen1(priv)) - return 0; - - for_each_rsnd_dai(rdai, priv, i) { - /* - * created debugfs will be automatically - * removed, nothing to do for _remove. - * see - * soc_cleanup_component_debugfs() - */ - snprintf(name, sizeof(name), "rdai%d", i); - dir = debugfs_create_dir(name, component->debugfs_root); - - debugfs_create_file("playback", 0444, dir, &rdai->playback, &rsnd_debugfs_fops); - debugfs_create_file("capture", 0444, dir, &rdai->capture, &rsnd_debugfs_fops); - } - - return 0; -} - -#endif /* CONFIG_DEBUG_FS */ diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c deleted file mode 100644 index 2342bbb6fe92..000000000000 --- a/sound/soc/sh/rcar/dma.c +++ /dev/null @@ -1,885 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Renesas R-Car Audio DMAC support -// -// Copyright (C) 2015 Renesas Electronics Corp. -// Copyright (c) 2015 Kuninori Morimoto - -#include -#include -#include -#include "rsnd.h" - -/* - * Audio DMAC peri peri register - */ -#define PDMASAR 0x00 -#define PDMADAR 0x04 -#define PDMACHCR 0x0c - -/* PDMACHCR */ -#define PDMACHCR_DE (1 << 0) - - -struct rsnd_dmaen { - struct dma_chan *chan; -}; - -struct rsnd_dmapp { - int dmapp_id; - u32 chcr; -}; - -struct rsnd_dma { - struct rsnd_mod mod; - struct rsnd_mod *mod_from; - struct rsnd_mod *mod_to; - dma_addr_t src_addr; - dma_addr_t dst_addr; - union { - struct rsnd_dmaen en; - struct rsnd_dmapp pp; - } dma; -}; - -struct rsnd_dma_ctrl { - void __iomem *ppbase; - phys_addr_t ppres; - int dmaen_num; - int dmapp_num; -}; - -#define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) -#define rsnd_mod_to_dma(_mod) container_of((_mod), struct rsnd_dma, mod) -#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) -#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) - -/* for DEBUG */ -static struct rsnd_mod_ops mem_ops = { - .name = "mem", -}; - -static struct rsnd_mod mem = { -}; - -/* - * Audio DMAC - */ -static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, - struct rsnd_mod *mod_from, - struct rsnd_mod *mod_to) -{ - if ((!mod_from && !mod_to) || - (mod_from && mod_to)) - return NULL; - - if (mod_from) - return rsnd_mod_dma_req(io, mod_from); - else - return rsnd_mod_dma_req(io, mod_to); -} - -static int rsnd_dmaen_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - return snd_dmaengine_pcm_trigger(io->substream, SNDRV_PCM_TRIGGER_STOP); -} - -static int rsnd_dmaen_cleanup(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - - /* - * DMAEngine release uses mutex lock. - * Thus, it shouldn't be called under spinlock. - * Let's call it under prepare - */ - if (dmaen->chan) - snd_dmaengine_pcm_close_release_chan(io->substream); - - dmaen->chan = NULL; - - return 0; -} - -static int rsnd_dmaen_prepare(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - struct device *dev = rsnd_priv_to_dev(priv); - - /* maybe suspended */ - if (dmaen->chan) - return 0; - - /* - * DMAEngine request uses mutex lock. - * Thus, it shouldn't be called under spinlock. - * Let's call it under prepare - */ - dmaen->chan = rsnd_dmaen_request_channel(io, - dma->mod_from, - dma->mod_to); - if (IS_ERR_OR_NULL(dmaen->chan)) { - dmaen->chan = NULL; - dev_err(dev, "can't get dma channel\n"); - return -EIO; - } - - return snd_dmaengine_pcm_open(io->substream, dmaen->chan); -} - -static int rsnd_dmaen_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - struct device *dev = rsnd_priv_to_dev(priv); - struct dma_slave_config cfg = {}; - enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; - int ret; - - /* - * in case of monaural data writing or reading through Audio-DMAC - * data is always in Left Justified format, so both src and dst - * DMA Bus width need to be set equal to physical data width. - */ - if (rsnd_runtime_channel_original(io) == 1) { - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - int bits = snd_pcm_format_physical_width(runtime->format); - - switch (bits) { - case 8: - buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; - break; - case 16: - buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; - break; - case 32: - buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; - break; - default: - dev_err(dev, "invalid format width %d\n", bits); - return -EINVAL; - } - } - - cfg.direction = snd_pcm_substream_to_dma_direction(io->substream); - cfg.src_addr = dma->src_addr; - cfg.dst_addr = dma->dst_addr; - cfg.src_addr_width = buswidth; - cfg.dst_addr_width = buswidth; - - dev_dbg(dev, "%s %pad -> %pad\n", - rsnd_mod_name(mod), - &cfg.src_addr, &cfg.dst_addr); - - ret = dmaengine_slave_config(dmaen->chan, &cfg); - if (ret < 0) - return ret; - - return snd_dmaengine_pcm_trigger(io->substream, SNDRV_PCM_TRIGGER_START); -} - -struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name, - struct rsnd_mod *mod, char *x) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - struct dma_chan *chan = NULL; - struct device_node *np; - int i = 0; - - for_each_child_of_node(of_node, np) { - i = rsnd_node_fixed_index(dev, np, name, i); - if (i < 0) { - chan = NULL; - of_node_put(np); - break; - } - - if (i == rsnd_mod_id_raw(mod) && (!chan)) - chan = of_dma_request_slave_channel(np, x); - i++; - } - - /* It should call of_node_put(), since, it is rsnd_xxx_of_node() */ - of_node_put(of_node); - - return chan; -} - -static int rsnd_dmaen_attach(struct rsnd_dai_stream *io, - struct rsnd_dma *dma, - struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) -{ - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - struct dma_chan *chan; - - /* try to get DMAEngine channel */ - chan = rsnd_dmaen_request_channel(io, mod_from, mod_to); - if (IS_ERR_OR_NULL(chan)) { - /* Let's follow when -EPROBE_DEFER case */ - if (PTR_ERR(chan) == -EPROBE_DEFER) - return PTR_ERR(chan); - - /* - * DMA failed. try to PIO mode - * see - * rsnd_ssi_fallback() - * rsnd_rdai_continuance_probe() - */ - return -EAGAIN; - } - - /* - * use it for IPMMU if needed - * see - * rsnd_preallocate_pages() - */ - io->dmac_dev = chan->device->dev; - - dma_release_channel(chan); - - dmac->dmaen_num++; - - return 0; -} - -static int rsnd_dmaen_pointer(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - snd_pcm_uframes_t *pointer) -{ - *pointer = snd_dmaengine_pcm_pointer(io->substream); - - return 0; -} - -static struct rsnd_mod_ops rsnd_dmaen_ops = { - .name = "audmac", - .prepare = rsnd_dmaen_prepare, - .cleanup = rsnd_dmaen_cleanup, - .start = rsnd_dmaen_start, - .stop = rsnd_dmaen_stop, - .pointer = rsnd_dmaen_pointer, - .get_status = rsnd_mod_get_status, -}; - -/* - * Audio DMAC peri peri - */ -static const u8 gen2_id_table_ssiu[] = { - /* SSI00 ~ SSI07 */ - 0x00, 0x01, 0x02, 0x03, 0x39, 0x3a, 0x3b, 0x3c, - /* SSI10 ~ SSI17 */ - 0x04, 0x05, 0x06, 0x07, 0x3d, 0x3e, 0x3f, 0x40, - /* SSI20 ~ SSI27 */ - 0x08, 0x09, 0x0a, 0x0b, 0x41, 0x42, 0x43, 0x44, - /* SSI30 ~ SSI37 */ - 0x0c, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, - /* SSI40 ~ SSI47 */ - 0x0d, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, - /* SSI5 */ - 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* SSI6 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* SSI7 */ - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* SSI8 */ - 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* SSI90 ~ SSI97 */ - 0x12, 0x13, 0x14, 0x15, 0x53, 0x54, 0x55, 0x56, -}; -static const u8 gen2_id_table_scu[] = { - 0x2d, /* SCU_SRCI0 */ - 0x2e, /* SCU_SRCI1 */ - 0x2f, /* SCU_SRCI2 */ - 0x30, /* SCU_SRCI3 */ - 0x31, /* SCU_SRCI4 */ - 0x32, /* SCU_SRCI5 */ - 0x33, /* SCU_SRCI6 */ - 0x34, /* SCU_SRCI7 */ - 0x35, /* SCU_SRCI8 */ - 0x36, /* SCU_SRCI9 */ -}; -static const u8 gen2_id_table_cmd[] = { - 0x37, /* SCU_CMD0 */ - 0x38, /* SCU_CMD1 */ -}; - -static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); - struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); - struct rsnd_mod *src = rsnd_io_to_mod_src(io); - struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); - const u8 *entry = NULL; - int id = 255; - int size = 0; - - if ((mod == ssi) || - (mod == ssiu)) { - int busif = rsnd_mod_id_sub(ssiu); - - entry = gen2_id_table_ssiu; - size = ARRAY_SIZE(gen2_id_table_ssiu); - id = (rsnd_mod_id(mod) * 8) + busif; - } else if (mod == src) { - entry = gen2_id_table_scu; - size = ARRAY_SIZE(gen2_id_table_scu); - id = rsnd_mod_id(mod); - } else if (mod == dvc) { - entry = gen2_id_table_cmd; - size = ARRAY_SIZE(gen2_id_table_cmd); - id = rsnd_mod_id(mod); - } - - if ((!entry) || (size <= id)) { - struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); - - dev_err(dev, "unknown connection (%s)\n", rsnd_mod_name(mod)); - - /* use non-prohibited SRS number as error */ - return 0x00; /* SSI00 */ - } - - return entry[id]; -} - -static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io, - struct rsnd_mod *mod_from, - struct rsnd_mod *mod_to) -{ - return (rsnd_dmapp_get_id(io, mod_from) << 24) + - (rsnd_dmapp_get_id(io, mod_to) << 16); -} - -#define rsnd_dmapp_addr(dmac, dma, reg) \ - (dmac->ppbase + 0x20 + reg + \ - (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id)) -static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) -{ - struct rsnd_mod *mod = rsnd_mod_get(dma); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "w 0x%px : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data); - - iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg)); -} - -static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) -{ - struct rsnd_mod *mod = rsnd_mod_get(dma); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - - return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); -} - -static void rsnd_dmapp_bset(struct rsnd_dma *dma, u32 data, u32 mask, u32 reg) -{ - struct rsnd_mod *mod = rsnd_mod_get(dma); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - void __iomem *addr = rsnd_dmapp_addr(dmac, dma, reg); - u32 val = ioread32(addr); - - val &= ~mask; - val |= (data & mask); - - iowrite32(val, addr); -} - -static int rsnd_dmapp_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - int i; - - rsnd_dmapp_bset(dma, 0, PDMACHCR_DE, PDMACHCR); - - for (i = 0; i < 1024; i++) { - if (0 == (rsnd_dmapp_read(dma, PDMACHCR) & PDMACHCR_DE)) - return 0; - udelay(1); - } - - return -EIO; -} - -static int rsnd_dmapp_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); - - rsnd_dmapp_write(dma, dma->src_addr, PDMASAR); - rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR); - rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); - - return 0; -} - -static int rsnd_dmapp_attach(struct rsnd_dai_stream *io, - struct rsnd_dma *dma, - struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) -{ - struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - struct device *dev = rsnd_priv_to_dev(priv); - - dmapp->dmapp_id = dmac->dmapp_num; - dmapp->chcr = rsnd_dmapp_get_chcr(io, mod_from, mod_to) | PDMACHCR_DE; - - dmac->dmapp_num++; - - dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", - dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); - - return 0; -} - -#ifdef CONFIG_DEBUG_FS -static void rsnd_dmapp_debug_info(struct seq_file *m, - struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); - - rsnd_debugfs_reg_show(m, dmac->ppres, dmac->ppbase, - 0x20 + 0x10 * dmapp->dmapp_id, 0x10); -} -#define DEBUG_INFO .debug_info = rsnd_dmapp_debug_info -#else -#define DEBUG_INFO -#endif - -static struct rsnd_mod_ops rsnd_dmapp_ops = { - .name = "audmac-pp", - .start = rsnd_dmapp_start, - .stop = rsnd_dmapp_stop, - .quit = rsnd_dmapp_stop, - .get_status = rsnd_mod_get_status, - DEBUG_INFO -}; - -/* - * Common DMAC Interface - */ - -/* - * DMA read/write register offset - * - * RSND_xxx_I_N for Audio DMAC input - * RSND_xxx_O_N for Audio DMAC output - * RSND_xxx_I_P for Audio DMAC peri peri input - * RSND_xxx_O_P for Audio DMAC peri peri output - * - * ex) R-Car H2 case - * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out - * SSI : 0xec541000 / 0xec241008 / 0xec24100c - * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 - * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 - * CMD : 0xec500000 / / 0xec008000 0xec308000 - */ -#define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) -#define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) - -#define RDMA_SSIU_I_N(addr, i, j) (addr ##_reg - 0x00441000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4))) -#define RDMA_SSIU_O_N(addr, i, j) RDMA_SSIU_I_N(addr, i, j) - -#define RDMA_SSIU_I_P(addr, i, j) (addr ##_reg - 0x00141000 + (0x1000 * (i)) + (((j) / 4) * 0xA000) + (((j) % 4) * 0x400) - (0x4000 * ((i) / 9) * ((j) / 4))) -#define RDMA_SSIU_O_P(addr, i, j) RDMA_SSIU_I_P(addr, i, j) - -#define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) -#define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) - -#define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) -#define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) - -#define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) -#define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) - -static dma_addr_t -rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - int is_play, int is_from) -{ - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct device *dev = rsnd_priv_to_dev(priv); - phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_BASE_SSI); - phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_BASE_SCU); - int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod) || - !!(rsnd_io_to_mod_ssiu(io) == mod); - int use_src = !!rsnd_io_to_mod_src(io); - int use_cmd = !!rsnd_io_to_mod_dvc(io) || - !!rsnd_io_to_mod_mix(io) || - !!rsnd_io_to_mod_ctu(io); - int id = rsnd_mod_id(mod); - int busif = rsnd_mod_id_sub(rsnd_io_to_mod_ssiu(io)); - struct dma_addr { - dma_addr_t out_addr; - dma_addr_t in_addr; - } dma_addrs[3][2][3] = { - /* SRC */ - /* Capture */ - {{{ 0, 0 }, - { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, - { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, - /* Playback */ - {{ 0, 0, }, - { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, - { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } - }, - /* SSI */ - /* Capture */ - {{{ RDMA_SSI_O_N(ssi, id), 0 }, - { RDMA_SSIU_O_P(ssi, id, busif), 0 }, - { RDMA_SSIU_O_P(ssi, id, busif), 0 } }, - /* Playback */ - {{ 0, RDMA_SSI_I_N(ssi, id) }, - { 0, RDMA_SSIU_I_P(ssi, id, busif) }, - { 0, RDMA_SSIU_I_P(ssi, id, busif) } } - }, - /* SSIU */ - /* Capture */ - {{{ RDMA_SSIU_O_N(ssi, id, busif), 0 }, - { RDMA_SSIU_O_P(ssi, id, busif), 0 }, - { RDMA_SSIU_O_P(ssi, id, busif), 0 } }, - /* Playback */ - {{ 0, RDMA_SSIU_I_N(ssi, id, busif) }, - { 0, RDMA_SSIU_I_P(ssi, id, busif) }, - { 0, RDMA_SSIU_I_P(ssi, id, busif) } } }, - }; - - /* - * FIXME - * - * We can't support SSI9-4/5/6/7, because its address is - * out of calculation rule - */ - if ((id == 9) && (busif >= 4)) - dev_err(dev, "This driver doesn't support SSI%d-%d, so far", - id, busif); - - /* it shouldn't happen */ - if (use_cmd && !use_src) - dev_err(dev, "DVC is selected without SRC\n"); - - /* use SSIU or SSI ? */ - if (is_ssi && rsnd_ssi_use_busif(io)) - is_ssi++; - - return (is_from) ? - dma_addrs[is_ssi][is_play][use_src + use_cmd].out_addr : - dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr; -} - -/* - * Gen4 DMA read/write register offset - * - * ex) R-Car V4H case - * mod / SYS-DMAC in / SYS-DMAC out - * SSI_SDMC: 0xec400000 / 0xec400000 / 0xec400000 - */ -#define RDMA_SSI_SDMC(addr, i) (addr + (0x8000 * i)) -static dma_addr_t -rsnd_gen4_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod, - int is_play, int is_from) -{ - struct rsnd_priv *priv = rsnd_io_to_priv(io); - phys_addr_t addr = rsnd_gen_get_phy_addr(priv, RSND_BASE_SDMC); - int id = rsnd_mod_id(mod); - int busif = rsnd_mod_id_sub(mod); - - /* - * SSI0 only is supported - */ - if (id != 0) { - struct device *dev = rsnd_priv_to_dev(priv); - - dev_err(dev, "This driver doesn't support non SSI0"); - return -EINVAL; - } - - return RDMA_SSI_SDMC(addr, busif); -} - -static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - int is_play, int is_from) -{ - struct rsnd_priv *priv = rsnd_io_to_priv(io); - - if (!mod) - return 0; - - /* - * gen1 uses default DMA addr - */ - if (rsnd_is_gen1(priv)) - return 0; - else if (rsnd_is_gen4(priv)) - return rsnd_gen4_dma_addr(io, mod, is_play, is_from); - else - return rsnd_gen2_dma_addr(io, mod, is_play, is_from); -} - -#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */ -static void rsnd_dma_of_path(struct rsnd_mod *this, - struct rsnd_dai_stream *io, - int is_play, - struct rsnd_mod **mod_from, - struct rsnd_mod **mod_to) -{ - struct rsnd_mod *ssi; - struct rsnd_mod *src = rsnd_io_to_mod_src(io); - struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io); - struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); - struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); - struct rsnd_mod *mod[MOD_MAX]; - struct rsnd_mod *mod_start, *mod_end; - struct rsnd_priv *priv = rsnd_mod_to_priv(this); - struct device *dev = rsnd_priv_to_dev(priv); - int nr, i, idx; - - /* - * It should use "rcar_sound,ssiu" on DT. - * But, we need to keep compatibility for old version. - * - * If it has "rcar_sound.ssiu", it will be used. - * If not, "rcar_sound.ssi" will be used. - * see - * rsnd_ssiu_dma_req() - * rsnd_ssi_dma_req() - */ - if (rsnd_ssiu_of_node(priv)) { - struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); - - /* use SSIU */ - ssi = ssiu; - if (this == rsnd_io_to_mod_ssi(io)) - this = ssiu; - } else { - /* keep compatible, use SSI */ - ssi = rsnd_io_to_mod_ssi(io); - } - - if (!ssi) - return; - - nr = 0; - for (i = 0; i < MOD_MAX; i++) { - mod[i] = NULL; - nr += !!rsnd_io_to_mod(io, i); - } - - /* - * [S] -*-> [E] - * [S] -*-> SRC -o-> [E] - * [S] -*-> SRC -> DVC -o-> [E] - * [S] -*-> SRC -> CTU -> MIX -> DVC -o-> [E] - * - * playback [S] = mem - * [E] = SSI - * - * capture [S] = SSI - * [E] = mem - * - * -*-> Audio DMAC - * -o-> Audio DMAC peri peri - */ - mod_start = (is_play) ? NULL : ssi; - mod_end = (is_play) ? ssi : NULL; - - idx = 0; - mod[idx++] = mod_start; - for (i = 1; i < nr; i++) { - if (src) { - mod[idx++] = src; - src = NULL; - } else if (ctu) { - mod[idx++] = ctu; - ctu = NULL; - } else if (mix) { - mod[idx++] = mix; - mix = NULL; - } else if (dvc) { - mod[idx++] = dvc; - dvc = NULL; - } - } - mod[idx] = mod_end; - - /* - * | SSI | SRC | - * -------------+-----+-----+ - * is_play | o | * | - * !is_play | * | o | - */ - if ((this == ssi) == (is_play)) { - *mod_from = mod[idx - 1]; - *mod_to = mod[idx]; - } else { - *mod_from = mod[0]; - *mod_to = mod[1]; - } - - dev_dbg(dev, "module connection (this is %s)\n", rsnd_mod_name(this)); - for (i = 0; i <= idx; i++) { - dev_dbg(dev, " %s%s\n", - rsnd_mod_name(mod[i] ? mod[i] : &mem), - (mod[i] == *mod_from) ? " from" : - (mod[i] == *mod_to) ? " to" : ""); - } -} - -static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod, - struct rsnd_mod **dma_mod) -{ - struct rsnd_mod *mod_from = NULL; - struct rsnd_mod *mod_to = NULL; - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dma *dma; - struct rsnd_mod_ops *ops; - enum rsnd_mod_type type; - int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, - struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); - int is_play = rsnd_io_is_play(io); - int ret, dma_id; - - /* - * DMA failed. try to PIO mode - * see - * rsnd_ssi_fallback() - * rsnd_rdai_continuance_probe() - */ - if (!dmac) - return -EAGAIN; - - rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to); - - /* for Gen2 or later */ - if (mod_from && mod_to) { - ops = &rsnd_dmapp_ops; - attach = rsnd_dmapp_attach; - dma_id = dmac->dmapp_num; - type = RSND_MOD_AUDMAPP; - } else { - ops = &rsnd_dmaen_ops; - attach = rsnd_dmaen_attach; - dma_id = dmac->dmaen_num; - type = RSND_MOD_AUDMA; - } - - /* for Gen1, overwrite */ - if (rsnd_is_gen1(priv)) { - ops = &rsnd_dmaen_ops; - attach = rsnd_dmaen_attach; - dma_id = dmac->dmaen_num; - type = RSND_MOD_AUDMA; - } - - dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); - if (!dma) - return -ENOMEM; - - *dma_mod = rsnd_mod_get(dma); - - ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, - type, dma_id); - if (ret < 0) - return ret; - - dev_dbg(dev, "%s %s -> %s\n", - rsnd_mod_name(*dma_mod), - rsnd_mod_name(mod_from ? mod_from : &mem), - rsnd_mod_name(mod_to ? mod_to : &mem)); - - ret = attach(io, dma, mod_from, mod_to); - if (ret < 0) - return ret; - - dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); - dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); - dma->mod_from = mod_from; - dma->mod_to = mod_to; - - return 0; -} - -int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, - struct rsnd_mod **dma_mod) -{ - if (!(*dma_mod)) { - int ret = rsnd_dma_alloc(io, mod, dma_mod); - - if (ret < 0) - return ret; - } - - return rsnd_dai_connect(*dma_mod, io, (*dma_mod)->type); -} - -int rsnd_dma_probe(struct rsnd_priv *priv) -{ - struct platform_device *pdev = rsnd_priv_to_pdev(priv); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dma_ctrl *dmac; - struct resource *res; - - /* - * for Gen1 - */ - if (rsnd_is_gen1(priv)) - return 0; - - /* - * for Gen2 or later - */ - dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL); - if (!dmac) { - dev_err(dev, "dma allocate failed\n"); - return 0; /* it will be PIO mode */ - } - - /* for Gen4 doesn't have DMA-pp */ - if (rsnd_is_gen4(priv)) - goto audmapp_end; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp"); - if (!res) { - dev_err(dev, "lack of audmapp in DT\n"); - return 0; /* it will be PIO mode */ - } - - dmac->dmapp_num = 0; - dmac->ppres = res->start; - dmac->ppbase = devm_ioremap_resource(dev, res); - if (IS_ERR(dmac->ppbase)) - return PTR_ERR(dmac->ppbase); -audmapp_end: - priv->dma = dmac; - - /* dummy mem mod for debug */ - return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, 0, 0); -} diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c deleted file mode 100644 index da91dd301aab..000000000000 --- a/sound/soc/sh/rcar/dvc.c +++ /dev/null @@ -1,392 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Renesas R-Car DVC support -// -// Copyright (C) 2014 Renesas Solutions Corp. -// Kuninori Morimoto - -/* - * Playback Volume - * amixer set "DVC Out" 100% - * - * Capture Volume - * amixer set "DVC In" 100% - * - * Playback Mute - * amixer set "DVC Out Mute" on - * - * Capture Mute - * amixer set "DVC In Mute" on - * - * Volume Ramp - * amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps" - * amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps" - * amixer set "DVC Out Ramp" on - * aplay xxx.wav & - * amixer set "DVC Out" 80% // Volume Down - * amixer set "DVC Out" 100% // Volume Up - */ - -#include "rsnd.h" - -#define RSND_DVC_NAME_SIZE 16 - -#define DVC_NAME "dvc" - -struct rsnd_dvc { - struct rsnd_mod mod; - struct rsnd_kctrl_cfg_m volume; - struct rsnd_kctrl_cfg_m mute; - struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ - struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ - struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ -}; - -#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id) -#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) - -#define rsnd_mod_to_dvc(_mod) \ - container_of((_mod), struct rsnd_dvc, mod) - -#define for_each_rsnd_dvc(pos, priv, i) \ - for ((i) = 0; \ - ((i) < rsnd_dvc_nr(priv)) && \ - ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ - i++) - -static void rsnd_dvc_activation(struct rsnd_mod *mod) -{ - rsnd_mod_write(mod, DVC_SWRSR, 0); - rsnd_mod_write(mod, DVC_SWRSR, 1); -} - -static void rsnd_dvc_halt(struct rsnd_mod *mod) -{ - rsnd_mod_write(mod, DVC_DVUIR, 1); - rsnd_mod_write(mod, DVC_SWRSR, 0); -} - -#define rsnd_dvc_get_vrpdr(dvc) (rsnd_kctrl_vals(dvc->rup) << 8 | \ - rsnd_kctrl_vals(dvc->rdown)) -#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (rsnd_kctrl_valm(dvc->volume, 0) >> 13)) - -static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); - u32 val[RSND_MAX_CHANNELS]; - int i; - - /* Enable Ramp */ - if (rsnd_kctrl_vals(dvc->ren)) - for (i = 0; i < RSND_MAX_CHANNELS; i++) - val[i] = rsnd_kctrl_max(dvc->volume); - else - for (i = 0; i < RSND_MAX_CHANNELS; i++) - val[i] = rsnd_kctrl_valm(dvc->volume, i); - - /* Enable Digital Volume */ - for (i = 0; i < RSND_MAX_CHANNELS; i++) - rsnd_mod_write(mod, DVC_VOLxR(i), val[i]); -} - -static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); - u32 adinr = 0; - u32 dvucr = 0; - u32 vrctr = 0; - u32 vrpdr = 0; - u32 vrdbr = 0; - - adinr = rsnd_get_adinr_bit(mod, io) | - rsnd_runtime_channel_after_ctu(io); - - /* Enable Digital Volume, Zero Cross Mute Mode */ - dvucr |= 0x101; - - /* Enable Ramp */ - if (rsnd_kctrl_vals(dvc->ren)) { - dvucr |= 0x10; - - /* - * FIXME !! - * use scale-downed Digital Volume - * as Volume Ramp - * 7F FFFF -> 3FF - */ - vrctr = 0xff; - vrpdr = rsnd_dvc_get_vrpdr(dvc); - vrdbr = rsnd_dvc_get_vrdbr(dvc); - } - - /* Initialize operation */ - rsnd_mod_write(mod, DVC_DVUIR, 1); - - /* General Information */ - rsnd_mod_write(mod, DVC_ADINR, adinr); - rsnd_mod_write(mod, DVC_DVUCR, dvucr); - - /* Volume Ramp Parameter */ - rsnd_mod_write(mod, DVC_VRCTR, vrctr); - rsnd_mod_write(mod, DVC_VRPDR, vrpdr); - rsnd_mod_write(mod, DVC_VRDBR, vrdbr); - - /* Digital Volume Function Parameter */ - rsnd_dvc_volume_parameter(io, mod); - - /* cancel operation */ - rsnd_mod_write(mod, DVC_DVUIR, 0); -} - -static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); - u32 zcmcr = 0; - u32 vrpdr = 0; - u32 vrdbr = 0; - int i; - - for (i = 0; i < rsnd_kctrl_size(dvc->mute); i++) - zcmcr |= (!!rsnd_kctrl_valm(dvc->mute, i)) << i; - - if (rsnd_kctrl_vals(dvc->ren)) { - vrpdr = rsnd_dvc_get_vrpdr(dvc); - vrdbr = rsnd_dvc_get_vrdbr(dvc); - } - - /* Disable DVC Register access */ - rsnd_mod_write(mod, DVC_DVUER, 0); - - /* Zero Cross Mute Function */ - rsnd_mod_write(mod, DVC_ZCMCR, zcmcr); - - /* Volume Ramp Function */ - rsnd_mod_write(mod, DVC_VRPDR, vrpdr); - rsnd_mod_write(mod, DVC_VRDBR, vrdbr); - /* add DVC_VRWTR here */ - - /* Digital Volume Function Parameter */ - rsnd_dvc_volume_parameter(io, mod); - - /* Enable DVC Register access */ - rsnd_mod_write(mod, DVC_DVUER, 1); -} - -static int rsnd_dvc_probe_(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - return rsnd_cmd_attach(io, rsnd_mod_id(mod)); -} - -static int rsnd_dvc_init(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - int ret; - - ret = rsnd_mod_power_on(mod); - if (ret < 0) - return ret; - - rsnd_dvc_activation(mod); - - rsnd_dvc_volume_init(io, mod); - - rsnd_dvc_volume_update(io, mod); - - return 0; -} - -static int rsnd_dvc_quit(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - rsnd_dvc_halt(mod); - - rsnd_mod_power_off(mod); - - return 0; -} - -static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_soc_pcm_runtime *rtd) -{ - struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - int is_play = rsnd_io_is_play(io); - int channels = rsnd_rdai_channels_get(rdai); - int ret; - - /* Volume */ - ret = rsnd_kctrl_new_m(mod, io, rtd, - is_play ? - "DVC Out Playback Volume" : "DVC In Capture Volume", - rsnd_kctrl_accept_anytime, - rsnd_dvc_volume_update, - &dvc->volume, channels, - 0x00800000 - 1); - if (ret < 0) - return ret; - - /* Mute */ - ret = rsnd_kctrl_new_m(mod, io, rtd, - is_play ? - "DVC Out Mute Switch" : "DVC In Mute Switch", - rsnd_kctrl_accept_anytime, - rsnd_dvc_volume_update, - &dvc->mute, channels, - 1); - if (ret < 0) - return ret; - - /* Ramp */ - ret = rsnd_kctrl_new_s(mod, io, rtd, - is_play ? - "DVC Out Ramp Switch" : "DVC In Ramp Switch", - rsnd_kctrl_accept_anytime, - rsnd_dvc_volume_update, - &dvc->ren, 1); - if (ret < 0) - return ret; - - ret = rsnd_kctrl_new_e(mod, io, rtd, - is_play ? - "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", - rsnd_kctrl_accept_anytime, - rsnd_dvc_volume_update, - &dvc->rup, - volume_ramp_rate, - VOLUME_RAMP_MAX_DVC); - if (ret < 0) - return ret; - - ret = rsnd_kctrl_new_e(mod, io, rtd, - is_play ? - "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", - rsnd_kctrl_accept_anytime, - rsnd_dvc_volume_update, - &dvc->rdown, - volume_ramp_rate, - VOLUME_RAMP_MAX_DVC); - - if (ret < 0) - return ret; - - return 0; -} - -static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - - return rsnd_dma_request_channel(rsnd_dvc_of_node(priv), - DVC_NAME, mod, "tx"); -} - -#ifdef CONFIG_DEBUG_FS -static void rsnd_dvc_debug_info(struct seq_file *m, - struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, - 0xe00 + rsnd_mod_id(mod) * 0x100, 0x60); -} -#define DEBUG_INFO .debug_info = rsnd_dvc_debug_info -#else -#define DEBUG_INFO -#endif - -static struct rsnd_mod_ops rsnd_dvc_ops = { - .name = DVC_NAME, - .dma_req = rsnd_dvc_dma_req, - .probe = rsnd_dvc_probe_, - .init = rsnd_dvc_init, - .quit = rsnd_dvc_quit, - .pcm_new = rsnd_dvc_pcm_new, - .get_status = rsnd_mod_get_status, - DEBUG_INFO -}; - -struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) -{ - if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) - id = 0; - - return rsnd_mod_get(rsnd_dvc_get(priv, id)); -} - -int rsnd_dvc_probe(struct rsnd_priv *priv) -{ - struct device_node *node; - struct device_node *np; - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dvc *dvc; - struct clk *clk; - char name[RSND_DVC_NAME_SIZE]; - int i, nr, ret; - - node = rsnd_dvc_of_node(priv); - if (!node) - return 0; /* not used is not error */ - - nr = of_get_child_count(node); - if (!nr) { - ret = -EINVAL; - goto rsnd_dvc_probe_done; - } - - dvc = devm_kcalloc(dev, nr, sizeof(*dvc), GFP_KERNEL); - if (!dvc) { - ret = -ENOMEM; - goto rsnd_dvc_probe_done; - } - - priv->dvc_nr = nr; - priv->dvc = dvc; - - i = 0; - ret = 0; - for_each_child_of_node(node, np) { - dvc = rsnd_dvc_get(priv, i); - - snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d", - DVC_NAME, i); - - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - of_node_put(np); - goto rsnd_dvc_probe_done; - } - - ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, - clk, RSND_MOD_DVC, i); - if (ret) { - of_node_put(np); - goto rsnd_dvc_probe_done; - } - - i++; - } - -rsnd_dvc_probe_done: - of_node_put(node); - - return ret; -} - -void rsnd_dvc_remove(struct rsnd_priv *priv) -{ - struct rsnd_dvc *dvc; - int i; - - for_each_rsnd_dvc(dvc, priv, i) { - rsnd_mod_quit(rsnd_mod_get(dvc)); - } -} diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c deleted file mode 100644 index d1f20cde66be..000000000000 --- a/sound/soc/sh/rcar/gen.c +++ /dev/null @@ -1,495 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Renesas R-Car Gen1 SRU/SSI support -// -// Copyright (C) 2013 Renesas Solutions Corp. -// Kuninori Morimoto - -/* - * #define DEBUG - * - * you can also add below in - * ${LINUX}/drivers/base/regmap/regmap.c - * for regmap debug - * - * #define LOG_DEVICE "xxxx.rcar_sound" - */ - -#include "rsnd.h" - -struct rsnd_gen { - struct rsnd_gen_ops *ops; - - /* RSND_BASE_MAX base */ - void __iomem *base[RSND_BASE_MAX]; - phys_addr_t res[RSND_BASE_MAX]; - struct regmap *regmap[RSND_BASE_MAX]; - - /* RSND_REG_MAX base */ - struct regmap_field *regs[REG_MAX]; - const char *reg_name[REG_MAX]; -}; - -#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) -#define rsnd_reg_name(gen, id) ((gen)->reg_name[id]) - -struct rsnd_regmap_field_conf { - int idx; - unsigned int reg_offset; - unsigned int id_offset; - const char *reg_name; -}; - -#define RSND_REG_SET(id, offset, _id_offset, n) \ -{ \ - .idx = id, \ - .reg_offset = offset, \ - .id_offset = _id_offset, \ - .reg_name = n, \ -} -/* single address mapping */ -#define RSND_GEN_S_REG(id, offset) \ - RSND_REG_SET(id, offset, 0, #id) - -/* multi address mapping */ -#define RSND_GEN_M_REG(id, offset, _id_offset) \ - RSND_REG_SET(id, offset, _id_offset, #id) - -/* - * basic function - */ -static int rsnd_is_accessible_reg(struct rsnd_priv *priv, - struct rsnd_gen *gen, enum rsnd_reg reg) -{ - if (!gen->regs[reg]) { - struct device *dev = rsnd_priv_to_dev(priv); - - dev_err(dev, "unsupported register access %x\n", reg); - return 0; - } - - return 1; -} - -static int rsnd_mod_id_cmd(struct rsnd_mod *mod) -{ - if (mod->ops->id_cmd) - return mod->ops->id_cmd(mod); - - return rsnd_mod_id(mod); -} - -u32 rsnd_mod_read(struct rsnd_mod *mod, enum rsnd_reg reg) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - u32 val; - - if (!rsnd_is_accessible_reg(priv, gen, reg)) - return 0; - - regmap_fields_read(gen->regs[reg], rsnd_mod_id_cmd(mod), &val); - - dev_dbg(dev, "r %s - %-18s (%4d) : %08x\n", - rsnd_mod_name(mod), - rsnd_reg_name(gen, reg), reg, val); - - return val; -} - -void rsnd_mod_write(struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - - if (!rsnd_is_accessible_reg(priv, gen, reg)) - return; - - regmap_fields_force_write(gen->regs[reg], rsnd_mod_id_cmd(mod), data); - - dev_dbg(dev, "w %s - %-18s (%4d) : %08x\n", - rsnd_mod_name(mod), - rsnd_reg_name(gen, reg), reg, data); -} - -void rsnd_mod_bset(struct rsnd_mod *mod, - enum rsnd_reg reg, u32 mask, u32 data) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - - if (!rsnd_is_accessible_reg(priv, gen, reg)) - return; - - regmap_fields_force_update_bits(gen->regs[reg], - rsnd_mod_id_cmd(mod), mask, data); - - dev_dbg(dev, "b %s - %-18s (%4d) : %08x/%08x\n", - rsnd_mod_name(mod), - rsnd_reg_name(gen, reg), reg, data, mask); - -} - -phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id) -{ - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - - return gen->res[reg_id]; -} - -#ifdef CONFIG_DEBUG_FS -void __iomem *rsnd_gen_get_base_addr(struct rsnd_priv *priv, int reg_id) -{ - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - - return gen->base[reg_id]; -} -#endif - -#define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf) \ - _rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf)) -static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, - int id_size, - int reg_id, - const char *name, - const struct rsnd_regmap_field_conf *conf, - int conf_size) -{ - struct platform_device *pdev = rsnd_priv_to_pdev(priv); - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - struct device *dev = rsnd_priv_to_dev(priv); - struct resource *res; - struct regmap_config regc; - struct regmap_field *regs; - struct regmap *regmap; - struct reg_field regf; - void __iomem *base; - int i; - - memset(®c, 0, sizeof(regc)); - regc.reg_bits = 32; - regc.val_bits = 32; - regc.reg_stride = 4; - regc.name = name; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); - if (!res) - return -ENODEV; - - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, ®c); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - /* RSND_BASE_MAX base */ - gen->base[reg_id] = base; - gen->regmap[reg_id] = regmap; - gen->res[reg_id] = res->start; - - for (i = 0; i < conf_size; i++) { - - regf.reg = conf[i].reg_offset; - regf.id_offset = conf[i].id_offset; - regf.lsb = 0; - regf.msb = 31; - regf.id_size = id_size; - - regs = devm_regmap_field_alloc(dev, regmap, regf); - if (IS_ERR(regs)) - return PTR_ERR(regs); - - /* RSND_REG_MAX base */ - gen->regs[conf[i].idx] = regs; - gen->reg_name[conf[i].idx] = conf[i].reg_name; - } - - return 0; -} - -/* - * (A) : Gen4 is 0xa0c, but it is not used. - * see - * rsnd_ssiu_init() - */ -static const struct rsnd_regmap_field_conf conf_common_ssiu[] = { - RSND_GEN_S_REG(SSI_MODE0, 0x800), - RSND_GEN_S_REG(SSI_MODE1, 0x804), - RSND_GEN_S_REG(SSI_MODE2, 0x808), // (A) - RSND_GEN_S_REG(SSI_CONTROL, 0x810), - RSND_GEN_S_REG(SSI_SYS_STATUS0, 0x840), - RSND_GEN_S_REG(SSI_SYS_STATUS1, 0x844), - RSND_GEN_S_REG(SSI_SYS_STATUS2, 0x848), - RSND_GEN_S_REG(SSI_SYS_STATUS3, 0x84c), - RSND_GEN_S_REG(SSI_SYS_STATUS4, 0x880), - RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884), - RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888), - RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c), - RSND_GEN_S_REG(SSI_SYS_INT_ENABLE0, 0x850), - RSND_GEN_S_REG(SSI_SYS_INT_ENABLE1, 0x854), - RSND_GEN_S_REG(SSI_SYS_INT_ENABLE2, 0x858), - RSND_GEN_S_REG(SSI_SYS_INT_ENABLE3, 0x85c), - RSND_GEN_S_REG(SSI_SYS_INT_ENABLE4, 0x890), - RSND_GEN_S_REG(SSI_SYS_INT_ENABLE5, 0x894), - RSND_GEN_S_REG(SSI_SYS_INT_ENABLE6, 0x898), - RSND_GEN_S_REG(SSI_SYS_INT_ENABLE7, 0x89c), - RSND_GEN_S_REG(HDMI0_SEL, 0x9e0), - RSND_GEN_S_REG(HDMI1_SEL, 0x9e4), - RSND_GEN_M_REG(SSI_BUSIF0_MODE, 0x0, 0x80), - RSND_GEN_M_REG(SSI_BUSIF0_ADINR, 0x4, 0x80), - RSND_GEN_M_REG(SSI_BUSIF0_DALIGN, 0x8, 0x80), - RSND_GEN_M_REG(SSI_BUSIF1_MODE, 0x20, 0x80), - RSND_GEN_M_REG(SSI_BUSIF1_ADINR, 0x24, 0x80), - RSND_GEN_M_REG(SSI_BUSIF1_DALIGN, 0x28, 0x80), - RSND_GEN_M_REG(SSI_BUSIF2_MODE, 0x40, 0x80), - RSND_GEN_M_REG(SSI_BUSIF2_ADINR, 0x44, 0x80), - RSND_GEN_M_REG(SSI_BUSIF2_DALIGN, 0x48, 0x80), - RSND_GEN_M_REG(SSI_BUSIF3_MODE, 0x60, 0x80), - RSND_GEN_M_REG(SSI_BUSIF3_ADINR, 0x64, 0x80), - RSND_GEN_M_REG(SSI_BUSIF3_DALIGN, 0x68, 0x80), - RSND_GEN_M_REG(SSI_BUSIF4_MODE, 0x500, 0x80), - RSND_GEN_M_REG(SSI_BUSIF4_ADINR, 0x504, 0x80), - RSND_GEN_M_REG(SSI_BUSIF4_DALIGN, 0x508, 0x80), - RSND_GEN_M_REG(SSI_BUSIF5_MODE, 0x520, 0x80), - RSND_GEN_M_REG(SSI_BUSIF5_ADINR, 0x524, 0x80), - RSND_GEN_M_REG(SSI_BUSIF5_DALIGN, 0x528, 0x80), - RSND_GEN_M_REG(SSI_BUSIF6_MODE, 0x540, 0x80), - RSND_GEN_M_REG(SSI_BUSIF6_ADINR, 0x544, 0x80), - RSND_GEN_M_REG(SSI_BUSIF6_DALIGN, 0x548, 0x80), - RSND_GEN_M_REG(SSI_BUSIF7_MODE, 0x560, 0x80), - RSND_GEN_M_REG(SSI_BUSIF7_ADINR, 0x564, 0x80), - RSND_GEN_M_REG(SSI_BUSIF7_DALIGN, 0x568, 0x80), - RSND_GEN_M_REG(SSI_MODE, 0xc, 0x80), - RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80), - RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80), - RSND_GEN_S_REG(SSI9_BUSIF0_MODE, 0x48c), - RSND_GEN_S_REG(SSI9_BUSIF0_ADINR, 0x484), - RSND_GEN_S_REG(SSI9_BUSIF0_DALIGN, 0x488), - RSND_GEN_S_REG(SSI9_BUSIF1_MODE, 0x4a0), - RSND_GEN_S_REG(SSI9_BUSIF1_ADINR, 0x4a4), - RSND_GEN_S_REG(SSI9_BUSIF1_DALIGN, 0x4a8), - RSND_GEN_S_REG(SSI9_BUSIF2_MODE, 0x4c0), - RSND_GEN_S_REG(SSI9_BUSIF2_ADINR, 0x4c4), - RSND_GEN_S_REG(SSI9_BUSIF2_DALIGN, 0x4c8), - RSND_GEN_S_REG(SSI9_BUSIF3_MODE, 0x4e0), - RSND_GEN_S_REG(SSI9_BUSIF3_ADINR, 0x4e4), - RSND_GEN_S_REG(SSI9_BUSIF3_DALIGN, 0x4e8), - RSND_GEN_S_REG(SSI9_BUSIF4_MODE, 0xd80), - RSND_GEN_S_REG(SSI9_BUSIF4_ADINR, 0xd84), - RSND_GEN_S_REG(SSI9_BUSIF4_DALIGN, 0xd88), - RSND_GEN_S_REG(SSI9_BUSIF5_MODE, 0xda0), - RSND_GEN_S_REG(SSI9_BUSIF5_ADINR, 0xda4), - RSND_GEN_S_REG(SSI9_BUSIF5_DALIGN, 0xda8), - RSND_GEN_S_REG(SSI9_BUSIF6_MODE, 0xdc0), - RSND_GEN_S_REG(SSI9_BUSIF6_ADINR, 0xdc4), - RSND_GEN_S_REG(SSI9_BUSIF6_DALIGN, 0xdc8), - RSND_GEN_S_REG(SSI9_BUSIF7_MODE, 0xde0), - RSND_GEN_S_REG(SSI9_BUSIF7_ADINR, 0xde4), - RSND_GEN_S_REG(SSI9_BUSIF7_DALIGN, 0xde8), -}; - -static const struct rsnd_regmap_field_conf conf_common_scu[] = { - RSND_GEN_M_REG(SRC_I_BUSIF_MODE, 0x0, 0x20), - RSND_GEN_M_REG(SRC_O_BUSIF_MODE, 0x4, 0x20), - RSND_GEN_M_REG(SRC_BUSIF_DALIGN, 0x8, 0x20), - RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), - RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), - RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), - RSND_GEN_M_REG(CMD_BUSIF_MODE, 0x184, 0x20), - RSND_GEN_M_REG(CMD_BUSIF_DALIGN, 0x188, 0x20), - RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20), - RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20), - RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), - RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc), - RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0), - RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4), - RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), - RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), - RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), - RSND_GEN_M_REG(SRC_IFSCR, 0x21c, 0x40), - RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40), - RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), - RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40), - RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40), - RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100), - RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100), - RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100), - RSND_GEN_M_REG(CTU_CPMDR, 0x510, 0x100), - RSND_GEN_M_REG(CTU_SCMDR, 0x514, 0x100), - RSND_GEN_M_REG(CTU_SV00R, 0x518, 0x100), - RSND_GEN_M_REG(CTU_SV01R, 0x51c, 0x100), - RSND_GEN_M_REG(CTU_SV02R, 0x520, 0x100), - RSND_GEN_M_REG(CTU_SV03R, 0x524, 0x100), - RSND_GEN_M_REG(CTU_SV04R, 0x528, 0x100), - RSND_GEN_M_REG(CTU_SV05R, 0x52c, 0x100), - RSND_GEN_M_REG(CTU_SV06R, 0x530, 0x100), - RSND_GEN_M_REG(CTU_SV07R, 0x534, 0x100), - RSND_GEN_M_REG(CTU_SV10R, 0x538, 0x100), - RSND_GEN_M_REG(CTU_SV11R, 0x53c, 0x100), - RSND_GEN_M_REG(CTU_SV12R, 0x540, 0x100), - RSND_GEN_M_REG(CTU_SV13R, 0x544, 0x100), - RSND_GEN_M_REG(CTU_SV14R, 0x548, 0x100), - RSND_GEN_M_REG(CTU_SV15R, 0x54c, 0x100), - RSND_GEN_M_REG(CTU_SV16R, 0x550, 0x100), - RSND_GEN_M_REG(CTU_SV17R, 0x554, 0x100), - RSND_GEN_M_REG(CTU_SV20R, 0x558, 0x100), - RSND_GEN_M_REG(CTU_SV21R, 0x55c, 0x100), - RSND_GEN_M_REG(CTU_SV22R, 0x560, 0x100), - RSND_GEN_M_REG(CTU_SV23R, 0x564, 0x100), - RSND_GEN_M_REG(CTU_SV24R, 0x568, 0x100), - RSND_GEN_M_REG(CTU_SV25R, 0x56c, 0x100), - RSND_GEN_M_REG(CTU_SV26R, 0x570, 0x100), - RSND_GEN_M_REG(CTU_SV27R, 0x574, 0x100), - RSND_GEN_M_REG(CTU_SV30R, 0x578, 0x100), - RSND_GEN_M_REG(CTU_SV31R, 0x57c, 0x100), - RSND_GEN_M_REG(CTU_SV32R, 0x580, 0x100), - RSND_GEN_M_REG(CTU_SV33R, 0x584, 0x100), - RSND_GEN_M_REG(CTU_SV34R, 0x588, 0x100), - RSND_GEN_M_REG(CTU_SV35R, 0x58c, 0x100), - RSND_GEN_M_REG(CTU_SV36R, 0x590, 0x100), - RSND_GEN_M_REG(CTU_SV37R, 0x594, 0x100), - RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40), - RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40), - RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40), - RSND_GEN_M_REG(MIX_MIXMR, 0xd10, 0x40), - RSND_GEN_M_REG(MIX_MVPDR, 0xd14, 0x40), - RSND_GEN_M_REG(MIX_MDBAR, 0xd18, 0x40), - RSND_GEN_M_REG(MIX_MDBBR, 0xd1c, 0x40), - RSND_GEN_M_REG(MIX_MDBCR, 0xd20, 0x40), - RSND_GEN_M_REG(MIX_MDBDR, 0xd24, 0x40), - RSND_GEN_M_REG(MIX_MDBER, 0xd28, 0x40), - RSND_GEN_M_REG(DVC_SWRSR, 0xe00, 0x100), - RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100), - RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100), - RSND_GEN_M_REG(DVC_DVUCR, 0xe10, 0x100), - RSND_GEN_M_REG(DVC_ZCMCR, 0xe14, 0x100), - RSND_GEN_M_REG(DVC_VRCTR, 0xe18, 0x100), - RSND_GEN_M_REG(DVC_VRPDR, 0xe1c, 0x100), - RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100), - RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100), - RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100), - RSND_GEN_M_REG(DVC_VOL2R, 0xe30, 0x100), - RSND_GEN_M_REG(DVC_VOL3R, 0xe34, 0x100), - RSND_GEN_M_REG(DVC_VOL4R, 0xe38, 0x100), - RSND_GEN_M_REG(DVC_VOL5R, 0xe3c, 0x100), - RSND_GEN_M_REG(DVC_VOL6R, 0xe40, 0x100), - RSND_GEN_M_REG(DVC_VOL7R, 0xe44, 0x100), - RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100), -}; - -static const struct rsnd_regmap_field_conf conf_common_adg[] = { - RSND_GEN_S_REG(BRRA, 0x00), - RSND_GEN_S_REG(BRRB, 0x04), - RSND_GEN_S_REG(BRGCKR, 0x08), - RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c), - RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10), - RSND_GEN_S_REG(AUDIO_CLK_SEL2, 0x14), - RSND_GEN_S_REG(DIV_EN, 0x30), - RSND_GEN_S_REG(SRCIN_TIMSEL0, 0x34), - RSND_GEN_S_REG(SRCIN_TIMSEL1, 0x38), - RSND_GEN_S_REG(SRCIN_TIMSEL2, 0x3c), - RSND_GEN_S_REG(SRCIN_TIMSEL3, 0x40), - RSND_GEN_S_REG(SRCIN_TIMSEL4, 0x44), - RSND_GEN_S_REG(SRCOUT_TIMSEL0, 0x48), - RSND_GEN_S_REG(SRCOUT_TIMSEL1, 0x4c), - RSND_GEN_S_REG(SRCOUT_TIMSEL2, 0x50), - RSND_GEN_S_REG(SRCOUT_TIMSEL3, 0x54), - RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58), - RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c), -}; - -static const struct rsnd_regmap_field_conf conf_common_ssi[] = { - RSND_GEN_M_REG(SSICR, 0x00, 0x40), - RSND_GEN_M_REG(SSISR, 0x04, 0x40), - RSND_GEN_M_REG(SSITDR, 0x08, 0x40), - RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40), - RSND_GEN_M_REG(SSIWSR, 0x20, 0x40), -}; - -/* - * Gen4 - */ -static int rsnd_gen4_probe(struct rsnd_priv *priv) -{ - struct rsnd_regmap_field_conf conf_null[] = { }; - - /* - * ssiu: SSIU0 - * ssi : SSI0 - */ - int ret_ssiu = rsnd_gen_regmap_init(priv, 1, RSND_BASE_SSIU, "ssiu", conf_common_ssiu); - int ret_ssi = rsnd_gen_regmap_init(priv, 1, RSND_BASE_SSI, "ssi", conf_common_ssi); - int ret_adg = rsnd_gen_regmap_init(priv, 1, RSND_BASE_ADG, "adg", conf_common_adg); - int ret_sdmc = rsnd_gen_regmap_init(priv, 1, RSND_BASE_SDMC, "sdmc", conf_null); - - return ret_adg | ret_ssiu | ret_ssi | ret_sdmc; -} - -/* - * Gen2 - */ -static int rsnd_gen2_probe(struct rsnd_priv *priv) -{ - /* - * ssi : SSI0 - SSI9 - * ssiu: SSIU0 - SSIU9 - * scu : SRC0 - SRC9 etc - */ - int ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SSI, "ssi", conf_common_ssi); - int ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SSIU, "ssiu", conf_common_ssiu); - int ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_BASE_SCU, "scu", conf_common_scu); - int ret_adg = rsnd_gen_regmap_init(priv, 1, RSND_BASE_ADG, "adg", conf_common_adg); - - return ret_ssi | ret_ssiu | ret_scu | ret_adg; -} - -/* - * Gen1 - */ - -static int rsnd_gen1_probe(struct rsnd_priv *priv) -{ - /* - * ssi : SSI0 - SSI8 - */ - int ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_BASE_SSI, "ssi", conf_common_ssi); - int ret_adg = rsnd_gen_regmap_init(priv, 1, RSND_BASE_ADG, "adg", conf_common_adg); - - return ret_adg | ret_ssi; -} - -/* - * Gen - */ -int rsnd_gen_probe(struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_gen *gen; - int ret; - - gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); - if (!gen) - return -ENOMEM; - - priv->gen = gen; - - ret = -ENODEV; - if (rsnd_is_gen1(priv)) - ret = rsnd_gen1_probe(priv); - else if (rsnd_is_gen2(priv) || - rsnd_is_gen3(priv)) - ret = rsnd_gen2_probe(priv); - else if (rsnd_is_gen4(priv)) - ret = rsnd_gen4_probe(priv); - - if (ret < 0) - dev_err(dev, "unknown generation R-Car sound device\n"); - - return ret; -} diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c deleted file mode 100644 index 024d91cc8748..000000000000 --- a/sound/soc/sh/rcar/mix.c +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// mix.c -// -// Copyright (c) 2015 Kuninori Morimoto - -/* - * CTUn MIXn - * +------+ +------+ - * [SRC3 / SRC6] -> |CTU n0| -> [MIX n0| -> - * [SRC4 / SRC9] -> |CTU n1| -> [MIX n1| -> - * [SRC0 / SRC1] -> |CTU n2| -> [MIX n2| -> - * [SRC2 / SRC5] -> |CTU n3| -> [MIX n3| -> - * +------+ +------+ - * - * ex) - * DAI0 : playback = <&src0 &ctu02 &mix0 &dvc0 &ssi0>; - * DAI1 : playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>; - * - * MIX Volume - * amixer set "MIX",0 100% // DAI0 Volume - * amixer set "MIX",1 100% // DAI1 Volume - * - * Volume Ramp - * amixer set "MIX Ramp Up Rate" "0.125 dB/1 step" - * amixer set "MIX Ramp Down Rate" "4 dB/1 step" - * amixer set "MIX Ramp" on - * aplay xxx.wav & - * amixer set "MIX",0 80% // DAI0 Volume Down - * amixer set "MIX",1 100% // DAI1 Volume Up - */ - -#include "rsnd.h" - -#define MIX_NAME_SIZE 16 -#define MIX_NAME "mix" - -struct rsnd_mix { - struct rsnd_mod mod; - struct rsnd_kctrl_cfg_s volumeA; /* MDBAR */ - struct rsnd_kctrl_cfg_s volumeB; /* MDBBR */ - struct rsnd_kctrl_cfg_s volumeC; /* MDBCR */ - struct rsnd_kctrl_cfg_s volumeD; /* MDBDR */ - struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ - struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ - struct rsnd_kctrl_cfg_s rdw; /* Ramp Rate Down */ - u32 flags; -}; - -#define ONCE_KCTRL_INITIALIZED (1 << 0) -#define HAS_VOLA (1 << 1) -#define HAS_VOLB (1 << 2) -#define HAS_VOLC (1 << 3) -#define HAS_VOLD (1 << 4) - -#define VOL_MAX 0x3ff - -#define rsnd_mod_to_mix(_mod) \ - container_of((_mod), struct rsnd_mix, mod) - -#define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id) -#define rsnd_mix_nr(priv) ((priv)->mix_nr) -#define for_each_rsnd_mix(pos, priv, i) \ - for ((i) = 0; \ - ((i) < rsnd_mix_nr(priv)) && \ - ((pos) = (struct rsnd_mix *)(priv)->mix + i); \ - i++) - -static void rsnd_mix_activation(struct rsnd_mod *mod) -{ - rsnd_mod_write(mod, MIX_SWRSR, 0); - rsnd_mod_write(mod, MIX_SWRSR, 1); -} - -static void rsnd_mix_halt(struct rsnd_mod *mod) -{ - rsnd_mod_write(mod, MIX_MIXIR, 1); - rsnd_mod_write(mod, MIX_SWRSR, 0); -} - -#define rsnd_mix_get_vol(mix, X) \ - rsnd_flags_has(mix, HAS_VOL##X) ? \ - (VOL_MAX - rsnd_kctrl_vals(mix->volume##X)) : 0 -static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mix *mix = rsnd_mod_to_mix(mod); - u32 volA = rsnd_mix_get_vol(mix, A); - u32 volB = rsnd_mix_get_vol(mix, B); - u32 volC = rsnd_mix_get_vol(mix, C); - u32 volD = rsnd_mix_get_vol(mix, D); - - dev_dbg(dev, "MIX A/B/C/D = %02x/%02x/%02x/%02x\n", - volA, volB, volC, volD); - - rsnd_mod_write(mod, MIX_MDBAR, volA); - rsnd_mod_write(mod, MIX_MDBBR, volB); - rsnd_mod_write(mod, MIX_MDBCR, volC); - rsnd_mod_write(mod, MIX_MDBDR, volD); -} - -static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_mix *mix = rsnd_mod_to_mix(mod); - - rsnd_mod_write(mod, MIX_MIXIR, 1); - - /* General Information */ - rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); - - /* volume step */ - rsnd_mod_write(mod, MIX_MIXMR, rsnd_kctrl_vals(mix->ren)); - rsnd_mod_write(mod, MIX_MVPDR, rsnd_kctrl_vals(mix->rup) << 8 | - rsnd_kctrl_vals(mix->rdw)); - - /* common volume parameter */ - rsnd_mix_volume_parameter(io, mod); - - rsnd_mod_write(mod, MIX_MIXIR, 0); -} - -static void rsnd_mix_volume_update(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - /* Disable MIX dB setting */ - rsnd_mod_write(mod, MIX_MDBER, 0); - - /* common volume parameter */ - rsnd_mix_volume_parameter(io, mod); - - /* Enable MIX dB setting */ - rsnd_mod_write(mod, MIX_MDBER, 1); -} - -static int rsnd_mix_probe_(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - return rsnd_cmd_attach(io, rsnd_mod_id(mod)); -} - -static int rsnd_mix_init(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - int ret; - - ret = rsnd_mod_power_on(mod); - if (ret < 0) - return ret; - - rsnd_mix_activation(mod); - - rsnd_mix_volume_init(io, mod); - - rsnd_mix_volume_update(io, mod); - - return 0; -} - -static int rsnd_mix_quit(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - rsnd_mix_halt(mod); - - rsnd_mod_power_off(mod); - - return 0; -} - -static int rsnd_mix_pcm_new(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_soc_pcm_runtime *rtd) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mix *mix = rsnd_mod_to_mix(mod); - struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - struct rsnd_kctrl_cfg_s *volume; - int ret; - - switch (rsnd_mod_id(src_mod)) { - case 3: - case 6: /* MDBAR */ - volume = &mix->volumeA; - rsnd_flags_set(mix, HAS_VOLA); - break; - case 4: - case 9: /* MDBBR */ - volume = &mix->volumeB; - rsnd_flags_set(mix, HAS_VOLB); - break; - case 0: - case 1: /* MDBCR */ - volume = &mix->volumeC; - rsnd_flags_set(mix, HAS_VOLC); - break; - case 2: - case 5: /* MDBDR */ - volume = &mix->volumeD; - rsnd_flags_set(mix, HAS_VOLD); - break; - default: - dev_err(dev, "unknown SRC is connected\n"); - return -EINVAL; - } - - /* Volume */ - ret = rsnd_kctrl_new_s(mod, io, rtd, - "MIX Playback Volume", - rsnd_kctrl_accept_anytime, - rsnd_mix_volume_update, - volume, VOL_MAX); - if (ret < 0) - return ret; - rsnd_kctrl_vals(*volume) = VOL_MAX; - - if (rsnd_flags_has(mix, ONCE_KCTRL_INITIALIZED)) - return ret; - - /* Ramp */ - ret = rsnd_kctrl_new_s(mod, io, rtd, - "MIX Ramp Switch", - rsnd_kctrl_accept_anytime, - rsnd_mix_volume_update, - &mix->ren, 1); - if (ret < 0) - return ret; - - ret = rsnd_kctrl_new_e(mod, io, rtd, - "MIX Ramp Up Rate", - rsnd_kctrl_accept_anytime, - rsnd_mix_volume_update, - &mix->rup, - volume_ramp_rate, - VOLUME_RAMP_MAX_MIX); - if (ret < 0) - return ret; - - ret = rsnd_kctrl_new_e(mod, io, rtd, - "MIX Ramp Down Rate", - rsnd_kctrl_accept_anytime, - rsnd_mix_volume_update, - &mix->rdw, - volume_ramp_rate, - VOLUME_RAMP_MAX_MIX); - - rsnd_flags_set(mix, ONCE_KCTRL_INITIALIZED); - - return ret; -} - -#ifdef CONFIG_DEBUG_FS -static void rsnd_mix_debug_info(struct seq_file *m, - struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, - 0xd00 + rsnd_mod_id(mod) * 0x40, 0x30); -} -#define DEBUG_INFO .debug_info = rsnd_mix_debug_info -#else -#define DEBUG_INFO -#endif - -static struct rsnd_mod_ops rsnd_mix_ops = { - .name = MIX_NAME, - .probe = rsnd_mix_probe_, - .init = rsnd_mix_init, - .quit = rsnd_mix_quit, - .pcm_new = rsnd_mix_pcm_new, - .get_status = rsnd_mod_get_status, - DEBUG_INFO -}; - -struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) -{ - if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv))) - id = 0; - - return rsnd_mod_get(rsnd_mix_get(priv, id)); -} - -int rsnd_mix_probe(struct rsnd_priv *priv) -{ - struct device_node *node; - struct device_node *np; - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mix *mix; - struct clk *clk; - char name[MIX_NAME_SIZE]; - int i, nr, ret; - - node = rsnd_mix_of_node(priv); - if (!node) - return 0; /* not used is not error */ - - nr = of_get_child_count(node); - if (!nr) { - ret = -EINVAL; - goto rsnd_mix_probe_done; - } - - mix = devm_kcalloc(dev, nr, sizeof(*mix), GFP_KERNEL); - if (!mix) { - ret = -ENOMEM; - goto rsnd_mix_probe_done; - } - - priv->mix_nr = nr; - priv->mix = mix; - - i = 0; - ret = 0; - for_each_child_of_node(node, np) { - mix = rsnd_mix_get(priv, i); - - snprintf(name, MIX_NAME_SIZE, "%s.%d", - MIX_NAME, i); - - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - of_node_put(np); - goto rsnd_mix_probe_done; - } - - ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, - clk, RSND_MOD_MIX, i); - if (ret) { - of_node_put(np); - goto rsnd_mix_probe_done; - } - - i++; - } - -rsnd_mix_probe_done: - of_node_put(node); - - return ret; -} - -void rsnd_mix_remove(struct rsnd_priv *priv) -{ - struct rsnd_mix *mix; - int i; - - for_each_rsnd_mix(mix, priv, i) { - rsnd_mod_quit(rsnd_mod_get(mix)); - } -} diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h deleted file mode 100644 index 3c164d8e3b16..000000000000 --- a/sound/soc/sh/rcar/rsnd.h +++ /dev/null @@ -1,896 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Renesas R-Car -// -// Copyright (C) 2013 Renesas Solutions Corp. -// Kuninori Morimoto - -#ifndef RSND_H -#define RSND_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RSND_BASE_ADG 0 -#define RSND_BASE_SSI 1 -#define RSND_BASE_SSIU 2 -#define RSND_BASE_SCU 3 // for Gen2/Gen3 -#define RSND_BASE_SDMC 3 // for Gen4 reuse -#define RSND_BASE_MAX 4 - -/* - * pseudo register - * - * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different. - * This driver uses pseudo register in order to hide it. - * see gen1/gen2 for detail - */ -enum rsnd_reg { - /* SCU (MIX/CTU/DVC) */ - SRC_I_BUSIF_MODE, - SRC_O_BUSIF_MODE, - SRC_ROUTE_MODE0, - SRC_SWRSR, - SRC_SRCIR, - SRC_ADINR, - SRC_IFSCR, - SRC_IFSVR, - SRC_SRCCR, - SRC_CTRL, - SRC_BSDSR, - SRC_BSISR, - SRC_INT_ENABLE0, - SRC_BUSIF_DALIGN, - SRCIN_TIMSEL0, - SRCIN_TIMSEL1, - SRCIN_TIMSEL2, - SRCIN_TIMSEL3, - SRCIN_TIMSEL4, - SRCOUT_TIMSEL0, - SRCOUT_TIMSEL1, - SRCOUT_TIMSEL2, - SRCOUT_TIMSEL3, - SRCOUT_TIMSEL4, - SCU_SYS_STATUS0, - SCU_SYS_STATUS1, - SCU_SYS_INT_EN0, - SCU_SYS_INT_EN1, - CMD_CTRL, - CMD_BUSIF_MODE, - CMD_BUSIF_DALIGN, - CMD_ROUTE_SLCT, - CMDOUT_TIMSEL, - CTU_SWRSR, - CTU_CTUIR, - CTU_ADINR, - CTU_CPMDR, - CTU_SCMDR, - CTU_SV00R, - CTU_SV01R, - CTU_SV02R, - CTU_SV03R, - CTU_SV04R, - CTU_SV05R, - CTU_SV06R, - CTU_SV07R, - CTU_SV10R, - CTU_SV11R, - CTU_SV12R, - CTU_SV13R, - CTU_SV14R, - CTU_SV15R, - CTU_SV16R, - CTU_SV17R, - CTU_SV20R, - CTU_SV21R, - CTU_SV22R, - CTU_SV23R, - CTU_SV24R, - CTU_SV25R, - CTU_SV26R, - CTU_SV27R, - CTU_SV30R, - CTU_SV31R, - CTU_SV32R, - CTU_SV33R, - CTU_SV34R, - CTU_SV35R, - CTU_SV36R, - CTU_SV37R, - MIX_SWRSR, - MIX_MIXIR, - MIX_ADINR, - MIX_MIXMR, - MIX_MVPDR, - MIX_MDBAR, - MIX_MDBBR, - MIX_MDBCR, - MIX_MDBDR, - MIX_MDBER, - DVC_SWRSR, - DVC_DVUIR, - DVC_ADINR, - DVC_DVUCR, - DVC_ZCMCR, - DVC_VOL0R, - DVC_VOL1R, - DVC_VOL2R, - DVC_VOL3R, - DVC_VOL4R, - DVC_VOL5R, - DVC_VOL6R, - DVC_VOL7R, - DVC_DVUER, - DVC_VRCTR, - DVC_VRPDR, - DVC_VRDBR, - - /* ADG */ - BRRA, - BRRB, - BRGCKR, - DIV_EN, - AUDIO_CLK_SEL0, - AUDIO_CLK_SEL1, - AUDIO_CLK_SEL2, - - /* SSIU */ - SSI_MODE, - SSI_MODE0, - SSI_MODE1, - SSI_MODE2, - SSI_CONTROL, - SSI_CTRL, - SSI_BUSIF0_MODE, - SSI_BUSIF1_MODE, - SSI_BUSIF2_MODE, - SSI_BUSIF3_MODE, - SSI_BUSIF4_MODE, - SSI_BUSIF5_MODE, - SSI_BUSIF6_MODE, - SSI_BUSIF7_MODE, - SSI_BUSIF0_ADINR, - SSI_BUSIF1_ADINR, - SSI_BUSIF2_ADINR, - SSI_BUSIF3_ADINR, - SSI_BUSIF4_ADINR, - SSI_BUSIF5_ADINR, - SSI_BUSIF6_ADINR, - SSI_BUSIF7_ADINR, - SSI_BUSIF0_DALIGN, - SSI_BUSIF1_DALIGN, - SSI_BUSIF2_DALIGN, - SSI_BUSIF3_DALIGN, - SSI_BUSIF4_DALIGN, - SSI_BUSIF5_DALIGN, - SSI_BUSIF6_DALIGN, - SSI_BUSIF7_DALIGN, - SSI_INT_ENABLE, - SSI_SYS_STATUS0, - SSI_SYS_STATUS1, - SSI_SYS_STATUS2, - SSI_SYS_STATUS3, - SSI_SYS_STATUS4, - SSI_SYS_STATUS5, - SSI_SYS_STATUS6, - SSI_SYS_STATUS7, - SSI_SYS_INT_ENABLE0, - SSI_SYS_INT_ENABLE1, - SSI_SYS_INT_ENABLE2, - SSI_SYS_INT_ENABLE3, - SSI_SYS_INT_ENABLE4, - SSI_SYS_INT_ENABLE5, - SSI_SYS_INT_ENABLE6, - SSI_SYS_INT_ENABLE7, - HDMI0_SEL, - HDMI1_SEL, - SSI9_BUSIF0_MODE, - SSI9_BUSIF1_MODE, - SSI9_BUSIF2_MODE, - SSI9_BUSIF3_MODE, - SSI9_BUSIF4_MODE, - SSI9_BUSIF5_MODE, - SSI9_BUSIF6_MODE, - SSI9_BUSIF7_MODE, - SSI9_BUSIF0_ADINR, - SSI9_BUSIF1_ADINR, - SSI9_BUSIF2_ADINR, - SSI9_BUSIF3_ADINR, - SSI9_BUSIF4_ADINR, - SSI9_BUSIF5_ADINR, - SSI9_BUSIF6_ADINR, - SSI9_BUSIF7_ADINR, - SSI9_BUSIF0_DALIGN, - SSI9_BUSIF1_DALIGN, - SSI9_BUSIF2_DALIGN, - SSI9_BUSIF3_DALIGN, - SSI9_BUSIF4_DALIGN, - SSI9_BUSIF5_DALIGN, - SSI9_BUSIF6_DALIGN, - SSI9_BUSIF7_DALIGN, - - /* SSI */ - SSICR, - SSISR, - SSITDR, - SSIRDR, - SSIWSR, - - REG_MAX, -}; -#define SRCIN_TIMSEL(i) (SRCIN_TIMSEL0 + (i)) -#define SRCOUT_TIMSEL(i) (SRCOUT_TIMSEL0 + (i)) -#define CTU_SVxxR(i, j) (CTU_SV00R + (i * 8) + (j)) -#define DVC_VOLxR(i) (DVC_VOL0R + (i)) -#define AUDIO_CLK_SEL(i) (AUDIO_CLK_SEL0 + (i)) -#define SSI_BUSIF_MODE(i) (SSI_BUSIF0_MODE + (i)) -#define SSI_BUSIF_ADINR(i) (SSI_BUSIF0_ADINR + (i)) -#define SSI_BUSIF_DALIGN(i) (SSI_BUSIF0_DALIGN + (i)) -#define SSI9_BUSIF_MODE(i) (SSI9_BUSIF0_MODE + (i)) -#define SSI9_BUSIF_ADINR(i) (SSI9_BUSIF0_ADINR + (i)) -#define SSI9_BUSIF_DALIGN(i) (SSI9_BUSIF0_DALIGN + (i)) -#define SSI_SYS_STATUS(i) (SSI_SYS_STATUS0 + (i)) -#define SSI_SYS_INT_ENABLE(i) (SSI_SYS_INT_ENABLE0 + (i)) - - -struct rsnd_priv; -struct rsnd_mod; -struct rsnd_dai; -struct rsnd_dai_stream; - -/* - * R-Car basic functions - */ -u32 rsnd_mod_read(struct rsnd_mod *mod, enum rsnd_reg reg); -void rsnd_mod_write(struct rsnd_mod *mod, enum rsnd_reg reg, u32 data); -void rsnd_mod_bset(struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); -u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); -u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); -u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod); - -/* - * R-Car DMA - */ -int rsnd_dma_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, struct rsnd_mod **dma_mod); -int rsnd_dma_probe(struct rsnd_priv *priv); -struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name, - struct rsnd_mod *mod, char *x); - -/* - * R-Car sound mod - */ -enum rsnd_mod_type { - RSND_MOD_AUDMAPP, - RSND_MOD_AUDMA, - RSND_MOD_DVC, - RSND_MOD_MIX, - RSND_MOD_CTU, - RSND_MOD_CMD, - RSND_MOD_SRC, - RSND_MOD_SSIM3, /* SSI multi 3 */ - RSND_MOD_SSIM2, /* SSI multi 2 */ - RSND_MOD_SSIM1, /* SSI multi 1 */ - RSND_MOD_SSIP, /* SSI parent */ - RSND_MOD_SSI, - RSND_MOD_SSIU, - RSND_MOD_MAX, -}; - -struct rsnd_mod_ops { - char *name; - struct dma_chan* (*dma_req)(struct rsnd_dai_stream *io, - struct rsnd_mod *mod); - int (*probe)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv); - int (*remove)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv); - int (*init)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv); - int (*quit)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv); - int (*start)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv); - int (*stop)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv); - int (*irq)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv, int enable); - int (*pcm_new)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_soc_pcm_runtime *rtd); - int (*hw_params)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params); - int (*pointer)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - snd_pcm_uframes_t *pointer); - int (*fallback)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv); - int (*prepare)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv); - int (*cleanup)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv); - int (*hw_free)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_pcm_substream *substream); - u32 *(*get_status)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - enum rsnd_mod_type type); - int (*id)(struct rsnd_mod *mod); - int (*id_sub)(struct rsnd_mod *mod); - int (*id_cmd)(struct rsnd_mod *mod); - -#ifdef CONFIG_DEBUG_FS - void (*debug_info)(struct seq_file *m, - struct rsnd_dai_stream *io, struct rsnd_mod *mod); -#endif -}; - -struct rsnd_dai_stream; -struct rsnd_mod { - int id; - enum rsnd_mod_type type; - struct rsnd_mod_ops *ops; - struct rsnd_priv *priv; - struct clk *clk; - u32 status; -}; -/* - * status - * - * 0xH000DCB0 - * - * B 0: init 1: quit - * C 0: start 1: stop - * D 0: hw_params 1: hw_free - * - * H is always called (see __rsnd_mod_call) - */ -#define __rsnd_mod_shift_init 4 -#define __rsnd_mod_shift_quit 4 -#define __rsnd_mod_shift_start 8 -#define __rsnd_mod_shift_stop 8 -#define __rsnd_mod_shift_hw_params 12 -#define __rsnd_mod_shift_hw_free 12 -#define __rsnd_mod_shift_probe 28 /* always called */ -#define __rsnd_mod_shift_remove 28 /* always called */ -#define __rsnd_mod_shift_irq 28 /* always called */ -#define __rsnd_mod_shift_pcm_new 28 /* always called */ -#define __rsnd_mod_shift_fallback 28 /* always called */ -#define __rsnd_mod_shift_pointer 28 /* always called */ -#define __rsnd_mod_shift_prepare 28 /* always called */ -#define __rsnd_mod_shift_cleanup 28 /* always called */ - -#define __rsnd_mod_add_probe 0 -#define __rsnd_mod_add_remove 0 -#define __rsnd_mod_add_prepare 0 -#define __rsnd_mod_add_cleanup 0 -#define __rsnd_mod_add_init 1 /* needs protect */ -#define __rsnd_mod_add_quit -1 /* needs protect */ -#define __rsnd_mod_add_start 1 /* needs protect */ -#define __rsnd_mod_add_stop -1 /* needs protect */ -#define __rsnd_mod_add_hw_params 1 /* needs protect */ -#define __rsnd_mod_add_hw_free -1 /* needs protect */ -#define __rsnd_mod_add_irq 0 -#define __rsnd_mod_add_pcm_new 0 -#define __rsnd_mod_add_fallback 0 -#define __rsnd_mod_add_pointer 0 - -#define __rsnd_mod_call_probe 0 -#define __rsnd_mod_call_remove 0 -#define __rsnd_mod_call_prepare 0 -#define __rsnd_mod_call_cleanup 0 -#define __rsnd_mod_call_init 0 /* needs protect */ -#define __rsnd_mod_call_quit 1 /* needs protect */ -#define __rsnd_mod_call_start 0 /* needs protect */ -#define __rsnd_mod_call_stop 1 /* needs protect */ -#define __rsnd_mod_call_hw_params 0 /* needs protect */ -#define __rsnd_mod_call_hw_free 1 /* needs protect */ -#define __rsnd_mod_call_irq 0 -#define __rsnd_mod_call_pcm_new 0 -#define __rsnd_mod_call_fallback 0 -#define __rsnd_mod_call_pointer 0 - -#define rsnd_mod_to_priv(mod) ((mod)->priv) -#define rsnd_mod_power_on(mod) clk_enable((mod)->clk) -#define rsnd_mod_power_off(mod) clk_disable((mod)->clk) -#define rsnd_mod_get(ip) (&(ip)->mod) - -int rsnd_mod_init(struct rsnd_priv *priv, - struct rsnd_mod *mod, - struct rsnd_mod_ops *ops, - struct clk *clk, - enum rsnd_mod_type type, - int id); -void rsnd_mod_quit(struct rsnd_mod *mod); -struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, - struct rsnd_mod *mod); -void rsnd_mod_interrupt(struct rsnd_mod *mod, - void (*callback)(struct rsnd_mod *mod, - struct rsnd_dai_stream *io)); -u32 *rsnd_mod_get_status(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - enum rsnd_mod_type type); -int rsnd_mod_id(struct rsnd_mod *mod); -int rsnd_mod_id_raw(struct rsnd_mod *mod); -int rsnd_mod_id_sub(struct rsnd_mod *mod); -char *rsnd_mod_name(struct rsnd_mod *mod); -struct rsnd_mod *rsnd_mod_next(int *iterator, - struct rsnd_dai_stream *io, - enum rsnd_mod_type *array, - int array_size); -#define for_each_rsnd_mod(iterator, pos, io) \ - for (iterator = 0; \ - (pos = rsnd_mod_next(&iterator, io, NULL, 0)); iterator++) -#define for_each_rsnd_mod_arrays(iterator, pos, io, array, size) \ - for (iterator = 0; \ - (pos = rsnd_mod_next(&iterator, io, array, size)); iterator++) -#define for_each_rsnd_mod_array(iterator, pos, io, array) \ - for_each_rsnd_mod_arrays(iterator, pos, io, array, ARRAY_SIZE(array)) - -void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name, - struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), - struct device_node *node, - struct device_node *playback, - struct device_node *capture); -int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name); -int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx); - -int rsnd_channel_normalization(int chan); -#define rsnd_runtime_channel_original(io) \ - rsnd_runtime_channel_original_with_params(io, NULL) -int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io, - struct snd_pcm_hw_params *params); -#define rsnd_runtime_channel_after_ctu(io) \ - rsnd_runtime_channel_after_ctu_with_params(io, NULL) -int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, - struct snd_pcm_hw_params *params); -#define rsnd_runtime_channel_for_ssi(io) \ - rsnd_runtime_channel_for_ssi_with_params(io, NULL) -int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, - struct snd_pcm_hw_params *params); -int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io); -int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io); -int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io); - -/* - * DT - */ -#define rsnd_parse_of_node(priv, node) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, node) -#define RSND_NODE_DAI "rcar_sound,dai" -#define RSND_NODE_SSI "rcar_sound,ssi" -#define RSND_NODE_SSIU "rcar_sound,ssiu" -#define RSND_NODE_SRC "rcar_sound,src" -#define RSND_NODE_CTU "rcar_sound,ctu" -#define RSND_NODE_MIX "rcar_sound,mix" -#define RSND_NODE_DVC "rcar_sound,dvc" - -/* - * R-Car sound DAI - */ -#define RSND_DAI_NAME_SIZE 16 -struct rsnd_dai_stream { - char name[RSND_DAI_NAME_SIZE]; - struct snd_pcm_substream *substream; - struct rsnd_mod *mod[RSND_MOD_MAX]; - struct rsnd_mod *dma; - struct rsnd_dai *rdai; - struct device *dmac_dev; /* for IPMMU */ - u32 converted_rate; /* converted sampling rate */ - int converted_chan; /* converted channels */ - u32 parent_ssi_status; - u32 flags; -}; - -/* flags */ -#define RSND_STREAM_HDMI0 (1 << 0) /* for HDMI0 */ -#define RSND_STREAM_HDMI1 (1 << 1) /* for HDMI1 */ -#define RSND_STREAM_TDM_SPLIT (1 << 2) /* for TDM split mode */ -#define RSND_HW_RULE_ERR (1 << 3) /* hw_rule error */ - -#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) -#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) -#define rsnd_io_to_mod_ssiu(io) rsnd_io_to_mod((io), RSND_MOD_SSIU) -#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP) -#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC) -#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU) -#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX) -#define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC) -#define rsnd_io_to_mod_cmd(io) rsnd_io_to_mod((io), RSND_MOD_CMD) -#define rsnd_io_to_rdai(io) ((io)->rdai) -#define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io))) -#define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) -#define rsnd_io_to_runtime(io) ((io)->substream ? \ - (io)->substream->runtime : NULL) -#define rsnd_io_converted_rate(io) ((io)->converted_rate) -#define rsnd_io_converted_chan(io) ((io)->converted_chan) -int rsnd_io_is_working(struct rsnd_dai_stream *io); - -struct rsnd_dai { - char name[RSND_DAI_NAME_SIZE]; - struct rsnd_dai_stream playback; - struct rsnd_dai_stream capture; - struct rsnd_priv *priv; - struct snd_pcm_hw_constraint_list constraint; - struct of_phandle_args dai_args; - - int max_channels; /* 2ch - 16ch */ - int ssi_lane; /* 1lane - 4lane */ - int chan_width; /* 16/24/32 bit width */ - - unsigned int clk_master:1; - unsigned int bit_clk_inv:1; - unsigned int frm_clk_inv:1; - unsigned int sys_delay:1; - unsigned int data_alignment:1; -}; - -#define rsnd_rdai_nr(priv) ((priv)->rdai_nr) -#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) -#define rsnd_rdai_to_priv(rdai) ((rdai)->priv) -#define for_each_rsnd_dai(rdai, priv, i) \ - for (i = 0; \ - (i < rsnd_rdai_nr(priv)) && \ - ((rdai) = rsnd_rdai_get(priv, i)); \ - i++) - -struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id); - -#define rsnd_rdai_channels_set(rdai, max_channels) \ - rsnd_rdai_channels_ctrl(rdai, max_channels) -#define rsnd_rdai_channels_get(rdai) \ - rsnd_rdai_channels_ctrl(rdai, 0) -int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai, - int max_channels); - -#define rsnd_rdai_ssi_lane_set(rdai, ssi_lane) \ - rsnd_rdai_ssi_lane_ctrl(rdai, ssi_lane) -#define rsnd_rdai_ssi_lane_get(rdai) \ - rsnd_rdai_ssi_lane_ctrl(rdai, 0) -int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai, - int ssi_lane); - -#define rsnd_rdai_width_set(rdai, width) \ - rsnd_rdai_width_ctrl(rdai, width) -#define rsnd_rdai_width_get(rdai) \ - rsnd_rdai_width_ctrl(rdai, 0) -int rsnd_rdai_width_ctrl(struct rsnd_dai *rdai, int width); -int rsnd_dai_connect(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - enum rsnd_mod_type type); - -/* - * R-Car Gen1/Gen2 - */ -int rsnd_gen_probe(struct rsnd_priv *priv); -void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg); -phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); -#ifdef CONFIG_DEBUG_FS -void __iomem *rsnd_gen_get_base_addr(struct rsnd_priv *priv, int reg_id); -#endif - -/* - * R-Car ADG - */ -int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate); -int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod); -int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate); -int rsnd_adg_probe(struct rsnd_priv *priv); -void rsnd_adg_remove(struct rsnd_priv *priv); -int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io, - unsigned int in_rate, - unsigned int out_rate); -int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, - struct rsnd_dai_stream *io); -#define rsnd_adg_clk_enable(priv) rsnd_adg_clk_control(priv, 1) -#define rsnd_adg_clk_disable(priv) rsnd_adg_clk_control(priv, 0) -void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable); -void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m); - -/* - * R-Car sound priv - */ -struct rsnd_priv { - - struct platform_device *pdev; - spinlock_t lock; - unsigned long flags; -#define RSND_GEN_MASK (0xF << 0) -#define RSND_GEN1 (1 << 0) -#define RSND_GEN2 (2 << 0) -#define RSND_GEN3 (3 << 0) -#define RSND_GEN4 (4 << 0) -#define RSND_SOC_MASK (0xFF << 4) -#define RSND_SOC_E (1 << 4) /* E1/E2/E3 */ - - /* - * below value will be filled on rsnd_gen_probe() - */ - void *gen; - - /* - * below value will be filled on rsnd_adg_probe() - */ - void *adg; - - /* - * below value will be filled on rsnd_dma_probe() - */ - void *dma; - - /* - * below value will be filled on rsnd_ssi_probe() - */ - void *ssi; - int ssi_nr; - - /* - * below value will be filled on rsnd_ssiu_probe() - */ - void *ssiu; - int ssiu_nr; - - /* - * below value will be filled on rsnd_src_probe() - */ - void *src; - int src_nr; - - /* - * below value will be filled on rsnd_ctu_probe() - */ - void *ctu; - int ctu_nr; - - /* - * below value will be filled on rsnd_mix_probe() - */ - void *mix; - int mix_nr; - - /* - * below value will be filled on rsnd_dvc_probe() - */ - void *dvc; - int dvc_nr; - - /* - * below value will be filled on rsnd_cmd_probe() - */ - void *cmd; - int cmd_nr; - - /* - * below value will be filled on rsnd_dai_probe() - */ - struct snd_soc_dai_driver *daidrv; - struct rsnd_dai *rdai; - int rdai_nr; - -#define RSND_MAX_COMPONENT 3 - int component_dais[RSND_MAX_COMPONENT]; -}; - -#define rsnd_priv_to_pdev(priv) ((priv)->pdev) -#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev)) - -#define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1) -#define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2) -#define rsnd_is_gen3(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN3) -#define rsnd_is_gen4(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN4) -#define rsnd_is_gen3_e3(priv) (((priv)->flags & \ - (RSND_GEN_MASK | RSND_SOC_MASK)) == \ - (RSND_GEN3 | RSND_SOC_E)) - -#define rsnd_flags_has(p, f) ((p)->flags & (f)) -#define rsnd_flags_set(p, f) ((p)->flags |= (f)) -#define rsnd_flags_del(p, f) ((p)->flags &= ~(f)) - -/* - * rsnd_kctrl - */ -struct rsnd_kctrl_cfg { - unsigned int max; - unsigned int size; - u32 *val; - const char * const *texts; - int (*accept)(struct rsnd_dai_stream *io); - void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod); - struct rsnd_dai_stream *io; - struct snd_card *card; - struct snd_kcontrol *kctrl; - struct rsnd_mod *mod; -}; - -#define RSND_MAX_CHANNELS 8 -struct rsnd_kctrl_cfg_m { - struct rsnd_kctrl_cfg cfg; - u32 val[RSND_MAX_CHANNELS]; -}; - -struct rsnd_kctrl_cfg_s { - struct rsnd_kctrl_cfg cfg; - u32 val; -}; -#define rsnd_kctrl_size(x) ((x).cfg.size) -#define rsnd_kctrl_max(x) ((x).cfg.max) -#define rsnd_kctrl_valm(x, i) ((x).val[i]) /* = (x).cfg.val[i] */ -#define rsnd_kctrl_vals(x) ((x).val) /* = (x).cfg.val[0] */ - -int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io); -int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io); -struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg); -struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg); -int rsnd_kctrl_new(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_soc_pcm_runtime *rtd, - const unsigned char *name, - int (*accept)(struct rsnd_dai_stream *io), - void (*update)(struct rsnd_dai_stream *io, - struct rsnd_mod *mod), - struct rsnd_kctrl_cfg *cfg, - const char * const *texts, - int size, - u32 max); - -#define rsnd_kctrl_new_m(mod, io, rtd, name, accept, update, cfg, size, max) \ - rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_m(cfg), \ - NULL, size, max) - -#define rsnd_kctrl_new_s(mod, io, rtd, name, accept, update, cfg, max) \ - rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ - NULL, 1, max) - -#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts, size) \ - rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ - texts, 1, size) - -extern const char * const volume_ramp_rate[]; -#define VOLUME_RAMP_MAX_DVC (0x17 + 1) -#define VOLUME_RAMP_MAX_MIX (0x0a + 1) - -/* - * R-Car SSI - */ -int rsnd_ssi_probe(struct rsnd_priv *priv); -void rsnd_ssi_remove(struct rsnd_priv *priv); -struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); -u32 rsnd_ssi_multi_secondaries_runtime(struct rsnd_dai_stream *io); -int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); - -#define rsnd_ssi_is_pin_sharing(io) \ - __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) -int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); - -#define rsnd_ssi_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSI) -void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, - struct device_node *playback, - struct device_node *capture); -unsigned int rsnd_ssi_clk_query(struct rsnd_dai *rdai, - int param1, int param2, int *idx); - -/* - * R-Car SSIU - */ -int rsnd_ssiu_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *mod); -int rsnd_ssiu_probe(struct rsnd_priv *priv); -void rsnd_ssiu_remove(struct rsnd_priv *priv); -void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, - struct device_node *playback, - struct device_node *capture); -#define rsnd_ssiu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSIU) -bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod); - -/* - * R-Car SRC - */ -int rsnd_src_probe(struct rsnd_priv *priv); -void rsnd_src_remove(struct rsnd_priv *priv); -struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); - -#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1) -#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0) -unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - int is_in); - -#define rsnd_src_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SRC) -#define rsnd_parse_connect_src(rdai, playback, capture) \ - rsnd_parse_connect_common(rdai, "src", rsnd_src_mod_get, \ - rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \ - playback, capture) - -/* - * R-Car CTU - */ -int rsnd_ctu_probe(struct rsnd_priv *priv); -void rsnd_ctu_remove(struct rsnd_priv *priv); -struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); -#define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU) -#define rsnd_parse_connect_ctu(rdai, playback, capture) \ - rsnd_parse_connect_common(rdai, "ctu", rsnd_ctu_mod_get, \ - rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \ - playback, capture) - -/* - * R-Car MIX - */ -int rsnd_mix_probe(struct rsnd_priv *priv); -void rsnd_mix_remove(struct rsnd_priv *priv); -struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); -#define rsnd_mix_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_MIX) -#define rsnd_parse_connect_mix(rdai, playback, capture) \ - rsnd_parse_connect_common(rdai, "mix", rsnd_mix_mod_get, \ - rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \ - playback, capture) - -/* - * R-Car DVC - */ -int rsnd_dvc_probe(struct rsnd_priv *priv); -void rsnd_dvc_remove(struct rsnd_priv *priv); -struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); -#define rsnd_dvc_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DVC) -#define rsnd_parse_connect_dvc(rdai, playback, capture) \ - rsnd_parse_connect_common(rdai, "dvc", rsnd_dvc_mod_get, \ - rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \ - playback, capture) - -/* - * R-Car CMD - */ -int rsnd_cmd_probe(struct rsnd_priv *priv); -void rsnd_cmd_remove(struct rsnd_priv *priv); -int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id); - -void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type); - -/* - * If you don't need interrupt status debug message, - * define RSND_DEBUG_NO_IRQ_STATUS as 1 on top of src.c/ssi.c - * - * #define RSND_DEBUG_NO_IRQ_STATUS 1 - */ -#define rsnd_print_irq_status(dev, param...) do { \ - if (!IS_BUILTIN(RSND_DEBUG_NO_IRQ_STATUS)) \ - dev_info(dev, param); \ -} while (0) - -#ifdef CONFIG_DEBUG_FS -int rsnd_debugfs_probe(struct snd_soc_component *component); -void rsnd_debugfs_reg_show(struct seq_file *m, phys_addr_t _addr, - void __iomem *base, int offset, int size); -void rsnd_debugfs_mod_reg_show(struct seq_file *m, struct rsnd_mod *mod, - int reg_id, int offset, int size); - -#else -#define rsnd_debugfs_probe NULL -#endif - -#endif /* RSND_H */ diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c deleted file mode 100644 index e7f86db0d94c..000000000000 --- a/sound/soc/sh/rcar/src.c +++ /dev/null @@ -1,732 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Renesas R-Car SRC support -// -// Copyright (C) 2013 Renesas Solutions Corp. -// Kuninori Morimoto - -/* - * You can use Synchronous Sampling Rate Convert (if no DVC) - * - * amixer set "SRC Out Rate" on - * aplay xxx.wav & - * amixer set "SRC Out Rate" 96000 // convert rate to 96000Hz - * amixer set "SRC Out Rate" 22050 // convert rate to 22050Hz - */ - -/* - * you can enable below define if you don't need - * SSI interrupt status debug message when debugging - * see rsnd_print_irq_status() - * - * #define RSND_DEBUG_NO_IRQ_STATUS 1 - */ - -#include -#include "rsnd.h" - -#define SRC_NAME "src" - -/* SCU_SYSTEM_STATUS0/1 */ -#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) - -struct rsnd_src { - struct rsnd_mod mod; - struct rsnd_mod *dma; - struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ - struct rsnd_kctrl_cfg_s sync; /* sync convert */ - int irq; -}; - -#define RSND_SRC_NAME_SIZE 16 - -#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) -#define rsnd_src_nr(priv) ((priv)->src_nr) -#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val) - -#define rsnd_mod_to_src(_mod) \ - container_of((_mod), struct rsnd_src, mod) - -#define for_each_rsnd_src(pos, priv, i) \ - for ((i) = 0; \ - ((i) < rsnd_src_nr(priv)) && \ - ((pos) = (struct rsnd_src *)(priv)->src + i); \ - i++) - - -/* - * image of SRC (Sampling Rate Converter) - * - * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ - * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | - * 44.1kHz <-> +-----+ +-----+ +-------+ - * ... - * - */ - -static void rsnd_src_activation(struct rsnd_mod *mod) -{ - rsnd_mod_write(mod, SRC_SWRSR, 0); - rsnd_mod_write(mod, SRC_SWRSR, 1); -} - -static void rsnd_src_halt(struct rsnd_mod *mod) -{ - rsnd_mod_write(mod, SRC_SRCIR, 1); - rsnd_mod_write(mod, SRC_SWRSR, 0); -} - -static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - int is_play = rsnd_io_is_play(io); - - return rsnd_dma_request_channel(rsnd_src_of_node(priv), - SRC_NAME, mod, - is_play ? "rx" : "tx"); -} - -static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate; - - if (!runtime) - return 0; - - if (!rsnd_src_sync_is_enabled(mod)) - return rsnd_io_converted_rate(io); - - convert_rate = src->sync.val; - - if (!convert_rate) - convert_rate = rsnd_io_converted_rate(io); - - if (!convert_rate) - convert_rate = runtime->rate; - - return convert_rate; -} - -unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - int is_in) -{ - struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - unsigned int rate = 0; - int is_play = rsnd_io_is_play(io); - - /* - * Playback - * runtime_rate -> [SRC] -> convert_rate - * - * Capture - * convert_rate -> [SRC] -> runtime_rate - */ - - if (is_play == is_in) - return runtime->rate; - - /* - * return convert rate if SRC is used, - * otherwise, return runtime->rate as usual - */ - if (src_mod) - rate = rsnd_src_convert_rate(io, src_mod); - - if (!rate) - rate = runtime->rate; - - return rate; -} - -static const u32 bsdsr_table_pattern1[] = { - 0x01800000, /* 6 - 1/6 */ - 0x01000000, /* 6 - 1/4 */ - 0x00c00000, /* 6 - 1/3 */ - 0x00800000, /* 6 - 1/2 */ - 0x00600000, /* 6 - 2/3 */ - 0x00400000, /* 6 - 1 */ -}; - -static const u32 bsdsr_table_pattern2[] = { - 0x02400000, /* 6 - 1/6 */ - 0x01800000, /* 6 - 1/4 */ - 0x01200000, /* 6 - 1/3 */ - 0x00c00000, /* 6 - 1/2 */ - 0x00900000, /* 6 - 2/3 */ - 0x00600000, /* 6 - 1 */ -}; - -static const u32 bsisr_table[] = { - 0x00100060, /* 6 - 1/6 */ - 0x00100040, /* 6 - 1/4 */ - 0x00100030, /* 6 - 1/3 */ - 0x00100020, /* 6 - 1/2 */ - 0x00100020, /* 6 - 2/3 */ - 0x00100020, /* 6 - 1 */ -}; - -static const u32 chan288888[] = { - 0x00000006, /* 1 to 2 */ - 0x000001fe, /* 1 to 8 */ - 0x000001fe, /* 1 to 8 */ - 0x000001fe, /* 1 to 8 */ - 0x000001fe, /* 1 to 8 */ - 0x000001fe, /* 1 to 8 */ -}; - -static const u32 chan244888[] = { - 0x00000006, /* 1 to 2 */ - 0x0000001e, /* 1 to 4 */ - 0x0000001e, /* 1 to 4 */ - 0x000001fe, /* 1 to 8 */ - 0x000001fe, /* 1 to 8 */ - 0x000001fe, /* 1 to 8 */ -}; - -static const u32 chan222222[] = { - 0x00000006, /* 1 to 2 */ - 0x00000006, /* 1 to 2 */ - 0x00000006, /* 1 to 2 */ - 0x00000006, /* 1 to 2 */ - 0x00000006, /* 1 to 2 */ - 0x00000006, /* 1 to 2 */ -}; - -static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - int is_play = rsnd_io_is_play(io); - int use_src = 0; - u32 fin, fout; - u32 ifscr, fsrate, adinr; - u32 cr, route; - u32 i_busif, o_busif, tmp; - const u32 *bsdsr_table; - const u32 *chptn; - uint ratio; - int chan; - int idx; - - if (!runtime) - return; - - fin = rsnd_src_get_in_rate(priv, io); - fout = rsnd_src_get_out_rate(priv, io); - - chan = rsnd_runtime_channel_original(io); - - /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ - if (fin == fout) - ratio = 0; - else if (fin > fout) - ratio = 100 * fin / fout; - else - ratio = 100 * fout / fin; - - if (ratio > 600) { - dev_err(dev, "FSO/FSI ratio error\n"); - return; - } - - use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod); - - /* - * SRC_ADINR - */ - adinr = rsnd_get_adinr_bit(mod, io) | chan; - - /* - * SRC_IFSCR / SRC_IFSVR - */ - ifscr = 0; - fsrate = 0; - if (use_src) { - u64 n; - - ifscr = 1; - n = (u64)0x0400000 * fin; - do_div(n, fout); - fsrate = n; - } - - /* - * SRC_SRCCR / SRC_ROUTE_MODE0 - */ - cr = 0x00011110; - route = 0x0; - if (use_src) { - route = 0x1; - - if (rsnd_src_sync_is_enabled(mod)) { - cr |= 0x1; - route |= rsnd_io_is_play(io) ? - (0x1 << 24) : (0x1 << 25); - } - } - - /* - * SRC_BSDSR / SRC_BSISR - * - * see - * Combination of Register Setting Related to - * FSO/FSI Ratio and Channel, Latency - */ - switch (rsnd_mod_id(mod)) { - case 0: - chptn = chan288888; - bsdsr_table = bsdsr_table_pattern1; - break; - case 1: - case 3: - case 4: - chptn = chan244888; - bsdsr_table = bsdsr_table_pattern1; - break; - case 2: - case 9: - chptn = chan222222; - bsdsr_table = bsdsr_table_pattern1; - break; - case 5: - case 6: - case 7: - case 8: - chptn = chan222222; - bsdsr_table = bsdsr_table_pattern2; - break; - default: - goto convert_rate_err; - } - - /* - * E3 need to overwrite - */ - if (rsnd_is_gen3_e3(priv)) - switch (rsnd_mod_id(mod)) { - case 0: - case 4: - chptn = chan222222; - } - - for (idx = 0; idx < ARRAY_SIZE(chan222222); idx++) - if (chptn[idx] & (1 << chan)) - break; - - if (chan > 8 || - idx >= ARRAY_SIZE(chan222222)) - goto convert_rate_err; - - /* BUSIF_MODE */ - tmp = rsnd_get_busif_shift(io, mod); - i_busif = ( is_play ? tmp : 0) | 1; - o_busif = (!is_play ? tmp : 0) | 1; - - rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); - - rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ - rsnd_mod_write(mod, SRC_ADINR, adinr); - rsnd_mod_write(mod, SRC_IFSCR, ifscr); - rsnd_mod_write(mod, SRC_IFSVR, fsrate); - rsnd_mod_write(mod, SRC_SRCCR, cr); - rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]); - rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]); - rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ - - rsnd_mod_write(mod, SRC_I_BUSIF_MODE, i_busif); - rsnd_mod_write(mod, SRC_O_BUSIF_MODE, o_busif); - - rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); - - rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); - - return; - -convert_rate_err: - dev_err(dev, "unknown BSDSR/BSDIR settings\n"); -} - -static int rsnd_src_irq(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv, - int enable) -{ - struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 sys_int_val, int_val, sys_int_mask; - int irq = src->irq; - int id = rsnd_mod_id(mod); - - sys_int_val = - sys_int_mask = OUF_SRC(id); - int_val = 0x3300; - - /* - * IRQ is not supported on non-DT - * see - * rsnd_src_probe_() - */ - if ((irq <= 0) || !enable) { - sys_int_val = 0; - int_val = 0; - } - - /* - * WORKAROUND - * - * ignore over flow error when rsnd_src_sync_is_enabled() - */ - if (rsnd_src_sync_is_enabled(mod)) - sys_int_val = sys_int_val & 0xffff; - - rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); - rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); - rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); - - return 0; -} - -static void rsnd_src_status_clear(struct rsnd_mod *mod) -{ - u32 val = OUF_SRC(rsnd_mod_id(mod)); - - rsnd_mod_write(mod, SCU_SYS_STATUS0, val); - rsnd_mod_write(mod, SCU_SYS_STATUS1, val); -} - -static bool rsnd_src_error_occurred(struct rsnd_mod *mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - u32 val0, val1; - u32 status0, status1; - bool ret = false; - - val0 = val1 = OUF_SRC(rsnd_mod_id(mod)); - - /* - * WORKAROUND - * - * ignore over flow error when rsnd_src_sync_is_enabled() - */ - if (rsnd_src_sync_is_enabled(mod)) - val0 = val0 & 0xffff; - - status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0); - status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1); - if ((status0 & val0) || (status1 & val1)) { - rsnd_print_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n", - rsnd_mod_name(mod), status0, status1); - - ret = true; - } - - return ret; -} - -static int rsnd_src_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - u32 val; - - /* - * WORKAROUND - * - * Enable SRC output if you want to use sync convert together with DVC - */ - val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ? - 0x01 : 0x11; - - rsnd_mod_write(mod, SRC_CTRL, val); - - return 0; -} - -static int rsnd_src_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - rsnd_mod_write(mod, SRC_CTRL, 0); - - return 0; -} - -static int rsnd_src_init(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_src *src = rsnd_mod_to_src(mod); - int ret; - - /* reset sync convert_rate */ - src->sync.val = 0; - - ret = rsnd_mod_power_on(mod); - if (ret < 0) - return ret; - - rsnd_src_activation(mod); - - rsnd_src_set_convert_rate(io, mod); - - rsnd_src_status_clear(mod); - - return 0; -} - -static int rsnd_src_quit(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_src *src = rsnd_mod_to_src(mod); - - rsnd_src_halt(mod); - - rsnd_mod_power_off(mod); - - /* reset sync convert_rate */ - src->sync.val = 0; - - return 0; -} - -static void __rsnd_src_interrupt(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - bool stop = false; - - spin_lock(&priv->lock); - - /* ignore all cases if not working */ - if (!rsnd_io_is_working(io)) - goto rsnd_src_interrupt_out; - - if (rsnd_src_error_occurred(mod)) - stop = true; - - rsnd_src_status_clear(mod); -rsnd_src_interrupt_out: - - spin_unlock(&priv->lock); - - if (stop) - snd_pcm_stop_xrun(io->substream); -} - -static irqreturn_t rsnd_src_interrupt(int irq, void *data) -{ - struct rsnd_mod *mod = data; - - rsnd_mod_interrupt(mod, __rsnd_src_interrupt); - - return IRQ_HANDLED; -} - -static int rsnd_src_probe_(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_src *src = rsnd_mod_to_src(mod); - struct device *dev = rsnd_priv_to_dev(priv); - int irq = src->irq; - int ret; - - if (irq > 0) { - /* - * IRQ is not supported on non-DT - * see - * rsnd_src_irq() - */ - ret = devm_request_irq(dev, irq, - rsnd_src_interrupt, - IRQF_SHARED, - dev_name(dev), mod); - if (ret) - return ret; - } - - ret = rsnd_dma_attach(io, mod, &src->dma); - - return ret; -} - -static int rsnd_src_pcm_new(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_soc_pcm_runtime *rtd) -{ - struct rsnd_src *src = rsnd_mod_to_src(mod); - int ret; - - /* - * enable SRC sync convert if possible - */ - - /* - * It can't use SRC Synchronous convert - * when Capture if it uses CMD - */ - if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io)) - return 0; - - /* - * enable sync convert - */ - ret = rsnd_kctrl_new_s(mod, io, rtd, - rsnd_io_is_play(io) ? - "SRC Out Rate Switch" : - "SRC In Rate Switch", - rsnd_kctrl_accept_anytime, - rsnd_src_set_convert_rate, - &src->sen, 1); - if (ret < 0) - return ret; - - ret = rsnd_kctrl_new_s(mod, io, rtd, - rsnd_io_is_play(io) ? - "SRC Out Rate" : - "SRC In Rate", - rsnd_kctrl_accept_runtime, - rsnd_src_set_convert_rate, - &src->sync, 192000); - - return ret; -} - -#ifdef CONFIG_DEBUG_FS -static void rsnd_src_debug_info(struct seq_file *m, - struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, - rsnd_mod_id(mod) * 0x20, 0x20); - seq_puts(m, "\n"); - rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, - 0x1c0, 0x20); - seq_puts(m, "\n"); - rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, - 0x200 + rsnd_mod_id(mod) * 0x40, 0x40); -} -#define DEBUG_INFO .debug_info = rsnd_src_debug_info -#else -#define DEBUG_INFO -#endif - -static struct rsnd_mod_ops rsnd_src_ops = { - .name = SRC_NAME, - .dma_req = rsnd_src_dma_req, - .probe = rsnd_src_probe_, - .init = rsnd_src_init, - .quit = rsnd_src_quit, - .start = rsnd_src_start, - .stop = rsnd_src_stop, - .irq = rsnd_src_irq, - .pcm_new = rsnd_src_pcm_new, - .get_status = rsnd_mod_get_status, - DEBUG_INFO -}; - -struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) -{ - if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) - id = 0; - - return rsnd_mod_get(rsnd_src_get(priv, id)); -} - -int rsnd_src_probe(struct rsnd_priv *priv) -{ - struct device_node *node; - struct device_node *np; - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_src *src; - struct clk *clk; - char name[RSND_SRC_NAME_SIZE]; - int i, nr, ret; - - node = rsnd_src_of_node(priv); - if (!node) - return 0; /* not used is not error */ - - nr = rsnd_node_count(priv, node, SRC_NAME); - if (!nr) { - ret = -EINVAL; - goto rsnd_src_probe_done; - } - - src = devm_kcalloc(dev, nr, sizeof(*src), GFP_KERNEL); - if (!src) { - ret = -ENOMEM; - goto rsnd_src_probe_done; - } - - priv->src_nr = nr; - priv->src = src; - - i = 0; - for_each_child_of_node(node, np) { - if (!of_device_is_available(np)) - goto skip; - - i = rsnd_node_fixed_index(dev, np, SRC_NAME, i); - if (i < 0) { - ret = -EINVAL; - of_node_put(np); - goto rsnd_src_probe_done; - } - - src = rsnd_src_get(priv, i); - - snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d", - SRC_NAME, i); - - src->irq = irq_of_parse_and_map(np, 0); - if (!src->irq) { - ret = -EINVAL; - of_node_put(np); - goto rsnd_src_probe_done; - } - - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - of_node_put(np); - goto rsnd_src_probe_done; - } - - ret = rsnd_mod_init(priv, rsnd_mod_get(src), - &rsnd_src_ops, clk, RSND_MOD_SRC, i); - if (ret) { - of_node_put(np); - goto rsnd_src_probe_done; - } - -skip: - i++; - } - - ret = 0; - -rsnd_src_probe_done: - of_node_put(node); - - return ret; -} - -void rsnd_src_remove(struct rsnd_priv *priv) -{ - struct rsnd_src *src; - int i; - - for_each_rsnd_src(src, priv, i) { - rsnd_mod_quit(rsnd_mod_get(src)); - } -} diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c deleted file mode 100644 index b3d4e8ae07ef..000000000000 --- a/sound/soc/sh/rcar/ssi.c +++ /dev/null @@ -1,1260 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Renesas R-Car SSIU/SSI support -// -// Copyright (C) 2013 Renesas Solutions Corp. -// Kuninori Morimoto -// -// Based on fsi.c -// Kuninori Morimoto - -/* - * you can enable below define if you don't need - * SSI interrupt status debug message when debugging - * see rsnd_print_irq_status() - * - * #define RSND_DEBUG_NO_IRQ_STATUS 1 - */ - -#include -#include -#include -#include -#include "rsnd.h" -#define RSND_SSI_NAME_SIZE 16 - -/* - * SSICR - */ -#define FORCE (1u << 31) /* Fixed */ -#define DMEN (1u << 28) /* DMA Enable */ -#define UIEN (1u << 27) /* Underflow Interrupt Enable */ -#define OIEN (1u << 26) /* Overflow Interrupt Enable */ -#define IIEN (1u << 25) /* Idle Mode Interrupt Enable */ -#define DIEN (1u << 24) /* Data Interrupt Enable */ -#define CHNL_4 (1u << 22) /* Channels */ -#define CHNL_6 (2u << 22) /* Channels */ -#define CHNL_8 (3u << 22) /* Channels */ -#define DWL_MASK (7u << 19) /* Data Word Length mask */ -#define DWL_8 (0u << 19) /* Data Word Length */ -#define DWL_16 (1u << 19) /* Data Word Length */ -#define DWL_18 (2u << 19) /* Data Word Length */ -#define DWL_20 (3u << 19) /* Data Word Length */ -#define DWL_22 (4u << 19) /* Data Word Length */ -#define DWL_24 (5u << 19) /* Data Word Length */ -#define DWL_32 (6u << 19) /* Data Word Length */ - -/* - * System word length - */ -#define SWL_16 (1 << 16) /* R/W System Word Length */ -#define SWL_24 (2 << 16) /* R/W System Word Length */ -#define SWL_32 (3 << 16) /* R/W System Word Length */ - -#define SCKD (1 << 15) /* Serial Bit Clock Direction */ -#define SWSD (1 << 14) /* Serial WS Direction */ -#define SCKP (1 << 13) /* Serial Bit Clock Polarity */ -#define SWSP (1 << 12) /* Serial WS Polarity */ -#define SDTA (1 << 10) /* Serial Data Alignment */ -#define PDTA (1 << 9) /* Parallel Data Alignment */ -#define DEL (1 << 8) /* Serial Data Delay */ -#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ -#define TRMD (1 << 1) /* Transmit/Receive Mode Select */ -#define EN (1 << 0) /* SSI Module Enable */ - -/* - * SSISR - */ -#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */ -#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */ -#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ -#define DIRQ (1 << 24) /* Data Interrupt Status Flag */ - -/* - * SSIWSR - */ -#define CONT (1 << 8) /* WS Continue Function */ -#define WS_MODE (1 << 0) /* WS Mode */ - -#define SSI_NAME "ssi" - -struct rsnd_ssi { - struct rsnd_mod mod; - - u32 flags; - u32 cr_own; - u32 cr_clk; - u32 cr_mode; - u32 cr_en; - u32 wsr; - int chan; - int rate; - int irq; - unsigned int usrcnt; - - /* for PIO */ - int byte_pos; - int byte_per_period; - int next_period_byte; -}; - -/* flags */ -#define RSND_SSI_CLK_PIN_SHARE (1 << 0) -#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ -#define RSND_SSI_PROBED (1 << 2) - -#define for_each_rsnd_ssi(pos, priv, i) \ - for (i = 0; \ - (i < rsnd_ssi_nr(priv)) && \ - ((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \ - i++) - -#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id) -#define rsnd_ssi_nr(priv) ((priv)->ssi_nr) -#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) -#define rsnd_ssi_is_multi_secondary(mod, io) \ - (rsnd_ssi_multi_secondaries(io) & (1 << rsnd_mod_id(mod))) -#define rsnd_ssi_is_run_mods(mod, io) \ - (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) -#define rsnd_ssi_can_output_clk(mod) (!__rsnd_ssi_is_pin_sharing(mod)) - -int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) -{ - struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - int use_busif = 0; - - if (!rsnd_ssi_is_dma_mode(mod)) - return 0; - - if (!(rsnd_flags_has(ssi, RSND_SSI_NO_BUSIF))) - use_busif = 1; - if (rsnd_io_to_mod_src(io)) - use_busif = 1; - - return use_busif; -} - -static void rsnd_ssi_status_clear(struct rsnd_mod *mod) -{ - rsnd_mod_write(mod, SSISR, 0); -} - -static u32 rsnd_ssi_status_get(struct rsnd_mod *mod) -{ - return rsnd_mod_read(mod, SSISR); -} - -static void rsnd_ssi_status_check(struct rsnd_mod *mod, - u32 bit) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - u32 status; - int i; - - for (i = 0; i < 1024; i++) { - status = rsnd_ssi_status_get(mod); - if (status & bit) - return; - - udelay(5); - } - - dev_warn(dev, "%s status check failed\n", rsnd_mod_name(mod)); -} - -static u32 rsnd_ssi_multi_secondaries(struct rsnd_dai_stream *io) -{ - static const enum rsnd_mod_type types[] = { - RSND_MOD_SSIM1, - RSND_MOD_SSIM2, - RSND_MOD_SSIM3, - }; - int i, mask; - - mask = 0; - for (i = 0; i < ARRAY_SIZE(types); i++) { - struct rsnd_mod *mod = rsnd_io_to_mod(io, types[i]); - - if (!mod) - continue; - - mask |= 1 << rsnd_mod_id(mod); - } - - return mask; -} - -static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) -{ - struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); - struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); - u32 mods; - - mods = rsnd_ssi_multi_secondaries_runtime(io) | - 1 << rsnd_mod_id(ssi_mod); - - if (ssi_parent_mod) - mods |= 1 << rsnd_mod_id(ssi_parent_mod); - - return mods; -} - -u32 rsnd_ssi_multi_secondaries_runtime(struct rsnd_dai_stream *io) -{ - if (rsnd_runtime_is_multi_ssi(io)) - return rsnd_ssi_multi_secondaries(io); - - return 0; -} - -static u32 rsnd_rdai_width_to_swl(struct rsnd_dai *rdai) -{ - struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); - struct device *dev = rsnd_priv_to_dev(priv); - int width = rsnd_rdai_width_get(rdai); - - switch (width) { - case 32: return SWL_32; - case 24: return SWL_24; - case 16: return SWL_16; - } - - dev_err(dev, "unsupported slot width value: %d\n", width); - return 0; -} - -unsigned int rsnd_ssi_clk_query(struct rsnd_dai *rdai, - int param1, int param2, int *idx) -{ - struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); - static const int ssi_clk_mul_table[] = { - 1, 2, 4, 8, 16, 6, 12, - }; - int j, ret; - unsigned int main_rate; - int width = rsnd_rdai_width_get(rdai); - - for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { - - /* - * It will set SSIWSR.CONT here, but SSICR.CKDV = 000 - * with it is not allowed. (SSIWSR.WS_MODE with - * SSICR.CKDV = 000 is not allowed either). - * Skip it. See SSICR.CKDV - */ - if (j == 0) - continue; - - main_rate = width * param1 * param2 * ssi_clk_mul_table[j]; - - ret = rsnd_adg_clk_query(priv, main_rate); - if (ret < 0) - continue; - - if (idx) - *idx = j; - - return main_rate; - } - - return 0; -} - -static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - int chan = rsnd_runtime_channel_for_ssi(io); - int idx, ret; - unsigned int main_rate; - unsigned int rate = rsnd_io_is_play(io) ? - rsnd_src_get_out_rate(priv, io) : - rsnd_src_get_in_rate(priv, io); - - if (!rsnd_rdai_is_clk_master(rdai)) - return 0; - - if (!rsnd_ssi_can_output_clk(mod)) - return 0; - - if (rsnd_ssi_is_multi_secondary(mod, io)) - return 0; - - if (rsnd_runtime_is_tdm_split(io)) - chan = rsnd_io_converted_chan(io); - - chan = rsnd_channel_normalization(chan); - - if (ssi->usrcnt > 0) { - if (ssi->rate != rate) { - dev_err(dev, "SSI parent/child should use same rate\n"); - return -EINVAL; - } - - if (ssi->chan != chan) { - dev_err(dev, "SSI parent/child should use same chan\n"); - return -EINVAL; - } - - return 0; - } - - ret = -EIO; - main_rate = rsnd_ssi_clk_query(rdai, rate, chan, &idx); - if (!main_rate) - goto rate_err; - - ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); - if (ret < 0) - goto rate_err; - - /* - * SSI clock will be output contiguously - * by below settings. - * This means, rsnd_ssi_master_clk_start() - * and rsnd_ssi_register_setup() are necessary - * for SSI parent - * - * SSICR : FORCE, SCKD, SWSD - * SSIWSR : CONT - */ - ssi->cr_clk = FORCE | rsnd_rdai_width_to_swl(rdai) | - SCKD | SWSD | CKDV(idx); - ssi->wsr = CONT; - ssi->rate = rate; - ssi->chan = chan; - - dev_dbg(dev, "%s outputs %d chan %u Hz\n", - rsnd_mod_name(mod), chan, rate); - - return 0; - -rate_err: - dev_err(dev, "unsupported clock rate\n"); - return ret; -} - -static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - if (!rsnd_rdai_is_clk_master(rdai)) - return; - - if (!rsnd_ssi_can_output_clk(mod)) - return; - - if (ssi->usrcnt > 1) - return; - - ssi->cr_clk = 0; - ssi->rate = 0; - ssi->chan = 0; - - rsnd_adg_ssi_clk_stop(mod); -} - -static void rsnd_ssi_config_init(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); - struct device *dev = rsnd_priv_to_dev(priv); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 cr_own = ssi->cr_own; - u32 cr_mode = ssi->cr_mode; - u32 wsr = ssi->wsr; - int width; - int is_tdm, is_tdm_split; - - is_tdm = rsnd_runtime_is_tdm(io); - is_tdm_split = rsnd_runtime_is_tdm_split(io); - - if (is_tdm) - dev_dbg(dev, "TDM mode\n"); - if (is_tdm_split) - dev_dbg(dev, "TDM Split mode\n"); - - cr_own |= FORCE | rsnd_rdai_width_to_swl(rdai); - - if (rdai->bit_clk_inv) - cr_own |= SCKP; - if (rdai->frm_clk_inv && !is_tdm) - cr_own |= SWSP; - if (rdai->data_alignment) - cr_own |= SDTA; - if (rdai->sys_delay) - cr_own |= DEL; - - /* - * TDM Mode - * see - * rsnd_ssiu_init_gen2() - */ - if (is_tdm || is_tdm_split) { - wsr |= WS_MODE; - cr_own |= CHNL_8; - } - - /* - * We shouldn't exchange SWSP after running. - * This means, parent needs to care it. - */ - if (rsnd_ssi_is_parent(mod, io)) - goto init_end; - - if (rsnd_io_is_play(io)) - cr_own |= TRMD; - - cr_own &= ~DWL_MASK; - width = snd_pcm_format_width(runtime->format); - if (is_tdm_split) { - /* - * The SWL and DWL bits in SSICR should be fixed at 32-bit - * setting when TDM split mode. - * see datasheet - * Operation :: TDM Format Split Function (TDM Split Mode) - */ - width = 32; - } - - switch (width) { - case 8: - cr_own |= DWL_8; - break; - case 16: - cr_own |= DWL_16; - break; - case 24: - cr_own |= DWL_24; - break; - case 32: - cr_own |= DWL_32; - break; - } - - if (rsnd_ssi_is_dma_mode(mod)) { - cr_mode = UIEN | OIEN | /* over/under run */ - DMEN; /* DMA : enable DMA */ - } else { - cr_mode = DIEN; /* PIO : enable Data interrupt */ - } - -init_end: - ssi->cr_own = cr_own; - ssi->cr_mode = cr_mode; - ssi->wsr = wsr; -} - -static void rsnd_ssi_register_setup(struct rsnd_mod *mod) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - rsnd_mod_write(mod, SSIWSR, ssi->wsr); - rsnd_mod_write(mod, SSICR, ssi->cr_own | - ssi->cr_clk | - ssi->cr_mode | - ssi->cr_en); -} - -/* - * SSI mod common functions - */ -static int rsnd_ssi_init(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - int ret; - - if (!rsnd_ssi_is_run_mods(mod, io)) - return 0; - - ret = rsnd_ssi_master_clk_start(mod, io); - if (ret < 0) - return ret; - - ssi->usrcnt++; - - ret = rsnd_mod_power_on(mod); - if (ret < 0) - return ret; - - rsnd_ssi_config_init(mod, io); - - rsnd_ssi_register_setup(mod); - - /* clear error status */ - rsnd_ssi_status_clear(mod); - - return 0; -} - -static int rsnd_ssi_quit(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - if (!rsnd_ssi_is_run_mods(mod, io)) - return 0; - - if (!ssi->usrcnt) { - dev_err(dev, "%s usrcnt error\n", rsnd_mod_name(mod)); - return -EIO; - } - - rsnd_ssi_master_clk_stop(mod, io); - - rsnd_mod_power_off(mod); - - ssi->usrcnt--; - - if (!ssi->usrcnt) { - ssi->cr_own = 0; - ssi->cr_mode = 0; - ssi->wsr = 0; - } - - return 0; -} - -static int rsnd_ssi_hw_params(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - unsigned int fmt_width = snd_pcm_format_width(params_format(params)); - - if (fmt_width > rdai->chan_width) { - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_err(dev, "invalid combination of slot-width and format-data-width\n"); - return -EINVAL; - } - - return 0; -} - -static int rsnd_ssi_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - if (!rsnd_ssi_is_run_mods(mod, io)) - return 0; - - /* - * EN will be set via SSIU :: SSI_CONTROL - * if Multi channel mode - */ - if (rsnd_ssi_multi_secondaries_runtime(io)) - return 0; - - /* - * EN is for data output. - * SSI parent EN is not needed. - */ - if (rsnd_ssi_is_parent(mod, io)) - return 0; - - ssi->cr_en = EN; - - rsnd_mod_write(mod, SSICR, ssi->cr_own | - ssi->cr_clk | - ssi->cr_mode | - ssi->cr_en); - - return 0; -} - -static int rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 cr; - - if (!rsnd_ssi_is_run_mods(mod, io)) - return 0; - - if (rsnd_ssi_is_parent(mod, io)) - return 0; - - cr = ssi->cr_own | - ssi->cr_clk; - - /* - * disable all IRQ, - * Playback: Wait all data was sent - * Capture: It might not receave data. Do nothing - */ - if (rsnd_io_is_play(io)) { - rsnd_mod_write(mod, SSICR, cr | ssi->cr_en); - rsnd_ssi_status_check(mod, DIRQ); - } - - /* In multi-SSI mode, stop is performed by setting ssi0129 in - * SSI_CONTROL to 0 (in rsnd_ssio_stop_gen2). Do nothing here. - */ - if (rsnd_ssi_multi_secondaries_runtime(io)) - return 0; - - /* - * disable SSI, - * and, wait idle state - */ - rsnd_mod_write(mod, SSICR, cr); /* disabled all */ - rsnd_ssi_status_check(mod, IIRQ); - - ssi->cr_en = 0; - - return 0; -} - -static int rsnd_ssi_irq(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv, - int enable) -{ - u32 val = 0; - int is_tdm, is_tdm_split; - int id = rsnd_mod_id(mod); - - is_tdm = rsnd_runtime_is_tdm(io); - is_tdm_split = rsnd_runtime_is_tdm_split(io); - - if (rsnd_is_gen1(priv)) - return 0; - - if (rsnd_ssi_is_parent(mod, io)) - return 0; - - if (!rsnd_ssi_is_run_mods(mod, io)) - return 0; - - if (enable) - val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; - - if (is_tdm || is_tdm_split) { - switch (id) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 9: - val |= 0x0000ff00; - break; - } - } - - rsnd_mod_write(mod, SSI_INT_ENABLE, val); - - return 0; -} - -static bool rsnd_ssi_pio_interrupt(struct rsnd_mod *mod, - struct rsnd_dai_stream *io); -static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - int is_dma = rsnd_ssi_is_dma_mode(mod); - u32 status; - bool elapsed = false; - bool stop = false; - - spin_lock(&priv->lock); - - /* ignore all cases if not working */ - if (!rsnd_io_is_working(io)) - goto rsnd_ssi_interrupt_out; - - status = rsnd_ssi_status_get(mod); - - /* PIO only */ - if (!is_dma && (status & DIRQ)) - elapsed = rsnd_ssi_pio_interrupt(mod, io); - - /* DMA only */ - if (is_dma && (status & (UIRQ | OIRQ))) { - rsnd_print_irq_status(dev, "%s err status : 0x%08x\n", - rsnd_mod_name(mod), status); - - stop = true; - } - - stop |= rsnd_ssiu_busif_err_status_clear(mod); - - rsnd_ssi_status_clear(mod); -rsnd_ssi_interrupt_out: - spin_unlock(&priv->lock); - - if (elapsed) - snd_pcm_period_elapsed(io->substream); - - if (stop) - snd_pcm_stop_xrun(io->substream); - -} - -static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) -{ - struct rsnd_mod *mod = data; - - rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt); - - return IRQ_HANDLED; -} - -static u32 *rsnd_ssi_get_status(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - enum rsnd_mod_type type) -{ - /* - * SSIP (= SSI parent) needs to be special, otherwise, - * 2nd SSI might doesn't start. see also rsnd_mod_call() - * - * We can't include parent SSI status on SSI, because we don't know - * how many SSI requests parent SSI. Thus, it is localed on "io" now. - * ex) trouble case - * Playback: SSI0 - * Capture : SSI1 (needs SSI0) - * - * 1) start Capture -> SSI0/SSI1 are started. - * 2) start Playback -> SSI0 doesn't work, because it is already - * marked as "started" on 1) - * - * OTOH, using each mod's status is good for MUX case. - * It doesn't need to start in 2nd start - * ex) - * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 - * | - * IO-1: SRC1 -> CTU2 -+ - * - * 1) start IO-0 -> start SSI0 - * 2) start IO-1 -> SSI0 doesn't need to start, because it is - * already started on 1) - */ - if (type == RSND_MOD_SSIP) - return &io->parent_ssi_status; - - return rsnd_mod_get_status(mod, io, type); -} - -/* - * SSI PIO - */ -static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - - if (!__rsnd_ssi_is_pin_sharing(mod)) - return; - - if (!rsnd_rdai_is_clk_master(rdai)) - return; - - if (rsnd_ssi_is_multi_secondary(mod, io)) - return; - - switch (rsnd_mod_id(mod)) { - case 1: - case 2: - case 9: - rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP); - break; - case 4: - rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP); - break; - case 8: - rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP); - break; - } -} - -static int rsnd_ssi_pcm_new(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_soc_pcm_runtime *rtd) -{ - /* - * rsnd_rdai_is_clk_master() will be enabled after set_fmt, - * and, pcm_new will be called after it. - * This function reuse pcm_new at this point. - */ - rsnd_ssi_parent_attach(mod, io); - - return 0; -} - -static int rsnd_ssi_common_probe(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - int ret = 0; - - /* - * SSIP/SSIU/IRQ are not needed on - * SSI Multi secondaries - */ - if (rsnd_ssi_is_multi_secondary(mod, io)) - return 0; - - /* - * It can't judge ssi parent at this point - * see rsnd_ssi_pcm_new() - */ - - /* - * SSI might be called again as PIO fallback - * It is easy to manual handling for IRQ request/free - * - * OTOH, this function might be called many times if platform is - * using MIX. It needs xxx_attach() many times on xxx_probe(). - * Because of it, we can't control .probe/.remove calling count by - * mod->status. - * But it don't need to call request_irq() many times. - * Let's control it by RSND_SSI_PROBED flag. - */ - if (!rsnd_flags_has(ssi, RSND_SSI_PROBED)) { - ret = request_irq(ssi->irq, - rsnd_ssi_interrupt, - IRQF_SHARED, - dev_name(dev), mod); - - rsnd_flags_set(ssi, RSND_SSI_PROBED); - } - - return ret; -} - -static int rsnd_ssi_common_remove(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_mod *pure_ssi_mod = rsnd_io_to_mod_ssi(io); - - /* Do nothing if non SSI (= SSI parent, multi SSI) mod */ - if (pure_ssi_mod != mod) - return 0; - - /* PIO will request IRQ again */ - if (rsnd_flags_has(ssi, RSND_SSI_PROBED)) { - free_irq(ssi->irq, mod); - - rsnd_flags_del(ssi, RSND_SSI_PROBED); - } - - return 0; -} - -/* - * SSI PIO functions - */ -static bool rsnd_ssi_pio_interrupt(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 *buf = (u32 *)(runtime->dma_area + ssi->byte_pos); - int shift = 0; - int byte_pos; - bool elapsed = false; - - if (snd_pcm_format_width(runtime->format) == 24) - shift = 8; - - /* - * 8/16/32 data can be assesse to TDR/RDR register - * directly as 32bit data - * see rsnd_ssi_init() - */ - if (rsnd_io_is_play(io)) - rsnd_mod_write(mod, SSITDR, (*buf) << shift); - else - *buf = (rsnd_mod_read(mod, SSIRDR) >> shift); - - byte_pos = ssi->byte_pos + sizeof(*buf); - - if (byte_pos >= ssi->next_period_byte) { - int period_pos = byte_pos / ssi->byte_per_period; - - if (period_pos >= runtime->periods) { - byte_pos = 0; - period_pos = 0; - } - - ssi->next_period_byte = (period_pos + 1) * ssi->byte_per_period; - - elapsed = true; - } - - WRITE_ONCE(ssi->byte_pos, byte_pos); - - return elapsed; -} - -static int rsnd_ssi_pio_init(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - if (!rsnd_ssi_is_parent(mod, io)) { - ssi->byte_pos = 0; - ssi->byte_per_period = runtime->period_size * - runtime->channels * - samples_to_bytes(runtime, 1); - ssi->next_period_byte = ssi->byte_per_period; - } - - return rsnd_ssi_init(mod, io, priv); -} - -static int rsnd_ssi_pio_pointer(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - snd_pcm_uframes_t *pointer) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - - *pointer = bytes_to_frames(runtime, READ_ONCE(ssi->byte_pos)); - - return 0; -} - -static struct rsnd_mod_ops rsnd_ssi_pio_ops = { - .name = SSI_NAME, - .probe = rsnd_ssi_common_probe, - .remove = rsnd_ssi_common_remove, - .init = rsnd_ssi_pio_init, - .quit = rsnd_ssi_quit, - .start = rsnd_ssi_start, - .stop = rsnd_ssi_stop, - .irq = rsnd_ssi_irq, - .pointer = rsnd_ssi_pio_pointer, - .pcm_new = rsnd_ssi_pcm_new, - .hw_params = rsnd_ssi_hw_params, - .get_status = rsnd_ssi_get_status, -}; - -static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - int ret; - - /* - * SSIP/SSIU/IRQ/DMA are not needed on - * SSI Multi secondaries - */ - if (rsnd_ssi_is_multi_secondary(mod, io)) - return 0; - - ret = rsnd_ssi_common_probe(mod, io, priv); - if (ret) - return ret; - - /* SSI probe might be called many times in MUX multi path */ - ret = rsnd_dma_attach(io, mod, &io->dma); - - return ret; -} - -static int rsnd_ssi_fallback(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - - /* - * fallback to PIO - * - * SSI .probe might be called again. - * see - * rsnd_rdai_continuance_probe() - */ - mod->ops = &rsnd_ssi_pio_ops; - - dev_info(dev, "%s fallback to PIO mode\n", rsnd_mod_name(mod)); - - return 0; -} - -static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - int is_play = rsnd_io_is_play(io); - char *name; - - /* - * It should use "rcar_sound,ssiu" on DT. - * But, we need to keep compatibility for old version. - * - * If it has "rcar_sound.ssiu", it will be used. - * If not, "rcar_sound.ssi" will be used. - * see - * rsnd_ssiu_dma_req() - * rsnd_dma_of_path() - */ - - if (rsnd_ssi_use_busif(io)) - name = is_play ? "rxu" : "txu"; - else - name = is_play ? "rx" : "tx"; - - return rsnd_dma_request_channel(rsnd_ssi_of_node(priv), - SSI_NAME, mod, name); -} - -#ifdef CONFIG_DEBUG_FS -static void rsnd_ssi_debug_info(struct seq_file *m, - struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - seq_printf(m, "clock: %s\n", rsnd_rdai_is_clk_master(rdai) ? - "provider" : "consumer"); - seq_printf(m, "bit_clk_inv: %d\n", rdai->bit_clk_inv); - seq_printf(m, "frm_clk_inv: %d\n", rdai->frm_clk_inv); - seq_printf(m, "pin share: %d\n", __rsnd_ssi_is_pin_sharing(mod)); - seq_printf(m, "can out clk: %d\n", rsnd_ssi_can_output_clk(mod)); - seq_printf(m, "multi secondary: %d\n", rsnd_ssi_is_multi_secondary(mod, io)); - seq_printf(m, "tdm: %d, %d\n", rsnd_runtime_is_tdm(io), - rsnd_runtime_is_tdm_split(io)); - seq_printf(m, "chan: %d\n", ssi->chan); - seq_printf(m, "user: %d\n", ssi->usrcnt); - - rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SSI, - rsnd_mod_id(mod) * 0x40, 0x40); -} -#define DEBUG_INFO .debug_info = rsnd_ssi_debug_info -#else -#define DEBUG_INFO -#endif - -static struct rsnd_mod_ops rsnd_ssi_dma_ops = { - .name = SSI_NAME, - .dma_req = rsnd_ssi_dma_req, - .probe = rsnd_ssi_dma_probe, - .remove = rsnd_ssi_common_remove, - .init = rsnd_ssi_init, - .quit = rsnd_ssi_quit, - .start = rsnd_ssi_start, - .stop = rsnd_ssi_stop, - .irq = rsnd_ssi_irq, - .pcm_new = rsnd_ssi_pcm_new, - .fallback = rsnd_ssi_fallback, - .hw_params = rsnd_ssi_hw_params, - .get_status = rsnd_ssi_get_status, - DEBUG_INFO -}; - -int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) -{ - return mod->ops == &rsnd_ssi_dma_ops; -} - -/* - * ssi mod function - */ -static void rsnd_ssi_connect(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - static const enum rsnd_mod_type types[] = { - RSND_MOD_SSI, - RSND_MOD_SSIM1, - RSND_MOD_SSIM2, - RSND_MOD_SSIM3, - }; - enum rsnd_mod_type type; - int i; - - /* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */ - for (i = 0; i < ARRAY_SIZE(types); i++) { - type = types[i]; - if (!rsnd_io_to_mod(io, type)) { - rsnd_dai_connect(mod, io, type); - rsnd_rdai_channels_set(rdai, (i + 1) * 2); - rsnd_rdai_ssi_lane_set(rdai, (i + 1)); - return; - } - } -} - -void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, - struct device_node *playback, - struct device_node *capture) -{ - struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *node; - struct device_node *np; - int i; - - node = rsnd_ssi_of_node(priv); - if (!node) - return; - - i = 0; - for_each_child_of_node(node, np) { - struct rsnd_mod *mod; - - i = rsnd_node_fixed_index(dev, np, SSI_NAME, i); - if (i < 0) { - of_node_put(np); - break; - } - - mod = rsnd_ssi_mod_get(priv, i); - - if (np == playback) - rsnd_ssi_connect(mod, &rdai->playback); - if (np == capture) - rsnd_ssi_connect(mod, &rdai->capture); - i++; - } - - of_node_put(node); -} - -struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) -{ - if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) - id = 0; - - return rsnd_mod_get(rsnd_ssi_get(priv, id)); -} - -int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) -{ - if (!mod) - return 0; - - return !!(rsnd_flags_has(rsnd_mod_to_ssi(mod), RSND_SSI_CLK_PIN_SHARE)); -} - -int rsnd_ssi_probe(struct rsnd_priv *priv) -{ - struct device_node *node; - struct device_node *np; - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mod_ops *ops; - struct clk *clk; - struct rsnd_ssi *ssi; - char name[RSND_SSI_NAME_SIZE]; - int i, nr, ret; - - node = rsnd_ssi_of_node(priv); - if (!node) - return -EINVAL; - - nr = rsnd_node_count(priv, node, SSI_NAME); - if (!nr) { - ret = -EINVAL; - goto rsnd_ssi_probe_done; - } - - ssi = devm_kcalloc(dev, nr, sizeof(*ssi), GFP_KERNEL); - if (!ssi) { - ret = -ENOMEM; - goto rsnd_ssi_probe_done; - } - - priv->ssi = ssi; - priv->ssi_nr = nr; - - i = 0; - for_each_child_of_node(node, np) { - if (!of_device_is_available(np)) - goto skip; - - i = rsnd_node_fixed_index(dev, np, SSI_NAME, i); - if (i < 0) { - ret = -EINVAL; - of_node_put(np); - goto rsnd_ssi_probe_done; - } - - ssi = rsnd_ssi_get(priv, i); - - snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d", - SSI_NAME, i); - - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - of_node_put(np); - goto rsnd_ssi_probe_done; - } - - if (of_property_read_bool(np, "shared-pin")) - rsnd_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE); - - if (of_property_read_bool(np, "no-busif")) - rsnd_flags_set(ssi, RSND_SSI_NO_BUSIF); - - ssi->irq = irq_of_parse_and_map(np, 0); - if (!ssi->irq) { - ret = -EINVAL; - of_node_put(np); - goto rsnd_ssi_probe_done; - } - - if (of_property_read_bool(np, "pio-transfer")) - ops = &rsnd_ssi_pio_ops; - else - ops = &rsnd_ssi_dma_ops; - - ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, - RSND_MOD_SSI, i); - if (ret) { - of_node_put(np); - goto rsnd_ssi_probe_done; - } -skip: - i++; - } - - ret = 0; - -rsnd_ssi_probe_done: - of_node_put(node); - - return ret; -} - -void rsnd_ssi_remove(struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi; - int i; - - for_each_rsnd_ssi(ssi, priv, i) { - rsnd_mod_quit(rsnd_mod_get(ssi)); - } -} diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c deleted file mode 100644 index 665e8b2db579..000000000000 --- a/sound/soc/sh/rcar/ssiu.c +++ /dev/null @@ -1,609 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Renesas R-Car SSIU support -// -// Copyright (c) 2015 Kuninori Morimoto - -#include "rsnd.h" - -#define SSIU_NAME "ssiu" - -struct rsnd_ssiu { - struct rsnd_mod mod; - u32 busif_status[8]; /* for BUSIF0 - BUSIF7 */ - unsigned int usrcnt; - int id; - int id_sub; -}; - -/* SSI_MODE */ -#define TDM_EXT (1 << 0) -#define TDM_SPLIT (1 << 8) - -#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr) -#define rsnd_mod_to_ssiu(_mod) container_of((_mod), struct rsnd_ssiu, mod) -#define for_each_rsnd_ssiu(pos, priv, i) \ - for (i = 0; \ - (i < rsnd_ssiu_nr(priv)) && \ - ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \ - i++) - -/* - * SSI Gen2 Gen3 Gen4 - * 0 BUSIF0-3 BUSIF0-7 BUSIF0-7 - * 1 BUSIF0-3 BUSIF0-7 - * 2 BUSIF0-3 BUSIF0-7 - * 3 BUSIF0 BUSIF0-7 - * 4 BUSIF0 BUSIF0-7 - * 5 BUSIF0 BUSIF0 - * 6 BUSIF0 BUSIF0 - * 7 BUSIF0 BUSIF0 - * 8 BUSIF0 BUSIF0 - * 9 BUSIF0-3 BUSIF0-7 - * total 22 52 8 - */ -static const int gen2_id[] = { 0, 4, 8, 12, 13, 14, 15, 16, 17, 18 }; -static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 }; -static const int gen4_id[] = { 0 }; - -/* enable busif buffer over/under run interrupt. */ -#define rsnd_ssiu_busif_err_irq_enable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 1) -#define rsnd_ssiu_busif_err_irq_disable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 0) -static void rsnd_ssiu_busif_err_irq_ctrl(struct rsnd_mod *mod, int enable) -{ - int id = rsnd_mod_id(mod); - int shift, offset; - int i; - - switch (id) { - case 0: - case 1: - case 2: - case 3: - case 4: - shift = id; - offset = 0; - break; - case 9: - shift = 1; - offset = 1; - break; - default: - return; - } - - for (i = 0; i < 4; i++) { - enum rsnd_reg reg = SSI_SYS_INT_ENABLE((i * 2) + offset); - u32 val = 0xf << (shift * 4); - u32 sys_int_enable = rsnd_mod_read(mod, reg); - - if (enable) - sys_int_enable |= val; - else - sys_int_enable &= ~val; - rsnd_mod_write(mod, reg, sys_int_enable); - } -} - -bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod) -{ - bool error = false; - int id = rsnd_mod_id(mod); - int shift, offset; - int i; - - switch (id) { - case 0: - case 1: - case 2: - case 3: - case 4: - shift = id; - offset = 0; - break; - case 9: - shift = 1; - offset = 1; - break; - default: - goto out; - } - - for (i = 0; i < 4; i++) { - u32 reg = SSI_SYS_STATUS(i * 2) + offset; - u32 status = rsnd_mod_read(mod, reg); - u32 val = 0xf << (shift * 4); - - status &= val; - if (status) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - rsnd_print_irq_status(dev, "%s err status : 0x%08x\n", - rsnd_mod_name(mod), status); - error = true; - } - rsnd_mod_write(mod, reg, val); - } -out: - return error; -} - -static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - enum rsnd_mod_type type) -{ - struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - int busif = rsnd_mod_id_sub(mod); - - return &ssiu->busif_status[busif]; -} - -static int rsnd_ssiu_init(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - u32 ssis = rsnd_ssi_multi_secondaries_runtime(io); - int use_busif = rsnd_ssi_use_busif(io); - int id = rsnd_mod_id(mod); - int is_clk_master = rsnd_rdai_is_clk_master(rdai); - u32 val1, val2; - - /* clear status */ - rsnd_ssiu_busif_err_status_clear(mod); - - /* Gen4 doesn't have SSI_MODE */ - if (rsnd_is_gen4(priv)) - goto ssi_mode_setting_end; - - /* - * SSI_MODE0 - */ - rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id); - - /* - * SSI_MODE1 / SSI_MODE2 - * - * FIXME - * sharing/multi with SSI0 are mainly supported - */ - val1 = rsnd_mod_read(mod, SSI_MODE1); - val2 = rsnd_mod_read(mod, SSI_MODE2); - if (rsnd_ssi_is_pin_sharing(io)) { - - ssis |= (1 << id); - - } else if (ssis) { - /* - * Multi SSI - * - * set synchronized bit here - */ - - /* SSI4 is synchronized with SSI3 */ - if (ssis & (1 << 4)) - val1 |= (1 << 20); - /* SSI012 are synchronized */ - if (ssis == 0x0006) - val1 |= (1 << 4); - /* SSI0129 are synchronized */ - if (ssis == 0x0206) - val2 |= (1 << 4); - } - - /* SSI1 is sharing pin with SSI0 */ - if (ssis & (1 << 1)) - val1 |= is_clk_master ? 0x2 : 0x1; - - /* SSI2 is sharing pin with SSI0 */ - if (ssis & (1 << 2)) - val1 |= is_clk_master ? 0x2 << 2 : - 0x1 << 2; - /* SSI4 is sharing pin with SSI3 */ - if (ssis & (1 << 4)) - val1 |= is_clk_master ? 0x2 << 16 : - 0x1 << 16; - /* SSI9 is sharing pin with SSI0 */ - if (ssis & (1 << 9)) - val2 |= is_clk_master ? 0x2 : 0x1; - - rsnd_mod_bset(mod, SSI_MODE1, 0x0013001f, val1); - rsnd_mod_bset(mod, SSI_MODE2, 0x00000017, val2); - -ssi_mode_setting_end: - /* - * Enable busif buffer over/under run interrupt. - * It will be handled from ssi.c - * see - * __rsnd_ssi_interrupt() - */ - rsnd_ssiu_busif_err_irq_enable(mod); - - return 0; -} - -static int rsnd_ssiu_quit(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - /* disable busif buffer over/under run interrupt. */ - rsnd_ssiu_busif_err_irq_disable(mod); - - return 0; -} - -static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = { - .name = SSIU_NAME, - .init = rsnd_ssiu_init, - .quit = rsnd_ssiu_quit, - .get_status = rsnd_ssiu_get_status, -}; - -static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - u32 has_hdmi0 = rsnd_flags_has(io, RSND_STREAM_HDMI0); - u32 has_hdmi1 = rsnd_flags_has(io, RSND_STREAM_HDMI1); - int ret; - u32 mode = 0; - - ret = rsnd_ssiu_init(mod, io, priv); - if (ret < 0) - return ret; - - ssiu->usrcnt++; - - /* - * TDM Extend/Split Mode - * see - * rsnd_ssi_config_init() - */ - if (rsnd_runtime_is_tdm(io)) - mode = TDM_EXT; - else if (rsnd_runtime_is_tdm_split(io)) - mode = TDM_SPLIT; - - rsnd_mod_write(mod, SSI_MODE, mode); - - if (rsnd_ssi_use_busif(io)) { - int id = rsnd_mod_id(mod); - int busif = rsnd_mod_id_sub(mod); - enum rsnd_reg adinr_reg, mode_reg, dalign_reg; - - if ((id == 9) && (busif >= 4)) { - adinr_reg = SSI9_BUSIF_ADINR(busif); - mode_reg = SSI9_BUSIF_MODE(busif); - dalign_reg = SSI9_BUSIF_DALIGN(busif); - } else { - adinr_reg = SSI_BUSIF_ADINR(busif); - mode_reg = SSI_BUSIF_MODE(busif); - dalign_reg = SSI_BUSIF_DALIGN(busif); - } - - rsnd_mod_write(mod, adinr_reg, - rsnd_get_adinr_bit(mod, io) | - (rsnd_io_is_play(io) ? - rsnd_runtime_channel_after_ctu(io) : - rsnd_runtime_channel_original(io))); - rsnd_mod_write(mod, mode_reg, - rsnd_get_busif_shift(io, mod) | 1); - rsnd_mod_write(mod, dalign_reg, - rsnd_get_dalign(mod, io)); - } - - if (has_hdmi0 || has_hdmi1) { - enum rsnd_mod_type rsnd_ssi_array[] = { - RSND_MOD_SSIM1, - RSND_MOD_SSIM2, - RSND_MOD_SSIM3, - }; - struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); - struct rsnd_mod *pos; - u32 val; - int i; - - i = rsnd_mod_id(ssi_mod); - - /* output all same SSI as default */ - val = i << 16 | - i << 20 | - i << 24 | - i << 28 | - i; - - for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) { - int shift = (i * 4) + 20; - - val = (val & ~(0xF << shift)) | - rsnd_mod_id(pos) << shift; - } - - if (has_hdmi0) - rsnd_mod_write(mod, HDMI0_SEL, val); - if (has_hdmi1) - rsnd_mod_write(mod, HDMI1_SEL, val); - } - - return 0; -} - -static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - int busif = rsnd_mod_id_sub(mod); - - if (!rsnd_ssi_use_busif(io)) - return 0; - - rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 1 << (busif * 4)); - - if (rsnd_ssi_multi_secondaries_runtime(io)) - rsnd_mod_write(mod, SSI_CONTROL, 0x1); - - return 0; -} - -static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - int busif = rsnd_mod_id_sub(mod); - - if (!rsnd_ssi_use_busif(io)) - return 0; - - rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 0); - - if (--ssiu->usrcnt) - return 0; - - if (rsnd_ssi_multi_secondaries_runtime(io)) - rsnd_mod_write(mod, SSI_CONTROL, 0); - - return 0; -} - -static int rsnd_ssiu_id(struct rsnd_mod *mod) -{ - struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - - /* see rsnd_ssiu_probe() */ - return ssiu->id; -} - -static int rsnd_ssiu_id_sub(struct rsnd_mod *mod) -{ - struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - - /* see rsnd_ssiu_probe() */ - return ssiu->id_sub; -} - -static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - int is_play = rsnd_io_is_play(io); - char *name; - - /* - * It should use "rcar_sound,ssiu" on DT. - * But, we need to keep compatibility for old version. - * - * If it has "rcar_sound.ssiu", it will be used. - * If not, "rcar_sound.ssi" will be used. - * see - * rsnd_ssi_dma_req() - * rsnd_dma_of_path() - */ - - name = is_play ? "rx" : "tx"; - - return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv), - SSIU_NAME, mod, name); -} - -#ifdef CONFIG_DEBUG_FS -static void rsnd_ssiu_debug_info(struct seq_file *m, - struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SSIU, - rsnd_mod_id(mod) * 0x80, 0x80); -} -#define DEBUG_INFO .debug_info = rsnd_ssiu_debug_info -#else -#define DEBUG_INFO -#endif - -static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = { - .name = SSIU_NAME, - .dma_req = rsnd_ssiu_dma_req, - .init = rsnd_ssiu_init_gen2, - .quit = rsnd_ssiu_quit, - .start = rsnd_ssiu_start_gen2, - .stop = rsnd_ssiu_stop_gen2, - .get_status = rsnd_ssiu_get_status, - DEBUG_INFO -}; - -static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id) -{ - if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv))) - id = 0; - - return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id); -} - -static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv, - struct rsnd_dai_stream *io) -{ - struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); - struct rsnd_ssiu *ssiu; - int is_dma_mode; - int i; - - if (!ssi_mod) - return; - - is_dma_mode = rsnd_ssi_is_dma_mode(ssi_mod); - - /* select BUSIF0 */ - for_each_rsnd_ssiu(ssiu, priv, i) { - struct rsnd_mod *mod = rsnd_mod_get(ssiu); - - if (is_dma_mode && - (rsnd_mod_id(ssi_mod) == rsnd_mod_id(mod)) && - (rsnd_mod_id_sub(mod) == 0)) { - rsnd_dai_connect(mod, io, mod->type); - return; - } - } -} - -void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, - struct device_node *playback, - struct device_node *capture) -{ - struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *node = rsnd_ssiu_of_node(priv); - struct rsnd_dai_stream *io_p = &rdai->playback; - struct rsnd_dai_stream *io_c = &rdai->capture; - - /* use rcar_sound,ssiu if exist */ - if (node) { - struct device_node *np; - int i = 0; - - for_each_child_of_node(node, np) { - struct rsnd_mod *mod; - - i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i); - if (i < 0) { - of_node_put(np); - break; - } - - mod = rsnd_ssiu_mod_get(priv, i); - - if (np == playback) - rsnd_dai_connect(mod, io_p, mod->type); - if (np == capture) - rsnd_dai_connect(mod, io_c, mod->type); - i++; - } - - of_node_put(node); - } - - /* Keep DT compatibility */ - if (!rsnd_io_to_mod_ssiu(io_p)) - rsnd_parse_connect_ssiu_compatible(priv, io_p); - if (!rsnd_io_to_mod_ssiu(io_c)) - rsnd_parse_connect_ssiu_compatible(priv, io_c); -} - -int rsnd_ssiu_probe(struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *node; - struct rsnd_ssiu *ssiu; - struct rsnd_mod_ops *ops; - const int *list = NULL; - int i, nr; - - /* - * Keep DT compatibility. - * if it has "rcar_sound,ssiu", use it. - * if not, use "rcar_sound,ssi" - * see - * rsnd_ssiu_bufsif_to_id() - */ - node = rsnd_ssiu_of_node(priv); - if (node) - nr = rsnd_node_count(priv, node, SSIU_NAME); - else - nr = priv->ssi_nr; - - if (!nr) - return -EINVAL; - - ssiu = devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL); - if (!ssiu) - return -ENOMEM; - - priv->ssiu = ssiu; - priv->ssiu_nr = nr; - - if (rsnd_is_gen1(priv)) - ops = &rsnd_ssiu_ops_gen1; - else - ops = &rsnd_ssiu_ops_gen2; - - /* Keep compatibility */ - nr = 0; - if ((node) && - (ops == &rsnd_ssiu_ops_gen2)) { - ops->id = rsnd_ssiu_id; - ops->id_sub = rsnd_ssiu_id_sub; - - if (rsnd_is_gen2(priv)) { - list = gen2_id; - nr = ARRAY_SIZE(gen2_id); - } else if (rsnd_is_gen3(priv)) { - list = gen3_id; - nr = ARRAY_SIZE(gen3_id); - } else if (rsnd_is_gen4(priv)) { - list = gen4_id; - nr = ARRAY_SIZE(gen4_id); - } else { - dev_err(dev, "unknown SSIU\n"); - return -ENODEV; - } - } - - for_each_rsnd_ssiu(ssiu, priv, i) { - int ret; - - if (node) { - int j; - - /* - * see - * rsnd_ssiu_get_id() - * rsnd_ssiu_get_id_sub() - */ - for (j = 0; j < nr; j++) { - if (list[j] > i) - break; - ssiu->id = j; - ssiu->id_sub = i - list[ssiu->id]; - } - } else { - ssiu->id = i; - } - - ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu), - ops, NULL, RSND_MOD_SSIU, i); - if (ret) - return ret; - } - - return 0; -} - -void rsnd_ssiu_remove(struct rsnd_priv *priv) -{ - struct rsnd_ssiu *ssiu; - int i; - - for_each_rsnd_ssiu(ssiu, priv, i) { - rsnd_mod_quit(rsnd_mod_get(ssiu)); - } -} diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c deleted file mode 100644 index 6efd017aaa7f..000000000000 --- a/sound/soc/sh/rz-ssi.c +++ /dev/null @@ -1,1212 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Renesas RZ/G2L ASoC Serial Sound Interface (SSIF-2) Driver -// -// Copyright (C) 2021 Renesas Electronics Corp. -// Copyright (C) 2019 Chris Brandt. -// - -#include -#include -#include -#include -#include -#include -#include - -/* REGISTER OFFSET */ -#define SSICR 0x000 -#define SSISR 0x004 -#define SSIFCR 0x010 -#define SSIFSR 0x014 -#define SSIFTDR 0x018 -#define SSIFRDR 0x01c -#define SSIOFR 0x020 -#define SSISCR 0x024 - -/* SSI REGISTER BITS */ -#define SSICR_DWL(x) (((x) & 0x7) << 19) -#define SSICR_SWL(x) (((x) & 0x7) << 16) - -#define SSICR_CKS BIT(30) -#define SSICR_TUIEN BIT(29) -#define SSICR_TOIEN BIT(28) -#define SSICR_RUIEN BIT(27) -#define SSICR_ROIEN BIT(26) -#define SSICR_MST BIT(14) -#define SSICR_BCKP BIT(13) -#define SSICR_LRCKP BIT(12) -#define SSICR_CKDV(x) (((x) & 0xf) << 4) -#define SSICR_TEN BIT(1) -#define SSICR_REN BIT(0) - -#define SSISR_TUIRQ BIT(29) -#define SSISR_TOIRQ BIT(28) -#define SSISR_RUIRQ BIT(27) -#define SSISR_ROIRQ BIT(26) -#define SSISR_IIRQ BIT(25) - -#define SSIFCR_AUCKE BIT(31) -#define SSIFCR_SSIRST BIT(16) -#define SSIFCR_TIE BIT(3) -#define SSIFCR_RIE BIT(2) -#define SSIFCR_TFRST BIT(1) -#define SSIFCR_RFRST BIT(0) -#define SSIFCR_FIFO_RST (SSIFCR_TFRST | SSIFCR_RFRST) - -#define SSIFSR_TDC_MASK 0x3f -#define SSIFSR_TDC_SHIFT 24 -#define SSIFSR_RDC_MASK 0x3f -#define SSIFSR_RDC_SHIFT 8 - -#define SSIFSR_TDE BIT(16) -#define SSIFSR_RDF BIT(0) - -#define SSIOFR_LRCONT BIT(8) - -#define SSISCR_TDES(x) (((x) & 0x1f) << 8) -#define SSISCR_RDFS(x) (((x) & 0x1f) << 0) - -/* Pre allocated buffers sizes */ -#define PREALLOC_BUFFER (SZ_32K) -#define PREALLOC_BUFFER_MAX (SZ_32K) - -#define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-44.1kHz */ -#define SSI_FMTS SNDRV_PCM_FMTBIT_S16_LE -#define SSI_CHAN_MIN 2 -#define SSI_CHAN_MAX 2 -#define SSI_FIFO_DEPTH 32 - -struct rz_ssi_priv; - -struct rz_ssi_stream { - struct rz_ssi_priv *priv; - struct snd_pcm_substream *substream; - int fifo_sample_size; /* sample capacity of SSI FIFO */ - int dma_buffer_pos; /* The address for the next DMA descriptor */ - int period_counter; /* for keeping track of periods transferred */ - int sample_width; - int buffer_pos; /* current frame position in the buffer */ - int running; /* 0=stopped, 1=running */ - - int uerr_num; - int oerr_num; - - struct dma_chan *dma_ch; - - int (*transfer)(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm); -}; - -struct rz_ssi_priv { - void __iomem *base; - struct platform_device *pdev; - struct reset_control *rstc; - struct device *dev; - struct clk *sfr_clk; - struct clk *clk; - - phys_addr_t phys; - int irq_int; - int irq_tx; - int irq_rx; - int irq_rt; - - spinlock_t lock; - - /* - * The SSI supports full-duplex transmission and reception. - * However, if an error occurs, channel reset (both transmission - * and reception reset) is required. - * So it is better to use as half-duplex (playing and recording - * should be done on separate channels). - */ - struct rz_ssi_stream playback; - struct rz_ssi_stream capture; - - /* clock */ - unsigned long audio_mck; - unsigned long audio_clk_1; - unsigned long audio_clk_2; - - bool lrckp_fsync_fall; /* LR clock polarity (SSICR.LRCKP) */ - bool bckp_rise; /* Bit clock polarity (SSICR.BCKP) */ - bool dma_rt; - - /* Full duplex communication support */ - struct { - unsigned int rate; - unsigned int channels; - unsigned int sample_width; - unsigned int sample_bits; - } hw_params_cache; -}; - -static void rz_ssi_dma_complete(void *data); - -static void rz_ssi_reg_writel(struct rz_ssi_priv *priv, uint reg, u32 data) -{ - writel(data, (priv->base + reg)); -} - -static u32 rz_ssi_reg_readl(struct rz_ssi_priv *priv, uint reg) -{ - return readl(priv->base + reg); -} - -static void rz_ssi_reg_mask_setl(struct rz_ssi_priv *priv, uint reg, - u32 bclr, u32 bset) -{ - u32 val; - - val = readl(priv->base + reg); - val = (val & ~bclr) | bset; - writel(val, (priv->base + reg)); -} - -static inline struct snd_soc_dai * -rz_ssi_get_dai(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - - return snd_soc_rtd_to_cpu(rtd, 0); -} - -static inline bool rz_ssi_stream_is_play(struct rz_ssi_priv *ssi, - struct snd_pcm_substream *substream) -{ - return substream->stream == SNDRV_PCM_STREAM_PLAYBACK; -} - -static inline struct rz_ssi_stream * -rz_ssi_stream_get(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream) -{ - struct rz_ssi_stream *stream = &ssi->playback; - - if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) - stream = &ssi->capture; - - return stream; -} - -static inline bool rz_ssi_is_dma_enabled(struct rz_ssi_priv *ssi) -{ - return (ssi->playback.dma_ch && (ssi->dma_rt || ssi->capture.dma_ch)); -} - -static void rz_ssi_set_substream(struct rz_ssi_stream *strm, - struct snd_pcm_substream *substream) -{ - struct rz_ssi_priv *ssi = strm->priv; - unsigned long flags; - - spin_lock_irqsave(&ssi->lock, flags); - strm->substream = substream; - spin_unlock_irqrestore(&ssi->lock, flags); -} - -static bool rz_ssi_stream_is_valid(struct rz_ssi_priv *ssi, - struct rz_ssi_stream *strm) -{ - unsigned long flags; - bool ret; - - spin_lock_irqsave(&ssi->lock, flags); - ret = strm->substream && strm->substream->runtime; - spin_unlock_irqrestore(&ssi->lock, flags); - - return ret; -} - -static inline bool rz_ssi_is_stream_running(struct rz_ssi_stream *strm) -{ - return strm->substream && strm->running; -} - -static void rz_ssi_stream_init(struct rz_ssi_stream *strm, - struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - rz_ssi_set_substream(strm, substream); - strm->sample_width = samples_to_bytes(runtime, 1); - strm->dma_buffer_pos = 0; - strm->period_counter = 0; - strm->buffer_pos = 0; - - strm->oerr_num = 0; - strm->uerr_num = 0; - strm->running = 0; - - /* fifo init */ - strm->fifo_sample_size = SSI_FIFO_DEPTH; -} - -static void rz_ssi_stream_quit(struct rz_ssi_priv *ssi, - struct rz_ssi_stream *strm) -{ - struct snd_soc_dai *dai = rz_ssi_get_dai(strm->substream); - - rz_ssi_set_substream(strm, NULL); - - if (strm->oerr_num > 0) - dev_info(dai->dev, "overrun = %d\n", strm->oerr_num); - - if (strm->uerr_num > 0) - dev_info(dai->dev, "underrun = %d\n", strm->uerr_num); -} - -static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, - unsigned int channels) -{ - static s8 ckdv[16] = { 1, 2, 4, 8, 16, 32, 64, 128, - 6, 12, 24, 48, 96, -1, -1, -1 }; - unsigned int channel_bits = 32; /* System Word Length */ - unsigned long bclk_rate = rate * channels * channel_bits; - unsigned int div; - unsigned int i; - u32 ssicr = 0; - u32 clk_ckdv; - - /* Clear AUCKE so we can set MST */ - rz_ssi_reg_writel(ssi, SSIFCR, 0); - - /* Continue to output LRCK pin even when idle */ - rz_ssi_reg_writel(ssi, SSIOFR, SSIOFR_LRCONT); - if (ssi->audio_clk_1 && ssi->audio_clk_2) { - if (ssi->audio_clk_1 % bclk_rate) - ssi->audio_mck = ssi->audio_clk_2; - else - ssi->audio_mck = ssi->audio_clk_1; - } - - /* Clock setting */ - ssicr |= SSICR_MST; - if (ssi->audio_mck == ssi->audio_clk_1) - ssicr |= SSICR_CKS; - if (ssi->bckp_rise) - ssicr |= SSICR_BCKP; - if (ssi->lrckp_fsync_fall) - ssicr |= SSICR_LRCKP; - - /* Determine the clock divider */ - clk_ckdv = 0; - div = ssi->audio_mck / bclk_rate; - /* try to find an match */ - for (i = 0; i < ARRAY_SIZE(ckdv); i++) { - if (ckdv[i] == div) { - clk_ckdv = i; - break; - } - } - - if (i == ARRAY_SIZE(ckdv)) { - dev_err(ssi->dev, "Rate not divisible by audio clock source\n"); - return -EINVAL; - } - - /* - * DWL: Data Word Length = 16 bits - * SWL: System Word Length = 32 bits - */ - ssicr |= SSICR_CKDV(clk_ckdv); - ssicr |= SSICR_DWL(1) | SSICR_SWL(3); - rz_ssi_reg_writel(ssi, SSICR, ssicr); - rz_ssi_reg_writel(ssi, SSIFCR, SSIFCR_AUCKE | SSIFCR_FIFO_RST); - - return 0; -} - -static void rz_ssi_set_idle(struct rz_ssi_priv *ssi) -{ - int timeout; - - /* Disable irqs */ - rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TUIEN | SSICR_TOIEN | - SSICR_RUIEN | SSICR_ROIEN, 0); - rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_TIE | SSIFCR_RIE, 0); - - /* Clear all error flags */ - rz_ssi_reg_mask_setl(ssi, SSISR, - (SSISR_TOIRQ | SSISR_TUIRQ | SSISR_ROIRQ | - SSISR_RUIRQ), 0); - - /* Wait for idle */ - timeout = 100; - while (--timeout) { - if (rz_ssi_reg_readl(ssi, SSISR) & SSISR_IIRQ) - break; - udelay(1); - } - - if (!timeout) - dev_info(ssi->dev, "timeout waiting for SSI idle\n"); - - /* Hold FIFOs in reset */ - rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_FIFO_RST); -} - -static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) -{ - bool is_play = rz_ssi_stream_is_play(ssi, strm->substream); - bool is_full_duplex; - u32 ssicr, ssifcr; - - is_full_duplex = rz_ssi_is_stream_running(&ssi->playback) || - rz_ssi_is_stream_running(&ssi->capture); - ssicr = rz_ssi_reg_readl(ssi, SSICR); - ssifcr = rz_ssi_reg_readl(ssi, SSIFCR); - if (!is_full_duplex) { - ssifcr &= ~0xF; - } else { - rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0); - rz_ssi_set_idle(ssi); - ssifcr &= ~SSIFCR_FIFO_RST; - } - - /* FIFO interrupt thresholds */ - if (rz_ssi_is_dma_enabled(ssi)) - rz_ssi_reg_writel(ssi, SSISCR, 0); - else - rz_ssi_reg_writel(ssi, SSISCR, - SSISCR_TDES(strm->fifo_sample_size / 2 - 1) | - SSISCR_RDFS(0)); - - /* enable IRQ */ - if (is_play) { - ssicr |= SSICR_TUIEN | SSICR_TOIEN; - ssifcr |= SSIFCR_TIE; - if (!is_full_duplex) - ssifcr |= SSIFCR_RFRST; - } else { - ssicr |= SSICR_RUIEN | SSICR_ROIEN; - ssifcr |= SSIFCR_RIE; - if (!is_full_duplex) - ssifcr |= SSIFCR_TFRST; - } - - rz_ssi_reg_writel(ssi, SSICR, ssicr); - rz_ssi_reg_writel(ssi, SSIFCR, ssifcr); - - /* Clear all error flags */ - rz_ssi_reg_mask_setl(ssi, SSISR, - (SSISR_TOIRQ | SSISR_TUIRQ | SSISR_ROIRQ | - SSISR_RUIRQ), 0); - - strm->running = 1; - if (is_full_duplex) - ssicr |= SSICR_TEN | SSICR_REN; - else - ssicr |= is_play ? SSICR_TEN : SSICR_REN; - - rz_ssi_reg_writel(ssi, SSICR, ssicr); - - return 0; -} - -static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) -{ - strm->running = 0; - - if (rz_ssi_is_stream_running(&ssi->playback) || - rz_ssi_is_stream_running(&ssi->capture)) - return 0; - - /* Disable TX/RX */ - rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0); - - /* Cancel all remaining DMA transactions */ - if (rz_ssi_is_dma_enabled(ssi)) - dmaengine_terminate_async(strm->dma_ch); - - rz_ssi_set_idle(ssi); - - return 0; -} - -static void rz_ssi_pointer_update(struct rz_ssi_stream *strm, int frames) -{ - struct snd_pcm_substream *substream = strm->substream; - struct snd_pcm_runtime *runtime; - int current_period; - - if (!strm->running || !substream || !substream->runtime) - return; - - runtime = substream->runtime; - strm->buffer_pos += frames; - WARN_ON(strm->buffer_pos > runtime->buffer_size); - - /* ring buffer */ - if (strm->buffer_pos == runtime->buffer_size) - strm->buffer_pos = 0; - - current_period = strm->buffer_pos / runtime->period_size; - if (strm->period_counter != current_period) { - snd_pcm_period_elapsed(strm->substream); - strm->period_counter = current_period; - } -} - -static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) -{ - struct snd_pcm_substream *substream = strm->substream; - struct snd_pcm_runtime *runtime; - u16 *buf; - int fifo_samples; - int frames_left; - int samples; - int i; - - if (!rz_ssi_stream_is_valid(ssi, strm)) - return -EINVAL; - - runtime = substream->runtime; - - do { - /* frames left in this period */ - frames_left = runtime->period_size - - (strm->buffer_pos % runtime->period_size); - if (!frames_left) - frames_left = runtime->period_size; - - /* Samples in RX FIFO */ - fifo_samples = (rz_ssi_reg_readl(ssi, SSIFSR) >> - SSIFSR_RDC_SHIFT) & SSIFSR_RDC_MASK; - - /* Only read full frames at a time */ - samples = 0; - while (frames_left && (fifo_samples >= runtime->channels)) { - samples += runtime->channels; - fifo_samples -= runtime->channels; - frames_left--; - } - - /* not enough samples yet */ - if (!samples) - break; - - /* calculate new buffer index */ - buf = (u16 *)runtime->dma_area; - buf += strm->buffer_pos * runtime->channels; - - /* Note, only supports 16-bit samples */ - for (i = 0; i < samples; i++) - *buf++ = (u16)(rz_ssi_reg_readl(ssi, SSIFRDR) >> 16); - - rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); - rz_ssi_pointer_update(strm, samples / runtime->channels); - } while (!frames_left && fifo_samples >= runtime->channels); - - return 0; -} - -static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) -{ - struct snd_pcm_substream *substream = strm->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - int sample_space; - int samples = 0; - int frames_left; - int i; - u32 ssifsr; - u16 *buf; - - if (!rz_ssi_stream_is_valid(ssi, strm)) - return -EINVAL; - - /* frames left in this period */ - frames_left = runtime->period_size - (strm->buffer_pos % - runtime->period_size); - if (frames_left == 0) - frames_left = runtime->period_size; - - sample_space = strm->fifo_sample_size; - ssifsr = rz_ssi_reg_readl(ssi, SSIFSR); - sample_space -= (ssifsr >> SSIFSR_TDC_SHIFT) & SSIFSR_TDC_MASK; - - /* Only add full frames at a time */ - while (frames_left && (sample_space >= runtime->channels)) { - samples += runtime->channels; - sample_space -= runtime->channels; - frames_left--; - } - - /* no space to send anything right now */ - if (samples == 0) - return 0; - - /* calculate new buffer index */ - buf = (u16 *)(runtime->dma_area); - buf += strm->buffer_pos * runtime->channels; - - /* Note, only supports 16-bit samples */ - for (i = 0; i < samples; i++) - rz_ssi_reg_writel(ssi, SSIFTDR, ((u32)(*buf++) << 16)); - - rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_TDE, 0); - rz_ssi_pointer_update(strm, samples / runtime->channels); - - return 0; -} - -static irqreturn_t rz_ssi_interrupt(int irq, void *data) -{ - struct rz_ssi_stream *strm_playback = NULL; - struct rz_ssi_stream *strm_capture = NULL; - struct rz_ssi_priv *ssi = data; - u32 ssisr = rz_ssi_reg_readl(ssi, SSISR); - - if (ssi->playback.substream) - strm_playback = &ssi->playback; - if (ssi->capture.substream) - strm_capture = &ssi->capture; - - if (!strm_playback && !strm_capture) - return IRQ_HANDLED; /* Left over TX/RX interrupt */ - - if (irq == ssi->irq_int) { /* error or idle */ - bool is_stopped = false; - int i, count; - - if (rz_ssi_is_dma_enabled(ssi)) - count = 4; - else - count = 1; - - if (ssisr & (SSISR_RUIRQ | SSISR_ROIRQ | SSISR_TUIRQ | SSISR_TOIRQ)) - is_stopped = true; - - if (ssi->capture.substream && is_stopped) { - if (ssisr & SSISR_RUIRQ) - strm_capture->uerr_num++; - if (ssisr & SSISR_ROIRQ) - strm_capture->oerr_num++; - - rz_ssi_stop(ssi, strm_capture); - } - - if (ssi->playback.substream && is_stopped) { - if (ssisr & SSISR_TUIRQ) - strm_playback->uerr_num++; - if (ssisr & SSISR_TOIRQ) - strm_playback->oerr_num++; - - rz_ssi_stop(ssi, strm_playback); - } - - /* Clear all flags */ - rz_ssi_reg_mask_setl(ssi, SSISR, SSISR_TOIRQ | SSISR_TUIRQ | - SSISR_ROIRQ | SSISR_RUIRQ, 0); - - /* Add/remove more data */ - if (ssi->capture.substream && is_stopped) { - for (i = 0; i < count; i++) - strm_capture->transfer(ssi, strm_capture); - } - - if (ssi->playback.substream && is_stopped) { - for (i = 0; i < count; i++) - strm_playback->transfer(ssi, strm_playback); - } - - /* Resume */ - if (ssi->playback.substream && is_stopped) - rz_ssi_start(ssi, &ssi->playback); - if (ssi->capture.substream && is_stopped) - rz_ssi_start(ssi, &ssi->capture); - } - - if (!rz_ssi_is_stream_running(&ssi->playback) && - !rz_ssi_is_stream_running(&ssi->capture)) - return IRQ_HANDLED; - - /* tx data empty */ - if (irq == ssi->irq_tx && rz_ssi_is_stream_running(&ssi->playback)) - strm_playback->transfer(ssi, &ssi->playback); - - /* rx data full */ - if (irq == ssi->irq_rx && rz_ssi_is_stream_running(&ssi->capture)) { - strm_capture->transfer(ssi, &ssi->capture); - rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); - } - - if (irq == ssi->irq_rt) { - if (ssi->playback.substream) { - strm_playback->transfer(ssi, &ssi->playback); - } else { - strm_capture->transfer(ssi, &ssi->capture); - rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); - } - } - - return IRQ_HANDLED; -} - -static int rz_ssi_dma_slave_config(struct rz_ssi_priv *ssi, - struct dma_chan *dma_ch, bool is_play) -{ - struct dma_slave_config cfg; - - memset(&cfg, 0, sizeof(cfg)); - - cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; - cfg.dst_addr = ssi->phys + SSIFTDR; - cfg.src_addr = ssi->phys + SSIFRDR; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - - return dmaengine_slave_config(dma_ch, &cfg); -} - -static int rz_ssi_dma_transfer(struct rz_ssi_priv *ssi, - struct rz_ssi_stream *strm) -{ - struct snd_pcm_substream *substream = strm->substream; - struct dma_async_tx_descriptor *desc; - struct snd_pcm_runtime *runtime; - enum dma_transfer_direction dir; - u32 dma_paddr, dma_size; - int amount; - - if (!rz_ssi_stream_is_valid(ssi, strm)) - return -EINVAL; - - runtime = substream->runtime; - if (runtime->state == SNDRV_PCM_STATE_DRAINING) - /* - * Stream is ending, so do not queue up any more DMA - * transfers otherwise we play partial sound clips - * because we can't shut off the DMA quick enough. - */ - return 0; - - dir = rz_ssi_stream_is_play(ssi, substream) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; - - /* Always transfer 1 period */ - amount = runtime->period_size; - - /* DMA physical address and size */ - dma_paddr = runtime->dma_addr + frames_to_bytes(runtime, - strm->dma_buffer_pos); - dma_size = frames_to_bytes(runtime, amount); - desc = dmaengine_prep_slave_single(strm->dma_ch, dma_paddr, dma_size, - dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - dev_err(ssi->dev, "dmaengine_prep_slave_single() fail\n"); - return -ENOMEM; - } - - desc->callback = rz_ssi_dma_complete; - desc->callback_param = strm; - - if (dmaengine_submit(desc) < 0) { - dev_err(ssi->dev, "dmaengine_submit() fail\n"); - return -EIO; - } - - /* Update DMA pointer */ - strm->dma_buffer_pos += amount; - if (strm->dma_buffer_pos >= runtime->buffer_size) - strm->dma_buffer_pos = 0; - - /* Start DMA */ - dma_async_issue_pending(strm->dma_ch); - - return 0; -} - -static void rz_ssi_dma_complete(void *data) -{ - struct rz_ssi_stream *strm = (struct rz_ssi_stream *)data; - - if (!strm->running || !strm->substream || !strm->substream->runtime) - return; - - /* Note that next DMA transaction has probably already started */ - rz_ssi_pointer_update(strm, strm->substream->runtime->period_size); - - /* Queue up another DMA transaction */ - rz_ssi_dma_transfer(strm->priv, strm); -} - -static void rz_ssi_release_dma_channels(struct rz_ssi_priv *ssi) -{ - if (ssi->playback.dma_ch) { - dma_release_channel(ssi->playback.dma_ch); - ssi->playback.dma_ch = NULL; - if (ssi->dma_rt) - ssi->dma_rt = false; - } - - if (ssi->capture.dma_ch) { - dma_release_channel(ssi->capture.dma_ch); - ssi->capture.dma_ch = NULL; - } -} - -static int rz_ssi_dma_request(struct rz_ssi_priv *ssi, struct device *dev) -{ - ssi->playback.dma_ch = dma_request_chan(dev, "tx"); - if (IS_ERR(ssi->playback.dma_ch)) - ssi->playback.dma_ch = NULL; - - ssi->capture.dma_ch = dma_request_chan(dev, "rx"); - if (IS_ERR(ssi->capture.dma_ch)) - ssi->capture.dma_ch = NULL; - - if (!ssi->playback.dma_ch && !ssi->capture.dma_ch) { - ssi->playback.dma_ch = dma_request_chan(dev, "rt"); - if (IS_ERR(ssi->playback.dma_ch)) { - ssi->playback.dma_ch = NULL; - goto no_dma; - } - - ssi->dma_rt = true; - } - - if (!rz_ssi_is_dma_enabled(ssi)) - goto no_dma; - - if (ssi->playback.dma_ch && - (rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, true) < 0)) - goto no_dma; - - if (ssi->capture.dma_ch && - (rz_ssi_dma_slave_config(ssi, ssi->capture.dma_ch, false) < 0)) - goto no_dma; - - return 0; - -no_dma: - rz_ssi_release_dma_channels(ssi); - - return -ENODEV; -} - -static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); - struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream); - int ret = 0, i, num_transfer = 1; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - /* Soft Reset */ - if (!rz_ssi_is_stream_running(&ssi->playback) && - !rz_ssi_is_stream_running(&ssi->capture)) { - rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_SSIRST); - rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_SSIRST, 0); - udelay(5); - } - - rz_ssi_stream_init(strm, substream); - - if (ssi->dma_rt) { - bool is_playback; - - is_playback = rz_ssi_stream_is_play(ssi, substream); - ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, - is_playback); - /* Fallback to pio */ - if (ret < 0) { - ssi->playback.transfer = rz_ssi_pio_send; - ssi->capture.transfer = rz_ssi_pio_recv; - rz_ssi_release_dma_channels(ssi); - } - } - - /* For DMA, queue up multiple DMA descriptors */ - if (rz_ssi_is_dma_enabled(ssi)) - num_transfer = 4; - - for (i = 0; i < num_transfer; i++) { - ret = strm->transfer(ssi, strm); - if (ret) - goto done; - } - - ret = rz_ssi_start(ssi, strm); - break; - case SNDRV_PCM_TRIGGER_STOP: - rz_ssi_stop(ssi, strm); - rz_ssi_stream_quit(ssi, strm); - break; - } - -done: - return ret; -} - -static int rz_ssi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -{ - struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); - - switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { - case SND_SOC_DAIFMT_BP_FP: - break; - default: - dev_err(ssi->dev, "Codec should be clk and frame consumer\n"); - return -EINVAL; - } - - /* - * set clock polarity - * - * "normal" BCLK = Signal is available at rising edge of BCLK - * "normal" FSYNC = (I2S) Left ch starts with falling FSYNC edge - */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - ssi->bckp_rise = false; - ssi->lrckp_fsync_fall = false; - break; - case SND_SOC_DAIFMT_NB_IF: - ssi->bckp_rise = false; - ssi->lrckp_fsync_fall = true; - break; - case SND_SOC_DAIFMT_IB_NF: - ssi->bckp_rise = true; - ssi->lrckp_fsync_fall = false; - break; - case SND_SOC_DAIFMT_IB_IF: - ssi->bckp_rise = true; - ssi->lrckp_fsync_fall = true; - break; - default: - return -EINVAL; - } - - /* only i2s support */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - break; - default: - dev_err(ssi->dev, "Only I2S mode is supported.\n"); - return -EINVAL; - } - - return 0; -} - -static bool rz_ssi_is_valid_hw_params(struct rz_ssi_priv *ssi, unsigned int rate, - unsigned int channels, - unsigned int sample_width, - unsigned int sample_bits) -{ - if (ssi->hw_params_cache.rate != rate || - ssi->hw_params_cache.channels != channels || - ssi->hw_params_cache.sample_width != sample_width || - ssi->hw_params_cache.sample_bits != sample_bits) - return false; - - return true; -} - -static void rz_ssi_cache_hw_params(struct rz_ssi_priv *ssi, unsigned int rate, - unsigned int channels, - unsigned int sample_width, - unsigned int sample_bits) -{ - ssi->hw_params_cache.rate = rate; - ssi->hw_params_cache.channels = channels; - ssi->hw_params_cache.sample_width = sample_width; - ssi->hw_params_cache.sample_bits = sample_bits; -} - -static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); - struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream); - unsigned int sample_bits = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; - unsigned int channels = params_channels(params); - unsigned int rate = params_rate(params); - - if (sample_bits != 16) { - dev_err(ssi->dev, "Unsupported sample width: %d\n", - sample_bits); - return -EINVAL; - } - - if (channels != 2) { - dev_err(ssi->dev, "Number of channels not matched: %d\n", - channels); - return -EINVAL; - } - - if (rz_ssi_is_stream_running(&ssi->playback) || - rz_ssi_is_stream_running(&ssi->capture)) { - if (rz_ssi_is_valid_hw_params(ssi, rate, channels, - strm->sample_width, sample_bits)) - return 0; - - dev_err(ssi->dev, "Full duplex needs same HW params\n"); - return -EINVAL; - } - - rz_ssi_cache_hw_params(ssi, rate, channels, strm->sample_width, - sample_bits); - - return rz_ssi_clk_setup(ssi, rate, channels); -} - -static const struct snd_soc_dai_ops rz_ssi_dai_ops = { - .trigger = rz_ssi_dai_trigger, - .set_fmt = rz_ssi_dai_set_fmt, - .hw_params = rz_ssi_dai_hw_params, -}; - -static const struct snd_pcm_hardware rz_ssi_pcm_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, - .buffer_bytes_max = PREALLOC_BUFFER, - .period_bytes_min = 32, - .period_bytes_max = 8192, - .channels_min = SSI_CHAN_MIN, - .channels_max = SSI_CHAN_MAX, - .periods_min = 1, - .periods_max = 32, - .fifo_size = 32 * 2, -}; - -static int rz_ssi_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - snd_soc_set_runtime_hwparams(substream, &rz_ssi_pcm_hardware); - - return snd_pcm_hw_constraint_integer(substream->runtime, - SNDRV_PCM_HW_PARAM_PERIODS); -} - -static snd_pcm_uframes_t rz_ssi_pcm_pointer(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_dai *dai = rz_ssi_get_dai(substream); - struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); - struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream); - - return strm->buffer_pos; -} - -static int rz_ssi_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) -{ - snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, - rtd->card->snd_card->dev, - PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); - return 0; -} - -static struct snd_soc_dai_driver rz_ssi_soc_dai[] = { - { - .name = "rz-ssi-dai", - .playback = { - .rates = SSI_RATES, - .formats = SSI_FMTS, - .channels_min = SSI_CHAN_MIN, - .channels_max = SSI_CHAN_MAX, - }, - .capture = { - .rates = SSI_RATES, - .formats = SSI_FMTS, - .channels_min = SSI_CHAN_MIN, - .channels_max = SSI_CHAN_MAX, - }, - .ops = &rz_ssi_dai_ops, - }, -}; - -static const struct snd_soc_component_driver rz_ssi_soc_component = { - .name = "rz-ssi", - .open = rz_ssi_pcm_open, - .pointer = rz_ssi_pcm_pointer, - .pcm_construct = rz_ssi_pcm_new, - .legacy_dai_naming = 1, -}; - -static int rz_ssi_probe(struct platform_device *pdev) -{ - struct rz_ssi_priv *ssi; - struct clk *audio_clk; - struct resource *res; - int ret; - - ssi = devm_kzalloc(&pdev->dev, sizeof(*ssi), GFP_KERNEL); - if (!ssi) - return -ENOMEM; - - ssi->pdev = pdev; - ssi->dev = &pdev->dev; - ssi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(ssi->base)) - return PTR_ERR(ssi->base); - - ssi->phys = res->start; - ssi->clk = devm_clk_get(&pdev->dev, "ssi"); - if (IS_ERR(ssi->clk)) - return PTR_ERR(ssi->clk); - - ssi->sfr_clk = devm_clk_get(&pdev->dev, "ssi_sfr"); - if (IS_ERR(ssi->sfr_clk)) - return PTR_ERR(ssi->sfr_clk); - - audio_clk = devm_clk_get(&pdev->dev, "audio_clk1"); - if (IS_ERR(audio_clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk), - "no audio clk1"); - - ssi->audio_clk_1 = clk_get_rate(audio_clk); - audio_clk = devm_clk_get(&pdev->dev, "audio_clk2"); - if (IS_ERR(audio_clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk), - "no audio clk2"); - - ssi->audio_clk_2 = clk_get_rate(audio_clk); - if (!(ssi->audio_clk_1 || ssi->audio_clk_2)) - return dev_err_probe(&pdev->dev, -EINVAL, - "no audio clk1 or audio clk2"); - - ssi->audio_mck = ssi->audio_clk_1 ? ssi->audio_clk_1 : ssi->audio_clk_2; - - /* Detect DMA support */ - ret = rz_ssi_dma_request(ssi, &pdev->dev); - if (ret < 0) { - dev_warn(&pdev->dev, "DMA not available, using PIO\n"); - ssi->playback.transfer = rz_ssi_pio_send; - ssi->capture.transfer = rz_ssi_pio_recv; - } else { - dev_info(&pdev->dev, "DMA enabled"); - ssi->playback.transfer = rz_ssi_dma_transfer; - ssi->capture.transfer = rz_ssi_dma_transfer; - } - - ssi->playback.priv = ssi; - ssi->capture.priv = ssi; - - spin_lock_init(&ssi->lock); - dev_set_drvdata(&pdev->dev, ssi); - - /* Error Interrupt */ - ssi->irq_int = platform_get_irq_byname(pdev, "int_req"); - if (ssi->irq_int < 0) { - rz_ssi_release_dma_channels(ssi); - return ssi->irq_int; - } - - ret = devm_request_irq(&pdev->dev, ssi->irq_int, &rz_ssi_interrupt, - 0, dev_name(&pdev->dev), ssi); - if (ret < 0) { - rz_ssi_release_dma_channels(ssi); - return dev_err_probe(&pdev->dev, ret, - "irq request error (int_req)\n"); - } - - if (!rz_ssi_is_dma_enabled(ssi)) { - /* Tx and Rx interrupts (pio only) */ - ssi->irq_tx = platform_get_irq_byname(pdev, "dma_tx"); - ssi->irq_rx = platform_get_irq_byname(pdev, "dma_rx"); - if (ssi->irq_tx == -ENXIO && ssi->irq_rx == -ENXIO) { - ssi->irq_rt = platform_get_irq_byname(pdev, "dma_rt"); - if (ssi->irq_rt < 0) - return ssi->irq_rt; - - ret = devm_request_irq(&pdev->dev, ssi->irq_rt, - &rz_ssi_interrupt, 0, - dev_name(&pdev->dev), ssi); - if (ret < 0) - return dev_err_probe(&pdev->dev, ret, - "irq request error (dma_rt)\n"); - } else { - if (ssi->irq_tx < 0) - return ssi->irq_tx; - - if (ssi->irq_rx < 0) - return ssi->irq_rx; - - ret = devm_request_irq(&pdev->dev, ssi->irq_tx, - &rz_ssi_interrupt, 0, - dev_name(&pdev->dev), ssi); - if (ret < 0) - return dev_err_probe(&pdev->dev, ret, - "irq request error (dma_tx)\n"); - - ret = devm_request_irq(&pdev->dev, ssi->irq_rx, - &rz_ssi_interrupt, 0, - dev_name(&pdev->dev), ssi); - if (ret < 0) - return dev_err_probe(&pdev->dev, ret, - "irq request error (dma_rx)\n"); - } - } - - ssi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(ssi->rstc)) { - ret = PTR_ERR(ssi->rstc); - goto err_reset; - } - - reset_control_deassert(ssi->rstc); - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_resume_and_get(&pdev->dev); - if (ret < 0) { - dev_err(&pdev->dev, "pm_runtime_resume_and_get failed\n"); - goto err_pm; - } - - ret = devm_snd_soc_register_component(&pdev->dev, &rz_ssi_soc_component, - rz_ssi_soc_dai, - ARRAY_SIZE(rz_ssi_soc_dai)); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register snd component\n"); - goto err_snd_soc; - } - - return 0; - -err_snd_soc: - pm_runtime_put(ssi->dev); -err_pm: - pm_runtime_disable(ssi->dev); - reset_control_assert(ssi->rstc); -err_reset: - rz_ssi_release_dma_channels(ssi); - - return ret; -} - -static void rz_ssi_remove(struct platform_device *pdev) -{ - struct rz_ssi_priv *ssi = dev_get_drvdata(&pdev->dev); - - rz_ssi_release_dma_channels(ssi); - - pm_runtime_put(ssi->dev); - pm_runtime_disable(ssi->dev); - reset_control_assert(ssi->rstc); -} - -static const struct of_device_id rz_ssi_of_match[] = { - { .compatible = "renesas,rz-ssi", }, - {/* Sentinel */}, -}; -MODULE_DEVICE_TABLE(of, rz_ssi_of_match); - -static struct platform_driver rz_ssi_driver = { - .driver = { - .name = "rz-ssi-pcm-audio", - .of_match_table = rz_ssi_of_match, - }, - .probe = rz_ssi_probe, - .remove = rz_ssi_remove, -}; - -module_platform_driver(rz_ssi_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Renesas RZ/G2L ASoC Serial Sound Interface Driver"); -MODULE_AUTHOR("Biju Das "); diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c deleted file mode 100644 index d267243a159b..000000000000 --- a/sound/soc/sh/sh7760-ac97.c +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Generic AC97 sound support for SH7760 -// -// (c) 2007 Manuel Lauss - -#include -#include -#include -#include -#include -#include -#include - -#define IPSEL 0xFE400034 - -SND_SOC_DAILINK_DEFS(ac97, - DAILINK_COMP_ARRAY(COMP_CPU("hac-dai.0")), /* HAC0 */ - DAILINK_COMP_ARRAY(COMP_CODEC("ac97-codec", "ac97-hifi")), - DAILINK_COMP_ARRAY(COMP_PLATFORM("sh7760-pcm-audio"))); - -static struct snd_soc_dai_link sh7760_ac97_dai = { - .name = "AC97", - .stream_name = "AC97 HiFi", - SND_SOC_DAILINK_REG(ac97), -}; - -static struct snd_soc_card sh7760_ac97_soc_machine = { - .name = "SH7760 AC97", - .owner = THIS_MODULE, - .dai_link = &sh7760_ac97_dai, - .num_links = 1, -}; - -static struct platform_device *sh7760_ac97_snd_device; - -static int __init sh7760_ac97_init(void) -{ - int ret; - unsigned short ipsel; - - /* enable both AC97 controllers in pinmux reg */ - ipsel = __raw_readw(IPSEL); - __raw_writew(ipsel | (3 << 10), IPSEL); - - ret = -ENOMEM; - sh7760_ac97_snd_device = platform_device_alloc("soc-audio", -1); - if (!sh7760_ac97_snd_device) - goto out; - - platform_set_drvdata(sh7760_ac97_snd_device, - &sh7760_ac97_soc_machine); - ret = platform_device_add(sh7760_ac97_snd_device); - - if (ret) - platform_device_put(sh7760_ac97_snd_device); - -out: - return ret; -} - -static void __exit sh7760_ac97_exit(void) -{ - platform_device_unregister(sh7760_ac97_snd_device); -} - -module_init(sh7760_ac97_init); -module_exit(sh7760_ac97_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine"); -MODULE_AUTHOR("Manuel Lauss "); diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h deleted file mode 100644 index a675c36fc9d9..000000000000 --- a/sound/soc/sh/siu.h +++ /dev/null @@ -1,180 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. -// -// Copyright (C) 2009-2010 Guennadi Liakhovetski -// Copyright (C) 2006 Carlos Munoz - -#ifndef SIU_H -#define SIU_H - -/* Common kernel and user-space firmware-building defines and types */ - -#define YRAM0_SIZE (0x0040 / 4) /* 16 */ -#define YRAM1_SIZE (0x0080 / 4) /* 32 */ -#define YRAM2_SIZE (0x0040 / 4) /* 16 */ -#define YRAM3_SIZE (0x0080 / 4) /* 32 */ -#define YRAM4_SIZE (0x0080 / 4) /* 32 */ -#define YRAM_DEF_SIZE (YRAM0_SIZE + YRAM1_SIZE + YRAM2_SIZE + \ - YRAM3_SIZE + YRAM4_SIZE) -#define YRAM_FIR_SIZE (0x0400 / 4) /* 256 */ -#define YRAM_IIR_SIZE (0x0200 / 4) /* 128 */ - -#define XRAM0_SIZE (0x0400 / 4) /* 256 */ -#define XRAM1_SIZE (0x0200 / 4) /* 128 */ -#define XRAM2_SIZE (0x0200 / 4) /* 128 */ - -/* PRAM program array size */ -#define PRAM0_SIZE (0x0100 / 4) /* 64 */ -#define PRAM1_SIZE ((0x2000 - 0x0100) / 4) /* 1984 */ - -#include - -struct siu_spb_param { - __u32 ab1a; /* input FIFO address */ - __u32 ab0a; /* output FIFO address */ - __u32 dir; /* 0=the ather except CPUOUTPUT, 1=CPUINPUT */ - __u32 event; /* SPB program starting conditions */ - __u32 stfifo; /* STFIFO register setting value */ - __u32 trdat; /* TRDAT register setting value */ -}; - -struct siu_firmware { - __u32 yram_fir_coeff[YRAM_FIR_SIZE]; - __u32 pram0[PRAM0_SIZE]; - __u32 pram1[PRAM1_SIZE]; - __u32 yram0[YRAM0_SIZE]; - __u32 yram1[YRAM1_SIZE]; - __u32 yram2[YRAM2_SIZE]; - __u32 yram3[YRAM3_SIZE]; - __u32 yram4[YRAM4_SIZE]; - __u32 spbpar_num; - struct siu_spb_param spbpar[32]; -}; - -#ifdef __KERNEL__ - -#include -#include -#include -#include - -#include -#include -#include - -#define SIU_PERIOD_BYTES_MAX 8192 /* DMA transfer/period size */ -#define SIU_PERIOD_BYTES_MIN 256 /* DMA transfer/period size */ -#define SIU_PERIODS_MAX 64 /* Max periods in buffer */ -#define SIU_PERIODS_MIN 4 /* Min periods in buffer */ -#define SIU_BUFFER_BYTES_MAX (SIU_PERIOD_BYTES_MAX * SIU_PERIODS_MAX) - -/* SIU ports: only one can be used at a time */ -enum { - SIU_PORT_A, - SIU_PORT_B, - SIU_PORT_NUM, -}; - -/* SIU clock configuration */ -enum { - SIU_CLKA_PLL, - SIU_CLKA_EXT, - SIU_CLKB_PLL, - SIU_CLKB_EXT -}; - -struct device; -struct siu_info { - struct device *dev; - int port_id; - u32 __iomem *pram; - u32 __iomem *xram; - u32 __iomem *yram; - u32 __iomem *reg; - struct siu_firmware fw; -}; - -struct siu_stream { - struct work_struct work; - struct snd_pcm_substream *substream; - snd_pcm_format_t format; - size_t buf_bytes; - size_t period_bytes; - int cur_period; /* Period currently in dma */ - u32 volume; - snd_pcm_sframes_t xfer_cnt; /* Number of frames */ - u8 rw_flg; /* transfer status */ - /* DMA status */ - struct dma_chan *chan; /* DMA channel */ - struct dma_async_tx_descriptor *tx_desc; - dma_cookie_t cookie; - struct sh_dmae_slave param; -}; - -struct siu_port { - unsigned long play_cap; /* Used to track full duplex */ - struct snd_pcm *pcm; - struct siu_stream playback; - struct siu_stream capture; - u32 stfifo; /* STFIFO value from firmware */ - u32 trdat; /* TRDAT value from firmware */ -}; - -extern struct siu_port *siu_ports[SIU_PORT_NUM]; - -static inline struct siu_port *siu_port_info(struct snd_pcm_substream *substream) -{ - struct platform_device *pdev = - to_platform_device(substream->pcm->card->dev); - return siu_ports[pdev->id]; -} - -/* Register access */ -static inline void siu_write32(u32 __iomem *addr, u32 val) -{ - __raw_writel(val, addr); -} - -static inline u32 siu_read32(u32 __iomem *addr) -{ - return __raw_readl(addr); -} - -/* SIU registers */ -#define SIU_IFCTL (0x000 / sizeof(u32)) -#define SIU_SRCTL (0x004 / sizeof(u32)) -#define SIU_SFORM (0x008 / sizeof(u32)) -#define SIU_CKCTL (0x00c / sizeof(u32)) -#define SIU_TRDAT (0x010 / sizeof(u32)) -#define SIU_STFIFO (0x014 / sizeof(u32)) -#define SIU_DPAK (0x01c / sizeof(u32)) -#define SIU_CKREV (0x020 / sizeof(u32)) -#define SIU_EVNTC (0x028 / sizeof(u32)) -#define SIU_SBCTL (0x040 / sizeof(u32)) -#define SIU_SBPSET (0x044 / sizeof(u32)) -#define SIU_SBFSTS (0x068 / sizeof(u32)) -#define SIU_SBDVCA (0x06c / sizeof(u32)) -#define SIU_SBDVCB (0x070 / sizeof(u32)) -#define SIU_SBACTIV (0x074 / sizeof(u32)) -#define SIU_DMAIA (0x090 / sizeof(u32)) -#define SIU_DMAIB (0x094 / sizeof(u32)) -#define SIU_DMAOA (0x098 / sizeof(u32)) -#define SIU_DMAOB (0x09c / sizeof(u32)) -#define SIU_DMAML (0x0a0 / sizeof(u32)) -#define SIU_SPSTS (0x0cc / sizeof(u32)) -#define SIU_SPCTL (0x0d0 / sizeof(u32)) -#define SIU_BRGASEL (0x100 / sizeof(u32)) -#define SIU_BRRA (0x104 / sizeof(u32)) -#define SIU_BRGBSEL (0x108 / sizeof(u32)) -#define SIU_BRRB (0x10c / sizeof(u32)) - -extern const struct snd_soc_component_driver siu_component; -extern struct siu_info *siu_i2s_data; - -int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card); -void siu_free_port(struct siu_port *port_info); - -#endif - -#endif /* SIU_H */ diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c deleted file mode 100644 index 7e771a164a80..000000000000 --- a/sound/soc/sh/siu_dai.c +++ /dev/null @@ -1,800 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. -// -// Copyright (C) 2009-2010 Guennadi Liakhovetski -// Copyright (C) 2006 Carlos Munoz - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "siu.h" - -/* Board specifics */ -#if defined(CONFIG_CPU_SUBTYPE_SH7722) -# define SIU_MAX_VOLUME 0x1000 -#else -# define SIU_MAX_VOLUME 0x7fff -#endif - -#define PRAM_SIZE 0x2000 -#define XRAM_SIZE 0x800 -#define YRAM_SIZE 0x800 - -#define XRAM_OFFSET 0x4000 -#define YRAM_OFFSET 0x6000 -#define REG_OFFSET 0xc000 - -#define PLAYBACK_ENABLED 1 -#define CAPTURE_ENABLED 2 - -#define VOLUME_CAPTURE 0 -#define VOLUME_PLAYBACK 1 -#define DFLT_VOLUME_LEVEL 0x08000800 - -/* - * SPDIF is only available on port A and on some SIU implementations it is only - * available for input. Due to the lack of hardware to test it, SPDIF is left - * disabled in this driver version - */ -struct format_flag { - u32 i2s; - u32 pcm; - u32 spdif; - u32 mask; -}; - -struct port_flag { - struct format_flag playback; - struct format_flag capture; -}; - -struct siu_info *siu_i2s_data; - -static struct port_flag siu_flags[SIU_PORT_NUM] = { - [SIU_PORT_A] = { - .playback = { - .i2s = 0x50000000, - .pcm = 0x40000000, - .spdif = 0x80000000, /* not on all SIU versions */ - .mask = 0xd0000000, - }, - .capture = { - .i2s = 0x05000000, - .pcm = 0x04000000, - .spdif = 0x08000000, - .mask = 0x0d000000, - }, - }, - [SIU_PORT_B] = { - .playback = { - .i2s = 0x00500000, - .pcm = 0x00400000, - .spdif = 0, /* impossible - turn off */ - .mask = 0x00500000, - }, - .capture = { - .i2s = 0x00050000, - .pcm = 0x00040000, - .spdif = 0, /* impossible - turn off */ - .mask = 0x00050000, - }, - }, -}; - -static void siu_dai_start(struct siu_port *port_info) -{ - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - - dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); - - /* Issue software reset to siu */ - siu_write32(base + SIU_SRCTL, 0); - - /* Wait for the reset to take effect */ - udelay(1); - - port_info->stfifo = 0; - port_info->trdat = 0; - - /* portA, portB, SIU operate */ - siu_write32(base + SIU_SRCTL, 0x301); - - /* portA=256fs, portB=256fs */ - siu_write32(base + SIU_CKCTL, 0x40400000); - - /* portA's BRG does not divide SIUCKA */ - siu_write32(base + SIU_BRGASEL, 0); - siu_write32(base + SIU_BRRA, 0); - - /* portB's BRG divides SIUCKB by half */ - siu_write32(base + SIU_BRGBSEL, 1); - siu_write32(base + SIU_BRRB, 0); - - siu_write32(base + SIU_IFCTL, 0x44440000); - - /* portA: 32 bit/fs, master; portB: 32 bit/fs, master */ - siu_write32(base + SIU_SFORM, 0x0c0c0000); - - /* - * Volume levels: looks like the DSP firmware implements volume controls - * differently from what's described in the datasheet - */ - siu_write32(base + SIU_SBDVCA, port_info->playback.volume); - siu_write32(base + SIU_SBDVCB, port_info->capture.volume); -} - -static void siu_dai_stop(struct siu_port *port_info) -{ - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - - /* SIU software reset */ - siu_write32(base + SIU_SRCTL, 0); -} - -static void siu_dai_spbAselect(struct siu_port *port_info) -{ - struct siu_info *info = siu_i2s_data; - struct siu_firmware *fw = &info->fw; - u32 *ydef = fw->yram0; - u32 idx; - - /* path A use */ - if (!info->port_id) - idx = 1; /* portA */ - else - idx = 2; /* portB */ - - ydef[0] = (fw->spbpar[idx].ab1a << 16) | - (fw->spbpar[idx].ab0a << 8) | - (fw->spbpar[idx].dir << 7) | 3; - ydef[1] = fw->yram0[1]; /* 0x03000300 */ - ydef[2] = (16 / 2) << 24; - ydef[3] = fw->yram0[3]; /* 0 */ - ydef[4] = fw->yram0[4]; /* 0 */ - ydef[7] = fw->spbpar[idx].event; - port_info->stfifo |= fw->spbpar[idx].stfifo; - port_info->trdat |= fw->spbpar[idx].trdat; -} - -static void siu_dai_spbBselect(struct siu_port *port_info) -{ - struct siu_info *info = siu_i2s_data; - struct siu_firmware *fw = &info->fw; - u32 *ydef = fw->yram0; - u32 idx; - - /* path B use */ - if (!info->port_id) - idx = 7; /* portA */ - else - idx = 8; /* portB */ - - ydef[5] = (fw->spbpar[idx].ab1a << 16) | - (fw->spbpar[idx].ab0a << 8) | 1; - ydef[6] = fw->spbpar[idx].event; - port_info->stfifo |= fw->spbpar[idx].stfifo; - port_info->trdat |= fw->spbpar[idx].trdat; -} - -static void siu_dai_open(struct siu_stream *siu_stream) -{ - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - u32 srctl, ifctl; - - srctl = siu_read32(base + SIU_SRCTL); - ifctl = siu_read32(base + SIU_IFCTL); - - switch (info->port_id) { - case SIU_PORT_A: - /* portA operates */ - srctl |= 0x200; - ifctl &= ~0xc2; - break; - case SIU_PORT_B: - /* portB operates */ - srctl |= 0x100; - ifctl &= ~0x31; - break; - } - - siu_write32(base + SIU_SRCTL, srctl); - /* Unmute and configure portA */ - siu_write32(base + SIU_IFCTL, ifctl); -} - -/* - * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower - * packing is supported - */ -static void siu_dai_pcmdatapack(struct siu_stream *siu_stream) -{ - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - u32 dpak; - - dpak = siu_read32(base + SIU_DPAK); - - switch (info->port_id) { - case SIU_PORT_A: - dpak &= ~0xc0000000; - break; - case SIU_PORT_B: - dpak &= ~0x00c00000; - break; - } - - siu_write32(base + SIU_DPAK, dpak); -} - -static int siu_dai_spbstart(struct siu_port *port_info) -{ - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - struct siu_firmware *fw = &info->fw; - u32 *ydef = fw->yram0; - int cnt; - u32 __iomem *add; - u32 *ptr; - - /* Load SPB Program in PRAM */ - ptr = fw->pram0; - add = info->pram; - for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++) - siu_write32(add, *ptr); - - ptr = fw->pram1; - add = info->pram + (0x0100 / sizeof(u32)); - for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++) - siu_write32(add, *ptr); - - /* XRAM initialization */ - add = info->xram; - for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++) - siu_write32(add, 0); - - /* YRAM variable area initialization */ - add = info->yram; - for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++) - siu_write32(add, ydef[cnt]); - - /* YRAM FIR coefficient area initialization */ - add = info->yram + (0x0200 / sizeof(u32)); - for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++) - siu_write32(add, fw->yram_fir_coeff[cnt]); - - /* YRAM IIR coefficient area initialization */ - add = info->yram + (0x0600 / sizeof(u32)); - for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++) - siu_write32(add, 0); - - siu_write32(base + SIU_TRDAT, port_info->trdat); - port_info->trdat = 0x0; - - - /* SPB start condition: software */ - siu_write32(base + SIU_SBACTIV, 0); - /* Start SPB */ - siu_write32(base + SIU_SBCTL, 0xc0000000); - /* Wait for program to halt */ - cnt = 0x10000; - while (--cnt && siu_read32(base + SIU_SBCTL) != 0x80000000) - cpu_relax(); - - if (!cnt) - return -EBUSY; - - /* SPB program start address setting */ - siu_write32(base + SIU_SBPSET, 0x00400000); - /* SPB hardware start(FIFOCTL source) */ - siu_write32(base + SIU_SBACTIV, 0xc0000000); - - return 0; -} - -static void siu_dai_spbstop(struct siu_port *port_info) -{ - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - - siu_write32(base + SIU_SBACTIV, 0); - /* SPB stop */ - siu_write32(base + SIU_SBCTL, 0); - - port_info->stfifo = 0; -} - -/* API functions */ - -/* Playback and capture hardware properties are identical */ -static const struct snd_pcm_hardware siu_dai_pcm_hw = { - .info = SNDRV_PCM_INFO_INTERLEAVED, - .formats = SNDRV_PCM_FMTBIT_S16, - .rates = SNDRV_PCM_RATE_8000_48000, - .rate_min = 8000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = SIU_BUFFER_BYTES_MAX, - .period_bytes_min = SIU_PERIOD_BYTES_MIN, - .period_bytes_max = SIU_PERIOD_BYTES_MAX, - .periods_min = SIU_PERIODS_MIN, - .periods_max = SIU_PERIODS_MAX, -}; - -static int siu_dai_info_volume(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_info *uinfo) -{ - struct siu_port *port_info = snd_kcontrol_chip(kctrl); - - dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = SIU_MAX_VOLUME; - - return 0; -} - -static int siu_dai_get_volume(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_value *ucontrol) -{ - struct siu_port *port_info = snd_kcontrol_chip(kctrl); - struct device *dev = port_info->pcm->card->dev; - u32 vol; - - dev_dbg(dev, "%s\n", __func__); - - switch (kctrl->private_value) { - case VOLUME_PLAYBACK: - /* Playback is always on port 0 */ - vol = port_info->playback.volume; - ucontrol->value.integer.value[0] = vol & 0xffff; - ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; - break; - case VOLUME_CAPTURE: - /* Capture is always on port 1 */ - vol = port_info->capture.volume; - ucontrol->value.integer.value[0] = vol & 0xffff; - ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; - break; - default: - dev_err(dev, "%s() invalid private_value=%ld\n", - __func__, kctrl->private_value); - return -EINVAL; - } - - return 0; -} - -static int siu_dai_put_volume(struct snd_kcontrol *kctrl, - struct snd_ctl_elem_value *ucontrol) -{ - struct siu_port *port_info = snd_kcontrol_chip(kctrl); - struct device *dev = port_info->pcm->card->dev; - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - u32 new_vol; - u32 cur_vol; - - dev_dbg(dev, "%s\n", __func__); - - if (ucontrol->value.integer.value[0] < 0 || - ucontrol->value.integer.value[0] > SIU_MAX_VOLUME || - ucontrol->value.integer.value[1] < 0 || - ucontrol->value.integer.value[1] > SIU_MAX_VOLUME) - return -EINVAL; - - new_vol = ucontrol->value.integer.value[0] | - ucontrol->value.integer.value[1] << 16; - - /* See comment above - DSP firmware implementation */ - switch (kctrl->private_value) { - case VOLUME_PLAYBACK: - /* Playback is always on port 0 */ - cur_vol = port_info->playback.volume; - siu_write32(base + SIU_SBDVCA, new_vol); - port_info->playback.volume = new_vol; - break; - case VOLUME_CAPTURE: - /* Capture is always on port 1 */ - cur_vol = port_info->capture.volume; - siu_write32(base + SIU_SBDVCB, new_vol); - port_info->capture.volume = new_vol; - break; - default: - dev_err(dev, "%s() invalid private_value=%ld\n", - __func__, kctrl->private_value); - return -EINVAL; - } - - if (cur_vol != new_vol) - return 1; - - return 0; -} - -static const struct snd_kcontrol_new playback_controls = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Volume", - .index = 0, - .info = siu_dai_info_volume, - .get = siu_dai_get_volume, - .put = siu_dai_put_volume, - .private_value = VOLUME_PLAYBACK, -}; - -static const struct snd_kcontrol_new capture_controls = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Capture Volume", - .index = 0, - .info = siu_dai_info_volume, - .get = siu_dai_get_volume, - .put = siu_dai_put_volume, - .private_value = VOLUME_CAPTURE, -}; - -int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card) -{ - struct device *dev = card->dev; - struct snd_kcontrol *kctrl; - int ret; - - *port_info = kzalloc(sizeof(**port_info), GFP_KERNEL); - if (!*port_info) - return -ENOMEM; - - dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info); - - (*port_info)->playback.volume = DFLT_VOLUME_LEVEL; - (*port_info)->capture.volume = DFLT_VOLUME_LEVEL; - - /* - * Add mixer support. The SPB is used to change the volume. Both - * ports use the same SPB. Therefore, we only register one - * control instance since it will be used by both channels. - * In error case we continue without controls. - */ - kctrl = snd_ctl_new1(&playback_controls, *port_info); - ret = snd_ctl_add(card, kctrl); - if (ret < 0) - dev_err(dev, - "failed to add playback controls %p port=%d err=%d\n", - kctrl, port, ret); - - kctrl = snd_ctl_new1(&capture_controls, *port_info); - ret = snd_ctl_add(card, kctrl); - if (ret < 0) - dev_err(dev, - "failed to add capture controls %p port=%d err=%d\n", - kctrl, port, ret); - - return 0; -} - -void siu_free_port(struct siu_port *port_info) -{ - kfree(port_info); -} - -static int siu_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct siu_info *info = snd_soc_dai_get_drvdata(dai); - struct snd_pcm_runtime *rt = substream->runtime; - struct siu_port *port_info = siu_port_info(substream); - int ret; - - dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, - info->port_id, port_info); - - snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw); - - ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); - if (unlikely(ret < 0)) - return ret; - - siu_dai_start(port_info); - - return 0; -} - -static void siu_dai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct siu_info *info = snd_soc_dai_get_drvdata(dai); - struct siu_port *port_info = siu_port_info(substream); - - dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, - info->port_id, port_info); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - port_info->play_cap &= ~PLAYBACK_ENABLED; - else - port_info->play_cap &= ~CAPTURE_ENABLED; - - /* Stop the siu if the other stream is not using it */ - if (!port_info->play_cap) { - /* during stmread or stmwrite ? */ - if (WARN_ON(port_info->playback.rw_flg || port_info->capture.rw_flg)) - return; - siu_dai_spbstop(port_info); - siu_dai_stop(port_info); - } -} - -/* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */ -static int siu_dai_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct siu_info *info = snd_soc_dai_get_drvdata(dai); - struct snd_pcm_runtime *rt = substream->runtime; - struct siu_port *port_info = siu_port_info(substream); - struct siu_stream *siu_stream; - int self, ret; - - dev_dbg(substream->pcm->card->dev, - "%s: port %d, active streams %lx, %d channels\n", - __func__, info->port_id, port_info->play_cap, rt->channels); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - self = PLAYBACK_ENABLED; - siu_stream = &port_info->playback; - } else { - self = CAPTURE_ENABLED; - siu_stream = &port_info->capture; - } - - /* Set up the siu if not already done */ - if (!port_info->play_cap) { - siu_stream->rw_flg = 0; /* stream-data transfer flag */ - - siu_dai_spbAselect(port_info); - siu_dai_spbBselect(port_info); - - siu_dai_open(siu_stream); - - siu_dai_pcmdatapack(siu_stream); - - ret = siu_dai_spbstart(port_info); - if (ret < 0) - goto fail; - } else { - ret = 0; - } - - port_info->play_cap |= self; - -fail: - return ret; -} - -/* - * SIU can set bus format to I2S / PCM / SPDIF independently for playback and - * capture, however, the current API sets the bus format globally for a DAI. - */ -static int siu_dai_set_fmt(struct snd_soc_dai *dai, - unsigned int fmt) -{ - struct siu_info *info = snd_soc_dai_get_drvdata(dai); - u32 __iomem *base = info->reg; - u32 ifctl; - - dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n", - __func__, fmt, info->port_id); - - if (info->port_id < 0) - return -ENODEV; - - /* Here select between I2S / PCM / SPDIF */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - ifctl = siu_flags[info->port_id].playback.i2s | - siu_flags[info->port_id].capture.i2s; - break; - case SND_SOC_DAIFMT_LEFT_J: - ifctl = siu_flags[info->port_id].playback.pcm | - siu_flags[info->port_id].capture.pcm; - break; - /* SPDIF disabled - see comment at the top */ - default: - return -EINVAL; - } - - ifctl |= ~(siu_flags[info->port_id].playback.mask | - siu_flags[info->port_id].capture.mask) & - siu_read32(base + SIU_IFCTL); - siu_write32(base + SIU_IFCTL, ifctl); - - return 0; -} - -static int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) -{ - struct clk *siu_clk, *parent_clk; - char *siu_name, *parent_name; - int ret; - - if (dir != SND_SOC_CLOCK_IN) - return -EINVAL; - - dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id); - - switch (clk_id) { - case SIU_CLKA_PLL: - siu_name = "siua_clk"; - parent_name = "pll_clk"; - break; - case SIU_CLKA_EXT: - siu_name = "siua_clk"; - parent_name = "siumcka_clk"; - break; - case SIU_CLKB_PLL: - siu_name = "siub_clk"; - parent_name = "pll_clk"; - break; - case SIU_CLKB_EXT: - siu_name = "siub_clk"; - parent_name = "siumckb_clk"; - break; - default: - return -EINVAL; - } - - siu_clk = clk_get(dai->dev, siu_name); - if (IS_ERR(siu_clk)) { - dev_err(dai->dev, "%s: cannot get a SIU clock: %ld\n", __func__, - PTR_ERR(siu_clk)); - return PTR_ERR(siu_clk); - } - - parent_clk = clk_get(dai->dev, parent_name); - if (IS_ERR(parent_clk)) { - ret = PTR_ERR(parent_clk); - dev_err(dai->dev, "cannot get a SIU clock parent: %d\n", ret); - goto epclkget; - } - - ret = clk_set_parent(siu_clk, parent_clk); - if (ret < 0) { - dev_err(dai->dev, "cannot reparent the SIU clock: %d\n", ret); - goto eclksetp; - } - - ret = clk_set_rate(siu_clk, freq); - if (ret < 0) - dev_err(dai->dev, "cannot set SIU clock rate: %d\n", ret); - - /* TODO: when clkdev gets reference counting we'll move these to siu_dai_shutdown() */ -eclksetp: - clk_put(parent_clk); -epclkget: - clk_put(siu_clk); - - return ret; -} - -static const struct snd_soc_dai_ops siu_dai_ops = { - .startup = siu_dai_startup, - .shutdown = siu_dai_shutdown, - .prepare = siu_dai_prepare, - .set_sysclk = siu_dai_set_sysclk, - .set_fmt = siu_dai_set_fmt, -}; - -static struct snd_soc_dai_driver siu_i2s_dai = { - .name = "siu-i2s-dai", - .playback = { - .channels_min = 2, - .channels_max = 2, - .formats = SNDRV_PCM_FMTBIT_S16, - .rates = SNDRV_PCM_RATE_8000_48000, - }, - .capture = { - .channels_min = 2, - .channels_max = 2, - .formats = SNDRV_PCM_FMTBIT_S16, - .rates = SNDRV_PCM_RATE_8000_48000, - }, - .ops = &siu_dai_ops, -}; - -static int siu_probe(struct platform_device *pdev) -{ - const struct firmware *fw_entry; - struct resource *res, *region; - struct siu_info *info; - int ret; - - info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - siu_i2s_data = info; - info->dev = &pdev->dev; - - ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); - if (ret) - return ret; - - /* - * Loaded firmware is "const" - read only, but we have to modify it in - * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect() - */ - memcpy(&info->fw, fw_entry->data, fw_entry->size); - - release_firmware(fw_entry); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - region = devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name); - if (!region) { - dev_err(&pdev->dev, "SIU region already claimed\n"); - return -EBUSY; - } - - info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE); - if (!info->pram) - return -ENOMEM; - info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET, - XRAM_SIZE); - if (!info->xram) - return -ENOMEM; - info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET, - YRAM_SIZE); - if (!info->yram) - return -ENOMEM; - info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET, - resource_size(res) - REG_OFFSET); - if (!info->reg) - return -ENOMEM; - - dev_set_drvdata(&pdev->dev, info); - - /* register using ARRAY version so we can keep dai name */ - ret = devm_snd_soc_register_component(&pdev->dev, &siu_component, - &siu_i2s_dai, 1); - if (ret < 0) - return ret; - - pm_runtime_enable(&pdev->dev); - - return 0; -} - -static void siu_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); -} - -static struct platform_driver siu_driver = { - .driver = { - .name = "siu-pcm-audio", - }, - .probe = siu_probe, - .remove = siu_remove, -}; - -module_platform_driver(siu_driver); - -MODULE_AUTHOR("Carlos Munoz "); -MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver"); -MODULE_LICENSE("GPL"); - -MODULE_FIRMWARE("siu_spb.bin"); diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c deleted file mode 100644 index f15ff36e7934..000000000000 --- a/sound/soc/sh/siu_pcm.c +++ /dev/null @@ -1,553 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral. -// -// Copyright (C) 2009-2010 Guennadi Liakhovetski -// Copyright (C) 2006 Carlos Munoz - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "siu.h" - -#define DRV_NAME "siu-i2s" -#define GET_MAX_PERIODS(buf_bytes, period_bytes) \ - ((buf_bytes) / (period_bytes)) -#define PERIOD_OFFSET(buf_addr, period_num, period_bytes) \ - ((buf_addr) + ((period_num) * (period_bytes))) - -#define RWF_STM_RD 0x01 /* Read in progress */ -#define RWF_STM_WT 0x02 /* Write in progress */ - -struct siu_port *siu_ports[SIU_PORT_NUM]; - -/* transfersize is number of u32 dma transfers per period */ -static int siu_pcm_stmwrite_stop(struct siu_port *port_info) -{ - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - struct siu_stream *siu_stream = &port_info->playback; - u32 stfifo; - - if (!siu_stream->rw_flg) - return -EPERM; - - /* output FIFO disable */ - stfifo = siu_read32(base + SIU_STFIFO); - siu_write32(base + SIU_STFIFO, stfifo & ~0x0c180c18); - pr_debug("%s: STFIFO %x -> %x\n", __func__, - stfifo, stfifo & ~0x0c180c18); - - /* during stmwrite clear */ - siu_stream->rw_flg = 0; - - return 0; -} - -static int siu_pcm_stmwrite_start(struct siu_port *port_info) -{ - struct siu_stream *siu_stream = &port_info->playback; - - if (siu_stream->rw_flg) - return -EPERM; - - /* Current period in buffer */ - port_info->playback.cur_period = 0; - - /* during stmwrite flag set */ - siu_stream->rw_flg = RWF_STM_WT; - - /* DMA transfer start */ - queue_work(system_highpri_wq, &siu_stream->work); - - return 0; -} - -static void siu_dma_tx_complete(void *arg) -{ - struct siu_stream *siu_stream = arg; - - if (!siu_stream->rw_flg) - return; - - /* Update completed period count */ - if (++siu_stream->cur_period >= - GET_MAX_PERIODS(siu_stream->buf_bytes, - siu_stream->period_bytes)) - siu_stream->cur_period = 0; - - pr_debug("%s: done period #%d (%u/%u bytes), cookie %d\n", - __func__, siu_stream->cur_period, - siu_stream->cur_period * siu_stream->period_bytes, - siu_stream->buf_bytes, siu_stream->cookie); - - queue_work(system_highpri_wq, &siu_stream->work); - - /* Notify alsa: a period is done */ - snd_pcm_period_elapsed(siu_stream->substream); -} - -static int siu_pcm_wr_set(struct siu_port *port_info, - dma_addr_t buff, u32 size) -{ - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - struct siu_stream *siu_stream = &port_info->playback; - struct snd_pcm_substream *substream = siu_stream->substream; - struct device *dev = substream->pcm->card->dev; - struct dma_async_tx_descriptor *desc; - dma_cookie_t cookie; - struct scatterlist sg; - u32 stfifo; - - sg_init_table(&sg, 1); - sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)), - size, offset_in_page(buff)); - sg_dma_len(&sg) = size; - sg_dma_address(&sg) = buff; - - desc = dmaengine_prep_slave_sg(siu_stream->chan, - &sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - dev_err(dev, "Failed to allocate a dma descriptor\n"); - return -ENOMEM; - } - - desc->callback = siu_dma_tx_complete; - desc->callback_param = siu_stream; - cookie = dmaengine_submit(desc); - if (cookie < 0) { - dev_err(dev, "Failed to submit a dma transfer\n"); - return cookie; - } - - siu_stream->tx_desc = desc; - siu_stream->cookie = cookie; - - dma_async_issue_pending(siu_stream->chan); - - /* only output FIFO enable */ - stfifo = siu_read32(base + SIU_STFIFO); - siu_write32(base + SIU_STFIFO, stfifo | (port_info->stfifo & 0x0c180c18)); - dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__, - stfifo, stfifo | (port_info->stfifo & 0x0c180c18)); - - return 0; -} - -static int siu_pcm_rd_set(struct siu_port *port_info, - dma_addr_t buff, size_t size) -{ - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - struct siu_stream *siu_stream = &port_info->capture; - struct snd_pcm_substream *substream = siu_stream->substream; - struct device *dev = substream->pcm->card->dev; - struct dma_async_tx_descriptor *desc; - dma_cookie_t cookie; - struct scatterlist sg; - u32 stfifo; - - dev_dbg(dev, "%s: %u@%llx\n", __func__, size, (unsigned long long)buff); - - sg_init_table(&sg, 1); - sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)), - size, offset_in_page(buff)); - sg_dma_len(&sg) = size; - sg_dma_address(&sg) = buff; - - desc = dmaengine_prep_slave_sg(siu_stream->chan, - &sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - dev_err(dev, "Failed to allocate dma descriptor\n"); - return -ENOMEM; - } - - desc->callback = siu_dma_tx_complete; - desc->callback_param = siu_stream; - cookie = dmaengine_submit(desc); - if (cookie < 0) { - dev_err(dev, "Failed to submit dma descriptor\n"); - return cookie; - } - - siu_stream->tx_desc = desc; - siu_stream->cookie = cookie; - - dma_async_issue_pending(siu_stream->chan); - - /* only input FIFO enable */ - stfifo = siu_read32(base + SIU_STFIFO); - siu_write32(base + SIU_STFIFO, siu_read32(base + SIU_STFIFO) | - (port_info->stfifo & 0x13071307)); - dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__, - stfifo, stfifo | (port_info->stfifo & 0x13071307)); - - return 0; -} - -static void siu_io_work(struct work_struct *work) -{ - struct siu_stream *siu_stream = container_of(work, struct siu_stream, - work); - struct snd_pcm_substream *substream = siu_stream->substream; - struct device *dev = substream->pcm->card->dev; - struct snd_pcm_runtime *rt = substream->runtime; - struct siu_port *port_info = siu_port_info(substream); - - dev_dbg(dev, "%s: flags %x\n", __func__, siu_stream->rw_flg); - - if (!siu_stream->rw_flg) { - dev_dbg(dev, "%s: stream inactive\n", __func__); - return; - } - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - dma_addr_t buff; - size_t count; - - buff = (dma_addr_t)PERIOD_OFFSET(rt->dma_addr, - siu_stream->cur_period, - siu_stream->period_bytes); - count = siu_stream->period_bytes; - - /* DMA transfer start */ - siu_pcm_rd_set(port_info, buff, count); - } else { - siu_pcm_wr_set(port_info, - (dma_addr_t)PERIOD_OFFSET(rt->dma_addr, - siu_stream->cur_period, - siu_stream->period_bytes), - siu_stream->period_bytes); - } -} - -/* Capture */ -static int siu_pcm_stmread_start(struct siu_port *port_info) -{ - struct siu_stream *siu_stream = &port_info->capture; - - if (siu_stream->xfer_cnt > 0x1000000) - return -EINVAL; - if (siu_stream->rw_flg) - return -EPERM; - - /* Current period in buffer */ - siu_stream->cur_period = 0; - - /* during stmread flag set */ - siu_stream->rw_flg = RWF_STM_RD; - - queue_work(system_highpri_wq, &siu_stream->work); - - return 0; -} - -static int siu_pcm_stmread_stop(struct siu_port *port_info) -{ - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - struct siu_stream *siu_stream = &port_info->capture; - struct device *dev = siu_stream->substream->pcm->card->dev; - u32 stfifo; - - if (!siu_stream->rw_flg) - return -EPERM; - - /* input FIFO disable */ - stfifo = siu_read32(base + SIU_STFIFO); - siu_write32(base + SIU_STFIFO, stfifo & ~0x13071307); - dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__, - stfifo, stfifo & ~0x13071307); - - /* during stmread flag clear */ - siu_stream->rw_flg = 0; - - return 0; -} - -static bool filter(struct dma_chan *chan, void *secondary) -{ - struct sh_dmae_slave *param = secondary; - - pr_debug("%s: secondary ID %d\n", __func__, param->shdma_slave.slave_id); - - chan->private = ¶m->shdma_slave; - return true; -} - -static int siu_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *ss) -{ - /* Playback / Capture */ - struct siu_platform *pdata = component->dev->platform_data; - struct siu_info *info = siu_i2s_data; - struct siu_port *port_info = siu_port_info(ss); - struct siu_stream *siu_stream; - u32 port = info->port_id; - struct device *dev = ss->pcm->card->dev; - dma_cap_mask_t mask; - struct sh_dmae_slave *param; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - dev_dbg(dev, "%s, port=%d@%p\n", __func__, port, port_info); - - if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) { - siu_stream = &port_info->playback; - param = &siu_stream->param; - param->shdma_slave.slave_id = port ? pdata->dma_slave_tx_b : - pdata->dma_slave_tx_a; - } else { - siu_stream = &port_info->capture; - param = &siu_stream->param; - param->shdma_slave.slave_id = port ? pdata->dma_slave_rx_b : - pdata->dma_slave_rx_a; - } - - /* Get DMA channel */ - siu_stream->chan = dma_request_channel(mask, filter, param); - if (!siu_stream->chan) { - dev_err(dev, "DMA channel allocation failed!\n"); - return -EBUSY; - } - - siu_stream->substream = ss; - - return 0; -} - -static int siu_pcm_close(struct snd_soc_component *component, - struct snd_pcm_substream *ss) -{ - struct siu_info *info = siu_i2s_data; - struct device *dev = ss->pcm->card->dev; - struct siu_port *port_info = siu_port_info(ss); - struct siu_stream *siu_stream; - - dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id); - - if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) - siu_stream = &port_info->playback; - else - siu_stream = &port_info->capture; - - dma_release_channel(siu_stream->chan); - siu_stream->chan = NULL; - - siu_stream->substream = NULL; - - return 0; -} - -static int siu_pcm_prepare(struct snd_soc_component *component, - struct snd_pcm_substream *ss) -{ - struct siu_info *info = siu_i2s_data; - struct siu_port *port_info = siu_port_info(ss); - struct device *dev = ss->pcm->card->dev; - struct snd_pcm_runtime *rt; - struct siu_stream *siu_stream; - snd_pcm_sframes_t xfer_cnt; - - if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) - siu_stream = &port_info->playback; - else - siu_stream = &port_info->capture; - - rt = siu_stream->substream->runtime; - - siu_stream->buf_bytes = snd_pcm_lib_buffer_bytes(ss); - siu_stream->period_bytes = snd_pcm_lib_period_bytes(ss); - - dev_dbg(dev, "%s: port=%d, %d channels, period=%u bytes\n", __func__, - info->port_id, rt->channels, siu_stream->period_bytes); - - /* We only support buffers that are multiples of the period */ - if (siu_stream->buf_bytes % siu_stream->period_bytes) { - dev_err(dev, "%s() - buffer=%d not multiple of period=%d\n", - __func__, siu_stream->buf_bytes, - siu_stream->period_bytes); - return -EINVAL; - } - - xfer_cnt = bytes_to_frames(rt, siu_stream->period_bytes); - if (!xfer_cnt || xfer_cnt > 0x1000000) - return -EINVAL; - - siu_stream->format = rt->format; - siu_stream->xfer_cnt = xfer_cnt; - - dev_dbg(dev, "port=%d buf=%lx buf_bytes=%d period_bytes=%d " - "format=%d channels=%d xfer_cnt=%d\n", info->port_id, - (unsigned long)rt->dma_addr, siu_stream->buf_bytes, - siu_stream->period_bytes, - siu_stream->format, rt->channels, (int)xfer_cnt); - - return 0; -} - -static int siu_pcm_trigger(struct snd_soc_component *component, - struct snd_pcm_substream *ss, int cmd) -{ - struct siu_info *info = siu_i2s_data; - struct device *dev = ss->pcm->card->dev; - struct siu_port *port_info = siu_port_info(ss); - int ret; - - dev_dbg(dev, "%s: port=%d@%p, cmd=%d\n", __func__, - info->port_id, port_info, cmd); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) - ret = siu_pcm_stmwrite_start(port_info); - else - ret = siu_pcm_stmread_start(port_info); - - if (ret < 0) - dev_warn(dev, "%s: start failed on port=%d\n", - __func__, info->port_id); - - break; - case SNDRV_PCM_TRIGGER_STOP: - if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) - siu_pcm_stmwrite_stop(port_info); - else - siu_pcm_stmread_stop(port_info); - ret = 0; - - break; - default: - dev_err(dev, "%s() unsupported cmd=%d\n", __func__, cmd); - ret = -EINVAL; - } - - return ret; -} - -/* - * So far only resolution of one period is supported, subject to extending the - * dmangine API - */ -static snd_pcm_uframes_t -siu_pcm_pointer_dma(struct snd_soc_component *component, - struct snd_pcm_substream *ss) -{ - struct device *dev = ss->pcm->card->dev; - struct siu_info *info = siu_i2s_data; - u32 __iomem *base = info->reg; - struct siu_port *port_info = siu_port_info(ss); - struct snd_pcm_runtime *rt = ss->runtime; - size_t ptr; - struct siu_stream *siu_stream; - - if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) - siu_stream = &port_info->playback; - else - siu_stream = &port_info->capture; - - /* - * ptr is the offset into the buffer where the dma is currently at. We - * check if the dma buffer has just wrapped. - */ - ptr = PERIOD_OFFSET(rt->dma_addr, - siu_stream->cur_period, - siu_stream->period_bytes) - rt->dma_addr; - - dev_dbg(dev, - "%s: port=%d, events %x, FSTS %x, xferred %u/%u, cookie %d\n", - __func__, info->port_id, siu_read32(base + SIU_EVNTC), - siu_read32(base + SIU_SBFSTS), ptr, siu_stream->buf_bytes, - siu_stream->cookie); - - if (ptr >= siu_stream->buf_bytes) - ptr = 0; - - return bytes_to_frames(ss->runtime, ptr); -} - -static int siu_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) -{ - /* card->dev == socdev->dev, see snd_soc_new_pcms() */ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - struct siu_info *info = siu_i2s_data; - struct platform_device *pdev = to_platform_device(card->dev); - int ret; - int i; - - /* pdev->id selects between SIUA and SIUB */ - if (pdev->id < 0 || pdev->id >= SIU_PORT_NUM) - return -EINVAL; - - info->port_id = pdev->id; - - /* - * While the siu has 2 ports, only one port can be on at a time (only 1 - * SPB). So far all the boards using the siu had only one of the ports - * wired to a codec. To simplify things, we only register one port with - * alsa. In case both ports are needed, it should be changed here - */ - for (i = pdev->id; i < pdev->id + 1; i++) { - struct siu_port **port_info = &siu_ports[i]; - - ret = siu_init_port(i, port_info, card); - if (ret < 0) - return ret; - - snd_pcm_set_managed_buffer_all(pcm, - SNDRV_DMA_TYPE_DEV, card->dev, - SIU_BUFFER_BYTES_MAX, SIU_BUFFER_BYTES_MAX); - - (*port_info)->pcm = pcm; - - /* IO works */ - INIT_WORK(&(*port_info)->playback.work, siu_io_work); - INIT_WORK(&(*port_info)->capture.work, siu_io_work); - } - - dev_info(card->dev, "SuperH SIU driver initialized.\n"); - return 0; -} - -static void siu_pcm_free(struct snd_soc_component *component, - struct snd_pcm *pcm) -{ - struct platform_device *pdev = to_platform_device(pcm->card->dev); - struct siu_port *port_info = siu_ports[pdev->id]; - - cancel_work_sync(&port_info->capture.work); - cancel_work_sync(&port_info->playback.work); - - siu_free_port(port_info); - - dev_dbg(pcm->card->dev, "%s\n", __func__); -} - -const struct snd_soc_component_driver siu_component = { - .name = DRV_NAME, - .open = siu_pcm_open, - .close = siu_pcm_close, - .prepare = siu_pcm_prepare, - .trigger = siu_pcm_trigger, - .pointer = siu_pcm_pointer_dma, - .pcm_construct = siu_pcm_new, - .pcm_destruct = siu_pcm_free, - .legacy_dai_naming = 1, -}; -EXPORT_SYMBOL_GPL(siu_component); diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c deleted file mode 100644 index 96cf523c2273..000000000000 --- a/sound/soc/sh/ssi.c +++ /dev/null @@ -1,403 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Serial Sound Interface (I2S) support for SH7760/SH7780 -// -// Copyright (c) 2007 Manuel Lauss -// -// dont forget to set IPSEL/OMSEL register bits (in your board code) to -// enable SSI output pins! - -/* - * LIMITATIONS: - * The SSI unit has only one physical data line, so full duplex is - * impossible. This can be remedied on the SH7760 by using the - * other SSI unit for recording; however the SH7780 has only 1 SSI - * unit, and its pins are shared with the AC97 unit, among others. - * - * FEATURES: - * The SSI features "compressed mode": in this mode it continuously - * streams PCM data over the I2S lines and uses LRCK as a handshake - * signal. Can be used to send compressed data (AC3/DTS) to a DSP. - * The number of bits sent over the wire in a frame can be adjusted - * and can be independent from the actual sample bit depth. This is - * useful to support TDM mode codecs like the AD1939 which have a - * fixed TDM slot size, regardless of sample resolution. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define SSICR 0x00 -#define SSISR 0x04 - -#define CR_DMAEN (1 << 28) -#define CR_CHNL_SHIFT 22 -#define CR_CHNL_MASK (3 << CR_CHNL_SHIFT) -#define CR_DWL_SHIFT 19 -#define CR_DWL_MASK (7 << CR_DWL_SHIFT) -#define CR_SWL_SHIFT 16 -#define CR_SWL_MASK (7 << CR_SWL_SHIFT) -#define CR_SCK_MASTER (1 << 15) /* bitclock master bit */ -#define CR_SWS_MASTER (1 << 14) /* wordselect master bit */ -#define CR_SCKP (1 << 13) /* I2Sclock polarity */ -#define CR_SWSP (1 << 12) /* LRCK polarity */ -#define CR_SPDP (1 << 11) -#define CR_SDTA (1 << 10) /* i2s alignment (msb/lsb) */ -#define CR_PDTA (1 << 9) /* fifo data alignment */ -#define CR_DEL (1 << 8) /* delay data by 1 i2sclk */ -#define CR_BREN (1 << 7) /* clock gating in burst mode */ -#define CR_CKDIV_SHIFT 4 -#define CR_CKDIV_MASK (7 << CR_CKDIV_SHIFT) /* bitclock divider */ -#define CR_MUTE (1 << 3) /* SSI mute */ -#define CR_CPEN (1 << 2) /* compressed mode */ -#define CR_TRMD (1 << 1) /* transmit/receive select */ -#define CR_EN (1 << 0) /* enable SSI */ - -#define SSIREG(reg) (*(unsigned long *)(ssi->mmio + (reg))) - -struct ssi_priv { - unsigned long mmio; - unsigned long sysclk; - int inuse; -} ssi_cpu_data[] = { -#if defined(CONFIG_CPU_SUBTYPE_SH7760) - { - .mmio = 0xFE680000, - }, - { - .mmio = 0xFE690000, - }, -#elif defined(CONFIG_CPU_SUBTYPE_SH7780) - { - .mmio = 0xFFE70000, - }, -#else -#error "Unsupported SuperH SoC" -#endif -}; - -/* - * track usage of the SSI; it is simplex-only so prevent attempts of - * concurrent playback + capture. FIXME: any locking required? - */ -static int ssi_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; - if (ssi->inuse) { - pr_debug("ssi: already in use!\n"); - return -EBUSY; - } else - ssi->inuse = 1; - return 0; -} - -static void ssi_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; - - ssi->inuse = 0; -} - -static int ssi_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - SSIREG(SSICR) |= CR_DMAEN | CR_EN; - break; - case SNDRV_PCM_TRIGGER_STOP: - SSIREG(SSICR) &= ~(CR_DMAEN | CR_EN); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int ssi_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; - unsigned long ssicr = SSIREG(SSICR); - unsigned int bits, channels, swl, recv, i; - - channels = params_channels(params); - bits = params->msbits; - recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1; - - pr_debug("ssi_hw_params() enter\nssicr was %08lx\n", ssicr); - pr_debug("bits: %u channels: %u\n", bits, channels); - - ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA | - CR_SWL_MASK); - - /* direction (send/receive) */ - if (!recv) - ssicr |= CR_TRMD; /* transmit */ - - /* channels */ - if ((channels < 2) || (channels > 8) || (channels & 1)) { - pr_debug("ssi: invalid number of channels\n"); - return -EINVAL; - } - ssicr |= ((channels >> 1) - 1) << CR_CHNL_SHIFT; - - /* DATA WORD LENGTH (DWL): databits in audio sample */ - i = 0; - switch (bits) { - case 32: ++i; - case 24: ++i; - case 22: ++i; - case 20: ++i; - case 18: ++i; - case 16: ++i; - ssicr |= i << CR_DWL_SHIFT; - case 8: break; - default: - pr_debug("ssi: invalid sample width\n"); - return -EINVAL; - } - - /* - * SYSTEM WORD LENGTH: size in bits of half a frame over the I2S - * wires. This is usually bits_per_sample x channels/2; i.e. in - * Stereo mode the SWL equals DWL. SWL can be bigger than the - * product of (channels_per_slot x samplebits), e.g. for codecs - * like the AD1939 which only accept 32bit wide TDM slots. For - * "standard" I2S operation we set SWL = chans / 2 * DWL here. - * Waiting for ASoC to get TDM support ;-) - */ - if ((bits > 16) && (bits <= 24)) { - bits = 24; /* these are padded by the SSI */ - /*ssicr |= CR_PDTA;*/ /* cpu/data endianness ? */ - } - i = 0; - swl = (bits * channels) / 2; - switch (swl) { - case 256: ++i; - case 128: ++i; - case 64: ++i; - case 48: ++i; - case 32: ++i; - case 16: ++i; - ssicr |= i << CR_SWL_SHIFT; - case 8: break; - default: - pr_debug("ssi: invalid system word length computed\n"); - return -EINVAL; - } - - SSIREG(SSICR) = ssicr; - - pr_debug("ssi_hw_params() leave\nssicr is now %08lx\n", ssicr); - return 0; -} - -static int ssi_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, - unsigned int freq, int dir) -{ - struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id]; - - ssi->sysclk = freq; - - return 0; -} - -/* - * This divider is used to generate the SSI_SCK (I2S bitclock) from the - * clock at the HAC_BIT_CLK ("oversampling clock") pin. - */ -static int ssi_set_clkdiv(struct snd_soc_dai *dai, int did, int div) -{ - struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; - unsigned long ssicr; - int i; - - i = 0; - ssicr = SSIREG(SSICR) & ~CR_CKDIV_MASK; - switch (div) { - case 16: ++i; - case 8: ++i; - case 4: ++i; - case 2: ++i; - SSIREG(SSICR) = ssicr | (i << CR_CKDIV_SHIFT); - case 1: break; - default: - pr_debug("ssi: invalid sck divider %d\n", div); - return -EINVAL; - } - - return 0; -} - -static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -{ - struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; - unsigned long ssicr = SSIREG(SSICR); - - pr_debug("ssi_set_fmt()\nssicr was 0x%08lx\n", ssicr); - - ssicr &= ~(CR_DEL | CR_PDTA | CR_BREN | CR_SWSP | CR_SCKP | - CR_SWS_MASTER | CR_SCK_MASTER); - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - break; - case SND_SOC_DAIFMT_RIGHT_J: - ssicr |= CR_DEL | CR_PDTA; - break; - case SND_SOC_DAIFMT_LEFT_J: - ssicr |= CR_DEL; - break; - default: - pr_debug("ssi: unsupported format\n"); - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { - case SND_SOC_DAIFMT_CONT: - break; - case SND_SOC_DAIFMT_GATED: - ssicr |= CR_BREN; - break; - } - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - ssicr |= CR_SCKP; /* sample data at low clkedge */ - break; - case SND_SOC_DAIFMT_NB_IF: - ssicr |= CR_SCKP | CR_SWSP; - break; - case SND_SOC_DAIFMT_IB_NF: - break; - case SND_SOC_DAIFMT_IB_IF: - ssicr |= CR_SWSP; /* word select starts low */ - break; - default: - pr_debug("ssi: invalid inversion\n"); - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { - case SND_SOC_DAIFMT_BC_FC: - break; - case SND_SOC_DAIFMT_BP_FC: - ssicr |= CR_SCK_MASTER; - break; - case SND_SOC_DAIFMT_BC_FP: - ssicr |= CR_SWS_MASTER; - break; - case SND_SOC_DAIFMT_BP_FP: - ssicr |= CR_SWS_MASTER | CR_SCK_MASTER; - break; - default: - pr_debug("ssi: invalid master/secondary configuration\n"); - return -EINVAL; - } - - SSIREG(SSICR) = ssicr; - pr_debug("ssi_set_fmt() leave\nssicr is now 0x%08lx\n", ssicr); - - return 0; -} - -/* the SSI depends on an external clocksource (at HAC_BIT_CLK) even in - * Master mode, so really this is board specific; the SSI can do any - * rate with the right bitclk and divider settings. - */ -#define SSI_RATES \ - SNDRV_PCM_RATE_8000_192000 - -/* the SSI can do 8-32 bit samples, with 8 possible channels */ -#define SSI_FMTS \ - (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \ - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) - -static const struct snd_soc_dai_ops ssi_dai_ops = { - .startup = ssi_startup, - .shutdown = ssi_shutdown, - .trigger = ssi_trigger, - .hw_params = ssi_hw_params, - .set_sysclk = ssi_set_sysclk, - .set_clkdiv = ssi_set_clkdiv, - .set_fmt = ssi_set_fmt, -}; - -static struct snd_soc_dai_driver sh4_ssi_dai[] = { -{ - .name = "ssi-dai.0", - .playback = { - .rates = SSI_RATES, - .formats = SSI_FMTS, - .channels_min = 2, - .channels_max = 8, - }, - .capture = { - .rates = SSI_RATES, - .formats = SSI_FMTS, - .channels_min = 2, - .channels_max = 8, - }, - .ops = &ssi_dai_ops, -}, -#ifdef CONFIG_CPU_SUBTYPE_SH7760 -{ - .name = "ssi-dai.1", - .playback = { - .rates = SSI_RATES, - .formats = SSI_FMTS, - .channels_min = 2, - .channels_max = 8, - }, - .capture = { - .rates = SSI_RATES, - .formats = SSI_FMTS, - .channels_min = 2, - .channels_max = 8, - }, - .ops = &ssi_dai_ops, -}, -#endif -}; - -static const struct snd_soc_component_driver sh4_ssi_component = { - .name = "sh4-ssi", - .legacy_dai_naming = 1, -}; - -static int sh4_soc_dai_probe(struct platform_device *pdev) -{ - return devm_snd_soc_register_component(&pdev->dev, &sh4_ssi_component, - sh4_ssi_dai, - ARRAY_SIZE(sh4_ssi_dai)); -} - -static struct platform_driver sh4_ssi_driver = { - .driver = { - .name = "sh4-ssi-dai", - }, - - .probe = sh4_soc_dai_probe, -}; - -module_platform_driver(sh4_ssi_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver"); -MODULE_AUTHOR("Manuel Lauss "); -- cgit v1.2.3