// SPDX-License-Identifier: GPL-2.0 // // Copyright (c) 2020 BayLibre, SAS. // Author: Jerome Brunet #include #include #include #include #include "aiu.h" #include "aiu-fifo.h" #define AIU_IEC958_DCU_FF_CTRL_EN BIT(0) #define AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE BIT(1) #define AIU_IEC958_DCU_FF_CTRL_IRQ_MODE GENMASK(3, 2) #define AIU_IEC958_DCU_FF_CTRL_IRQ_OUT_THD BIT(2) #define AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ BIT(3) #define AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN BIT(4) #define AIU_IEC958_DCU_FF_CTRL_BYTE_SEEK BIT(5) #define AIU_IEC958_DCU_FF_CTRL_CONTINUE BIT(6) #define AIU_MEM_IEC958_CONTROL_ENDIAN GENMASK(5, 3) #define AIU_MEM_IEC958_CONTROL_RD_DDR BIT(6) #define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7) #define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8) #define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0) #define AIU_FIFO_SPDIF_BLOCK 8 static struct snd_pcm_hardware fifo_spdif_pcm = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), .formats = AIU_FORMATS, .rate_min = 5512, .rate_max = 192000, .channels_min = 2, .channels_max = 2, .period_bytes_min = AIU_FIFO_SPDIF_BLOCK, .period_bytes_max = AIU_FIFO_SPDIF_BLOCK * USHRT_MAX, .periods_min = 2, .periods_max = UINT_MAX, /* No real justification for this */ .buffer_bytes_max = 1 * 1024 * 1024, }; static void fifo_spdif_dcu_enable(struct snd_soc_component *component, bool enable) { snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL, AIU_IEC958_DCU_FF_CTRL_EN, enable ? AIU_IEC958_DCU_FF_CTRL_EN : 0); } static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; int ret; ret = aiu_fifo_trigger(substream, cmd, dai); if (ret) return ret; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: fifo_spdif_dcu_enable(component, true); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: fifo_spdif_dcu_enable(component, false); break; default: return -EINVAL; } return 0; } static int fifo_spdif_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; int ret; ret = aiu_fifo_prepare(substream, dai); if (ret) return ret; snd_soc_component_update_bits(component, AIU_MEM_IEC958_BUF_CNTL, AIU_MEM_IEC958_BUF_CNTL_INIT, AIU_MEM_IEC958_BUF_CNTL_INIT); snd_soc_component_update_bits(component, AIU_MEM_IEC958_BUF_CNTL, AIU_MEM_IEC958_BUF_CNTL_INIT, 0); return 0; } static int fifo_spdif_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; unsigned int val; int ret; ret = aiu_fifo_hw_params(substream, params, dai); if (ret) return ret; val = AIU_MEM_IEC958_CONTROL_RD_DDR | AIU_MEM_IEC958_CONTROL_MODE_LINEAR; switch (params_physical_width(params)) { case 16: val |= AIU_MEM_IEC958_CONTROL_MODE_16BIT; break; case 32: break; default: dev_err(dai->dev, "Unsupported physical width %u\n", params_physical_width(params)); return -EINVAL; } snd_soc_component_update_bits(component, AIU_MEM_IEC958_CONTROL, AIU_MEM_IEC958_CONTROL_ENDIAN | AIU_MEM_IEC958_CONTROL_RD_DDR | AIU_MEM_IEC958_CONTROL_MODE_LINEAR | AIU_MEM_IEC958_CONTROL_MODE_16BIT, val); /* Number bytes read by the FIFO between each IRQ */ snd_soc_component_write(component, AIU_IEC958_BPF, params_period_bytes(params)); /* * AUTO_DISABLE and SYNC_HEAD are enabled by default but * this should be disabled in PCM (uncompressed) mode */ snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL, AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE | AIU_IEC958_DCU_FF_CTRL_IRQ_MODE | AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN, AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ); return 0; } const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = { .pcm_new = aiu_fifo_pcm_new, .probe = aiu_fifo_spdif_dai_probe, .remove = aiu_fifo_dai_remove, .trigger = fifo_spdif_trigger, .prepare = fifo_spdif_prepare, .hw_params = fifo_spdif_hw_params, .startup = aiu_fifo_startup, .shutdown = aiu_fifo_shutdown, }; int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct aiu *aiu = snd_soc_component_get_drvdata(component); struct aiu_fifo *fifo; int ret; ret = aiu_fifo_dai_probe(dai); if (ret) return ret; fifo = snd_soc_dai_dma_data_get_playback(dai); fifo->pcm = &fifo_spdif_pcm; fifo->mem_offset = AIU_MEM_IEC958_START; fifo->fifo_block = 1; fifo->pclk = aiu->spdif.clks[PCLK].clk; fifo->irq = aiu->spdif.irq; return 0; }