xref: /linux/sound/soc/meson/aiu-fifo-spdif.c (revision 33e02dc69afbd8f1b85a51d74d72f139ba4ca623)
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 
30 static 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 
fifo_spdif_dcu_enable(struct snd_soc_component * component,bool enable)49 static 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 
fifo_spdif_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)57 static 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 
fifo_spdif_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)85 static 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 
fifo_spdif_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)106 static 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 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 
157 const 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 
aiu_fifo_spdif_dai_probe(struct snd_soc_dai * dai)168 int 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