189 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Copyright (c) 2020 BayLibre, SAS.4// Author: Jerome Brunet <jbrunet@baylibre.com>5 6#include <linux/clk.h>7#include <sound/pcm_params.h>8#include <sound/soc.h>9#include <sound/soc-dai.h>10 11#include "aiu.h"12#include "aiu-fifo.h"13 14#define AIU_IEC958_DCU_FF_CTRL_EN BIT(0)15#define AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE BIT(1)16#define AIU_IEC958_DCU_FF_CTRL_IRQ_MODE GENMASK(3, 2)17#define AIU_IEC958_DCU_FF_CTRL_IRQ_OUT_THD BIT(2)18#define AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ BIT(3)19#define AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN BIT(4)20#define AIU_IEC958_DCU_FF_CTRL_BYTE_SEEK BIT(5)21#define AIU_IEC958_DCU_FF_CTRL_CONTINUE BIT(6)22#define AIU_MEM_IEC958_CONTROL_ENDIAN GENMASK(5, 3)23#define AIU_MEM_IEC958_CONTROL_RD_DDR BIT(6)24#define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7)25#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8)26#define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0)27 28#define AIU_FIFO_SPDIF_BLOCK 829 30static const struct snd_pcm_hardware fifo_spdif_pcm = {31 .info = (SNDRV_PCM_INFO_INTERLEAVED |32 SNDRV_PCM_INFO_MMAP |33 SNDRV_PCM_INFO_MMAP_VALID |34 SNDRV_PCM_INFO_PAUSE),35 .formats = AIU_FORMATS,36 .rate_min = 5512,37 .rate_max = 192000,38 .channels_min = 2,39 .channels_max = 2,40 .period_bytes_min = AIU_FIFO_SPDIF_BLOCK,41 .period_bytes_max = AIU_FIFO_SPDIF_BLOCK * USHRT_MAX,42 .periods_min = 2,43 .periods_max = UINT_MAX,44 45 /* No real justification for this */46 .buffer_bytes_max = 1 * 1024 * 1024,47};48 49static void fifo_spdif_dcu_enable(struct snd_soc_component *component,50 bool enable)51{52 snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL,53 AIU_IEC958_DCU_FF_CTRL_EN,54 enable ? AIU_IEC958_DCU_FF_CTRL_EN : 0);55}56 57static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd,58 struct snd_soc_dai *dai)59{60 struct snd_soc_component *component = dai->component;61 int ret;62 63 ret = aiu_fifo_trigger(substream, cmd, dai);64 if (ret)65 return ret;66 67 switch (cmd) {68 case SNDRV_PCM_TRIGGER_START:69 case SNDRV_PCM_TRIGGER_RESUME:70 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:71 fifo_spdif_dcu_enable(component, true);72 break;73 case SNDRV_PCM_TRIGGER_SUSPEND:74 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:75 case SNDRV_PCM_TRIGGER_STOP:76 fifo_spdif_dcu_enable(component, false);77 break;78 default:79 return -EINVAL;80 }81 82 return 0;83}84 85static int fifo_spdif_prepare(struct snd_pcm_substream *substream,86 struct snd_soc_dai *dai)87{88 struct snd_soc_component *component = dai->component;89 int ret;90 91 ret = aiu_fifo_prepare(substream, dai);92 if (ret)93 return ret;94 95 snd_soc_component_update_bits(component,96 AIU_MEM_IEC958_BUF_CNTL,97 AIU_MEM_IEC958_BUF_CNTL_INIT,98 AIU_MEM_IEC958_BUF_CNTL_INIT);99 snd_soc_component_update_bits(component,100 AIU_MEM_IEC958_BUF_CNTL,101 AIU_MEM_IEC958_BUF_CNTL_INIT, 0);102 103 return 0;104}105 106static int fifo_spdif_hw_params(struct snd_pcm_substream *substream,107 struct snd_pcm_hw_params *params,108 struct snd_soc_dai *dai)109{110 struct snd_soc_component *component = dai->component;111 unsigned int val;112 int ret;113 114 ret = aiu_fifo_hw_params(substream, params, dai);115 if (ret)116 return ret;117 118 val = AIU_MEM_IEC958_CONTROL_RD_DDR |119 AIU_MEM_IEC958_CONTROL_MODE_LINEAR;120 121 switch (params_physical_width(params)) {122 case 16:123 val |= AIU_MEM_IEC958_CONTROL_MODE_16BIT;124 break;125 case 32:126 break;127 default:128 dev_err(dai->dev, "Unsupported physical width %u\n",129 params_physical_width(params));130 return -EINVAL;131 }132 133 snd_soc_component_update_bits(component, AIU_MEM_IEC958_CONTROL,134 AIU_MEM_IEC958_CONTROL_ENDIAN |135 AIU_MEM_IEC958_CONTROL_RD_DDR |136 AIU_MEM_IEC958_CONTROL_MODE_LINEAR |137 AIU_MEM_IEC958_CONTROL_MODE_16BIT,138 val);139 140 /* Number bytes read by the FIFO between each IRQ */141 snd_soc_component_write(component, AIU_IEC958_BPF,142 params_period_bytes(params));143 144 /*145 * AUTO_DISABLE and SYNC_HEAD are enabled by default but146 * this should be disabled in PCM (uncompressed) mode147 */148 snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL,149 AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE |150 AIU_IEC958_DCU_FF_CTRL_IRQ_MODE |151 AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN,152 AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ);153 154 return 0;155}156 157const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = {158 .pcm_new = aiu_fifo_pcm_new,159 .probe = aiu_fifo_spdif_dai_probe,160 .remove = aiu_fifo_dai_remove,161 .trigger = fifo_spdif_trigger,162 .prepare = fifo_spdif_prepare,163 .hw_params = fifo_spdif_hw_params,164 .startup = aiu_fifo_startup,165 .shutdown = aiu_fifo_shutdown,166};167 168int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai)169{170 struct snd_soc_component *component = dai->component;171 struct aiu *aiu = snd_soc_component_get_drvdata(component);172 struct aiu_fifo *fifo;173 int ret;174 175 ret = aiu_fifo_dai_probe(dai);176 if (ret)177 return ret;178 179 fifo = snd_soc_dai_dma_data_get_playback(dai);180 181 fifo->pcm = &fifo_spdif_pcm;182 fifo->mem_offset = AIU_MEM_IEC958_START;183 fifo->fifo_block = 1;184 fifo->pclk = aiu->spdif.clks[PCLK].clk;185 fifo->irq = aiu->spdif.irq;186 187 return 0;188}189