1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2020 Intel Corporation. All rights reserved.
4 //
5 // Author: Tomasz Lauda <tomasz.lauda@linux.intel.com>
6 
7 #include <sof/drivers/dmic.h>
8 #include <sof/drivers/ssp.h>
9 #include <sof/drivers/timestamp.h>
10 #include <rtos/clk.h>
11 #include <sof/lib/dai.h>
12 #include <sof/lib/io.h>
13 #include <ipc/dai.h>
14 #include <ipc/stream.h>
15 
16 #include <errno.h>
17 #include <stdint.h>
18 
19 LOG_MODULE_REGISTER(dai_ts, CONFIG_SOF_LOG_LEVEL);
20 
timestamp_hda_config(struct dai * dai,struct timestamp_cfg * cfg)21 int timestamp_hda_config(struct dai *dai, struct timestamp_cfg *cfg)
22 {
23 	int i;
24 
25 	if (cfg->type != SOF_DAI_INTEL_HDA) {
26 		dai_err(dai, "dmic_ts_config(): Illegal DAI type");
27 		return -EINVAL;
28 	}
29 
30 	cfg->walclk_rate = 0;
31 	for (i = 0; i < NUM_SSP_FREQ; i++) {
32 		if (ssp_freq_sources[i] == SSP_CLOCK_XTAL_OSCILLATOR)
33 			cfg->walclk_rate = ssp_freq[i].freq;
34 	}
35 
36 	return 0;
37 }
38 
timestamp_hda_start(struct dai * dai,struct timestamp_cfg * cfg)39 int timestamp_hda_start(struct dai *dai, struct timestamp_cfg *cfg)
40 {
41 	/* Set HDA timestamp registers */
42 	uint32_t addr = TIMESTAMP_BASE + TS_HDA_LOCAL_TSCTRL;
43 	uint32_t cdmas;
44 
45 	/* Set HDA timestamp registers */
46 
47 	/* Set CDMAS(4:0) to match DMA engine index and direction
48 	 * also clear NTK to be sure there is no old timestamp.
49 	 */
50 	cdmas = TS_LOCAL_TSCTRL_CDMAS(cfg->dma_chan_index |
51 		(cfg->direction == SOF_IPC_STREAM_PLAYBACK ? BIT(4) : 0));
52 	io_reg_write(addr, TS_LOCAL_TSCTRL_NTK_BIT | cdmas);
53 
54 	/* Request on demand timestamp */
55 	io_reg_write(addr, TS_LOCAL_TSCTRL_ODTS_BIT | cdmas);
56 
57 	return 0;
58 }
59 
timestamp_hda_stop(struct dai * dai,struct timestamp_cfg * cfg)60 int timestamp_hda_stop(struct dai *dai, struct timestamp_cfg *cfg)
61 {
62 	/* Clear NTK and write zero to CDMAS */
63 	io_reg_write(TIMESTAMP_BASE + TS_HDA_LOCAL_TSCTRL,
64 		     TS_LOCAL_TSCTRL_NTK_BIT);
65 	return 0;
66 }
67 
timestamp_hda_get(struct dai * dai,struct timestamp_cfg * cfg,struct timestamp_data * tsd)68 int timestamp_hda_get(struct dai *dai, struct timestamp_cfg *cfg,
69 		      struct timestamp_data *tsd)
70 {
71 	/* Read HDA timestamp registers */
72 	uint32_t tsctrl = TIMESTAMP_BASE + TS_HDA_LOCAL_TSCTRL;
73 	uint32_t ntk;
74 
75 	ntk = io_reg_read(tsctrl) & TS_LOCAL_TSCTRL_NTK_BIT;
76 	if (!ntk)
77 		goto out;
78 
79 	/* NTK was set, get wall clock */
80 	tsd->walclk = io_reg_read64(TIMESTAMP_BASE + TS_HDA_LOCAL_WALCLK);
81 
82 	/* Sample */
83 	tsd->sample = io_reg_read64(TIMESTAMP_BASE + TS_HDA_LOCAL_SAMPLE);
84 
85 	/* Clear NTK to enable successive timestamps */
86 	io_reg_write(tsctrl, TS_LOCAL_TSCTRL_NTK_BIT);
87 
88 out:
89 	tsd->walclk_rate = cfg->walclk_rate;
90 	if (!ntk)
91 		return -ENODATA;
92 
93 	return 0;
94 }
95 
96 #if CONFIG_INTEL_DMIC
97 
timestamp_dmic_config(struct dai * dai,struct timestamp_cfg * cfg)98 int timestamp_dmic_config(struct dai *dai, struct timestamp_cfg *cfg)
99 {
100 	if (cfg->type != SOF_DAI_INTEL_DMIC) {
101 		dai_err(dai, "dmic_ts_config(): Illegal DAI type");
102 		return -EINVAL;
103 	}
104 
105 	cfg->walclk_rate = CONFIG_DMIC_HW_IOCLK;
106 
107 	return 0;
108 }
109 
timestamp_dmic_start(struct dai * dai,struct timestamp_cfg * cfg)110 int timestamp_dmic_start(struct dai *dai, struct timestamp_cfg *cfg)
111 {
112 	uint32_t addr = TIMESTAMP_BASE + TS_DMIC_LOCAL_TSCTRL;
113 	uint32_t cdmas;
114 
115 	/* Set DMIC timestamp registers */
116 
117 	/* First point CDMAS to GPDMA channel that is used by DMIC
118 	 * also clear NTK to be sure there is no old timestamp.
119 	 */
120 	cdmas = TS_LOCAL_TSCTRL_CDMAS(cfg->dma_chan_index +
121 		cfg->dma_chan_count * cfg->dma_id);
122 	io_reg_write(addr, TS_LOCAL_TSCTRL_NTK_BIT | cdmas);
123 
124 	/* Request on demand timestamp */
125 	io_reg_write(addr, TS_LOCAL_TSCTRL_ODTS_BIT | cdmas);
126 
127 	return 0;
128 }
129 
timestamp_dmic_stop(struct dai * dai,struct timestamp_cfg * cfg)130 int timestamp_dmic_stop(struct dai *dai, struct timestamp_cfg *cfg)
131 {
132 	/* Clear NTK and write zero to CDMAS */
133 	io_reg_write(TIMESTAMP_BASE + TS_DMIC_LOCAL_TSCTRL,
134 		     TS_LOCAL_TSCTRL_NTK_BIT);
135 	return 0;
136 }
137 
timestamp_dmic_get(struct dai * dai,struct timestamp_cfg * cfg,struct timestamp_data * tsd)138 int timestamp_dmic_get(struct dai *dai, struct timestamp_cfg *cfg,
139 		       struct timestamp_data *tsd)
140 {
141 	/* Read DMIC timestamp registers */
142 	uint32_t tsctrl = TIMESTAMP_BASE + TS_DMIC_LOCAL_TSCTRL;
143 	uint32_t ntk;
144 
145 	/* Read SSP timestamp registers */
146 	ntk = io_reg_read(tsctrl) & TS_LOCAL_TSCTRL_NTK_BIT;
147 	if (!ntk)
148 		goto out;
149 
150 	/* NTK was set, get wall clock */
151 	tsd->walclk = io_reg_read64(TIMESTAMP_BASE + TS_DMIC_LOCAL_WALCLK);
152 
153 	/* Sample */
154 	tsd->sample = io_reg_read64(TIMESTAMP_BASE + TS_DMIC_LOCAL_SAMPLE);
155 
156 	/* Clear NTK to enable successive timestamps */
157 	io_reg_write(tsctrl, TS_LOCAL_TSCTRL_NTK_BIT);
158 
159 out:
160 	tsd->walclk_rate = cfg->walclk_rate;
161 	if (!ntk)
162 		return -ENODATA;
163 
164 	return 0;
165 }
166 
167 #endif /* CONFIG_INTEL_DMIC */
168 
169 #if CONFIG_INTEL_SSP
170 
ssp_ts_local_tsctrl_addr(int index)171 static uint32_t ssp_ts_local_tsctrl_addr(int index)
172 {
173 	return TIMESTAMP_BASE + TS_I2S_LOCAL_TSCTRL(index);
174 }
175 
ssp_ts_local_sample_addr(int index)176 static uint32_t ssp_ts_local_sample_addr(int index)
177 {
178 	return TIMESTAMP_BASE + TS_I2S_LOCAL_SAMPLE(index);
179 }
180 
ssp_ts_local_walclk_addr(int index)181 static uint32_t ssp_ts_local_walclk_addr(int index)
182 {
183 	return TIMESTAMP_BASE + TS_I2S_LOCAL_WALCLK(index);
184 }
185 
timestamp_ssp_config(struct dai * dai,struct timestamp_cfg * cfg)186 int timestamp_ssp_config(struct dai *dai, struct timestamp_cfg *cfg)
187 {
188 	int i;
189 
190 	if (cfg->type != SOF_DAI_INTEL_SSP) {
191 		dai_err(dai, "ssp_ts_config(): Illegal DAI type");
192 		return -EINVAL;
193 	}
194 
195 	if (cfg->index > DAI_NUM_SSP_BASE + DAI_NUM_SSP_EXT - 1) {
196 		dai_err(dai, "ssp_ts_config(): Illegal DAI index");
197 		return -EINVAL;
198 	}
199 
200 	cfg->walclk_rate = 0;
201 	for (i = 0; i < NUM_SSP_FREQ; i++) {
202 		if (ssp_freq_sources[i] == SSP_CLOCK_XTAL_OSCILLATOR)
203 			cfg->walclk_rate = ssp_freq[i].freq;
204 	}
205 
206 	if (!cfg->walclk_rate) {
207 		dai_err(dai, "ssp_ts_config(): No XTAL frequency defined");
208 		return -EINVAL;
209 	}
210 
211 	return 0;
212 }
213 
timestamp_ssp_start(struct dai * dai,struct timestamp_cfg * cfg)214 int timestamp_ssp_start(struct dai *dai, struct timestamp_cfg *cfg)
215 {
216 	uint32_t cdmas;
217 	uint32_t addr = ssp_ts_local_tsctrl_addr(cfg->index);
218 
219 	/* Set SSP timestamp registers */
220 
221 	/* First point CDMAS to GPDMA channel that is used by this SSP,
222 	 * also clear NTK to be sure there is no old timestamp.
223 	 */
224 	cdmas = TS_LOCAL_TSCTRL_CDMAS(cfg->dma_chan_index +
225 		cfg->dma_chan_count * cfg->dma_id);
226 	io_reg_write(addr, TS_LOCAL_TSCTRL_NTK_BIT | cdmas);
227 
228 	/* Request on demand timestamp */
229 	io_reg_write(addr, TS_LOCAL_TSCTRL_ODTS_BIT | cdmas);
230 
231 	return 0;
232 }
233 
timestamp_ssp_stop(struct dai * dai,struct timestamp_cfg * cfg)234 int timestamp_ssp_stop(struct dai *dai, struct timestamp_cfg *cfg)
235 {
236 	/* Clear NTK and write zero to CDMAS */
237 	io_reg_write(ssp_ts_local_tsctrl_addr(cfg->index),
238 		     TS_LOCAL_TSCTRL_NTK_BIT);
239 	return 0;
240 }
241 
timestamp_ssp_get(struct dai * dai,struct timestamp_cfg * cfg,struct timestamp_data * tsd)242 int timestamp_ssp_get(struct dai *dai, struct timestamp_cfg *cfg,
243 		      struct timestamp_data *tsd)
244 {
245 	uint32_t ntk;
246 	uint32_t tsctrl = ssp_ts_local_tsctrl_addr(cfg->index);
247 
248 	/* Read SSP timestamp registers */
249 	ntk = io_reg_read(tsctrl) & TS_LOCAL_TSCTRL_NTK_BIT;
250 	if (!ntk)
251 		goto out;
252 
253 	/* NTK was set, get wall clock */
254 	tsd->walclk = io_reg_read64(ssp_ts_local_walclk_addr(cfg->index));
255 
256 	/* Sample */
257 	tsd->sample = io_reg_read64(ssp_ts_local_sample_addr(cfg->index));
258 
259 	/* Clear NTK to enable successive timestamps */
260 	io_reg_write(tsctrl, TS_LOCAL_TSCTRL_NTK_BIT);
261 
262 out:
263 	tsd->walclk_rate = cfg->walclk_rate;
264 	if (!ntk)
265 		return -ENODATA;
266 
267 	return 0;
268 }
269 
270 #endif /* CONFIG_INTEL_SSP */
271