1// SPDX-License-Identifier: GPL-2.0
2//
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 8
29
30static 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 val: 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, enable: 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, enable: 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, val: 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(p: 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 val: params_period_bytes(p: params));
143
144 /*
145 * AUTO_DISABLE and SYNC_HEAD are enabled by default but
146 * this should be disabled in PCM (uncompressed) mode
147 */
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(c: 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

source code of linux/sound/soc/meson/aiu-fifo-spdif.c