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