1 /*
2 * Copyright 2023 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/dai.h>
8 #include <zephyr/device.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/pm/device_runtime.h>
11 #include <zephyr/pm/device.h>
12
13 #include "sai.h"
14
15 /* used for binding the driver */
16 #define DT_DRV_COMPAT nxp_dai_sai
17
18 #define SAI_TX_RX_HW_DISABLE_TIMEOUT 50
19
20 /* TODO list:
21 *
22 * 1) No busy waiting should be performed in any of the operations.
23 * In the case of STOP(), the operation should be split into TRIGGER_STOP
24 * and TRIGGER_POST_STOP. (SOF)
25 *
26 * 2) The SAI ISR should stop the SAI whenever a FIFO error interrupt
27 * is raised.
28 *
29 * 3) Transmitter/receiver may remain enabled after sai_tx_rx_disable().
30 * Fix this.
31 */
32
33 #ifdef CONFIG_SAI_HAS_MCLK_CONFIG_OPTION
34 /* note: i.MX8 boards don't seem to support the MICS field in the MCR
35 * register. As such, the MCLK source field of sai_master_clock_t is
36 * useless. I'm assuming the source is selected through xCR2's MSEL.
37 *
38 * TODO: for now, this function will set MCR's MSEL to the same value
39 * as xCR2's MSEL or, rather, to the same MCLK as the one used for
40 * generating BCLK. Is there a need to support different MCLKs in
41 * xCR2 and MCR?
42 */
sai_mclk_config(const struct device * dev,sai_bclk_source_t bclk_source,const struct sai_bespoke_config * bespoke)43 static int sai_mclk_config(const struct device *dev,
44 sai_bclk_source_t bclk_source,
45 const struct sai_bespoke_config *bespoke)
46 {
47 const struct sai_config *cfg;
48 struct sai_data *data;
49 sai_master_clock_t mclk_config;
50 uint32_t msel, mclk_rate;
51 int ret;
52
53 cfg = dev->config;
54 data = dev->data;
55
56 mclk_config.mclkOutputEnable = cfg->mclk_is_output;
57
58 ret = get_msel(bclk_source, &msel);
59 if (ret < 0) {
60 LOG_ERR("invalid MCLK source %d for MSEL", bclk_source);
61 return ret;
62 }
63
64 /* get MCLK's rate */
65 ret = get_mclk_rate(&cfg->clk_data, bclk_source, &mclk_rate);
66 if (ret < 0) {
67 LOG_ERR("failed to query MCLK's rate");
68 return ret;
69 }
70
71 LOG_DBG("source MCLK is %u", mclk_rate);
72
73 LOG_DBG("target MCLK is %u", bespoke->mclk_rate);
74
75 /* source MCLK rate */
76 mclk_config.mclkSourceClkHz = mclk_rate;
77
78 /* target MCLK rate */
79 mclk_config.mclkHz = bespoke->mclk_rate;
80
81 /* commit configuration */
82 SAI_SetMasterClockConfig(UINT_TO_I2S(data->regmap), &mclk_config);
83
84 set_msel(data->regmap, msel);
85
86 return 0;
87 }
88 #endif /* CONFIG_SAI_HAS_MCLK_CONFIG_OPTION */
89
sai_isr(const void * parameter)90 void sai_isr(const void *parameter)
91 {
92 const struct device *dev;
93 struct sai_data *data;
94
95 dev = parameter;
96 data = dev->data;
97
98 /* check for TX FIFO error */
99 if (SAI_TX_RX_STATUS_IS_SET(DAI_DIR_TX, data->regmap, kSAI_FIFOErrorFlag)) {
100 LOG_WRN("FIFO underrun detected");
101 SAI_TX_RX_STATUS_CLEAR(DAI_DIR_TX, data->regmap, kSAI_FIFOErrorFlag);
102 }
103
104 /* check for RX FIFO error */
105 if (SAI_TX_RX_STATUS_IS_SET(DAI_DIR_RX, data->regmap, kSAI_FIFOErrorFlag)) {
106 LOG_WRN("FIFO overrun detected");
107 SAI_TX_RX_STATUS_CLEAR(DAI_DIR_RX, data->regmap, kSAI_FIFOErrorFlag);
108 }
109 }
110
sai_config_get(const struct device * dev,struct dai_config * cfg,enum dai_dir dir)111 static int sai_config_get(const struct device *dev,
112 struct dai_config *cfg,
113 enum dai_dir dir)
114 {
115 struct sai_data *data = dev->data;
116
117 /* dump content of the DAI configuration */
118 memcpy(cfg, &data->cfg, sizeof(*cfg));
119
120 return 0;
121 }
122
123 static const struct dai_properties
sai_get_properties(const struct device * dev,enum dai_dir dir,int stream_id)124 *sai_get_properties(const struct device *dev, enum dai_dir dir, int stream_id)
125 {
126 const struct sai_config *cfg = dev->config;
127
128 switch (dir) {
129 case DAI_DIR_RX:
130 return cfg->rx_props;
131 case DAI_DIR_TX:
132 return cfg->tx_props;
133 default:
134 LOG_ERR("invalid direction: %d", dir);
135 return NULL;
136 }
137
138 CODE_UNREACHABLE;
139 }
140
141 #ifdef CONFIG_SAI_IMX93_ERRATA_051421
142 /* notes:
143 * 1) TX and RX operate in the same mode: master/slave. As such,
144 * there's no need to check the mode for both directions.
145 *
146 * 2) Only one of the directions can operate in SYNC mode at a
147 * time.
148 *
149 * 3) What this piece of code does is it makes the SYNC direction
150 * use the ASYNC direction's BCLK that comes from its input pad.
151 * Logically speaking, this would look like:
152 *
153 * +--------+ +--------+
154 * | TX | | RX |
155 * | module | | module |
156 * +--------+ +--------+
157 * | ^ |
158 * | | |
159 * TX_BCLK | |____________| RX_BCLK
160 * | |
161 * V V
162 * +---------+ +---------+
163 * | TX BCLK | | RX BCLK |
164 * | pad | | pad |
165 * +---------+ +---------+
166 * | |
167 * | TX_BCLK | RX_BCLK
168 * V V
169 *
170 * Without BCI enabled, the TX module would use an RX_BCLK
171 * that's divided instead of the one that's obtained from
172 * bypassing the MCLK (i.e: TX_BCLK would have the value of
173 * MCLK / ((RX_DIV + 1) * 2)). If BCI is 1, then TX_BCLK will
174 * be the same as the RX_BCLK that's obtained from bypassing
175 * the MCLK on RX's side.
176 *
177 * 4) The check for BCLK == MCLK is there to see if the ASYNC
178 * direction will have the BYP bit toggled.
179 *
180 * IMPORTANT1: in the above diagram and information, RX is SYNC
181 * with TX. The same applies if RX is SYNC with TX. Also, this
182 * applies to i.MX93. For other SoCs, things may be different
183 * so use this information with caution.
184 *
185 * IMPORTANT2: for this to work, you also need to enable the
186 * pad's input path. For i.MX93, this can be achieved by setting
187 * the pad's SION bit.
188 */
sai_config_set_err_051421(I2S_Type * base,const struct sai_config * cfg,const struct sai_bespoke_config * bespoke,sai_transceiver_t * rx_config,sai_transceiver_t * tx_config)189 static void sai_config_set_err_051421(I2S_Type *base,
190 const struct sai_config *cfg,
191 const struct sai_bespoke_config *bespoke,
192 sai_transceiver_t *rx_config,
193 sai_transceiver_t *tx_config)
194 {
195 if (tx_config->masterSlave == kSAI_Master &&
196 bespoke->mclk_rate == bespoke->bclk_rate) {
197 if (cfg->tx_sync_mode == kSAI_ModeSync) {
198 base->TCR2 |= I2S_TCR2_BCI(1);
199 }
200
201 if (cfg->rx_sync_mode == kSAI_ModeSync) {
202 base->RCR2 |= I2S_RCR2_BCI(1);
203 }
204 }
205 }
206 #endif /* CONFIG_SAI_IMX93_ERRATA_051421 */
207
sai_config_set(const struct device * dev,const struct dai_config * cfg,const void * bespoke_data)208 static int sai_config_set(const struct device *dev,
209 const struct dai_config *cfg,
210 const void *bespoke_data)
211 {
212 const struct sai_bespoke_config *bespoke;
213 sai_transceiver_t *rx_config, *tx_config;
214 struct sai_data *data;
215 const struct sai_config *sai_cfg;
216 int ret;
217
218 if (cfg->type != DAI_IMX_SAI) {
219 LOG_ERR("wrong DAI type: %d", cfg->type);
220 return -EINVAL;
221 }
222
223 bespoke = bespoke_data;
224 data = dev->data;
225 sai_cfg = dev->config;
226 rx_config = &data->rx_config;
227 tx_config = &data->tx_config;
228
229 /* since this function configures the transmitter AND the receiver, that
230 * means both of them need to be stopped. As such, doing the state
231 * transition here will also result in a state check.
232 */
233 ret = sai_update_state(DAI_DIR_TX, data, DAI_STATE_READY);
234 if (ret < 0) {
235 LOG_ERR("failed to update TX state. Reason: %d", ret);
236 return ret;
237 }
238
239 ret = sai_update_state(DAI_DIR_RX, data, DAI_STATE_READY);
240 if (ret < 0) {
241 LOG_ERR("failed to update RX state. Reason: %d", ret);
242 return ret;
243 }
244
245 /* condition: BCLK = FSYNC * TDM_SLOT_WIDTH * TDM_SLOTS */
246 if (bespoke->bclk_rate !=
247 (bespoke->fsync_rate * bespoke->tdm_slot_width * bespoke->tdm_slots)) {
248 LOG_ERR("bad BCLK value: %d", bespoke->bclk_rate);
249 return -EINVAL;
250 }
251
252 /* TODO: this should be removed if we're to support sw channels != hw channels */
253 if (count_leading_zeros(~bespoke->tx_slots) != bespoke->tdm_slots ||
254 count_leading_zeros(~bespoke->rx_slots) != bespoke->tdm_slots) {
255 LOG_ERR("number of TX/RX slots doesn't match number of TDM slots");
256 return -EINVAL;
257 }
258
259 /* get default configurations */
260 get_bclk_default_config(&tx_config->bitClock);
261 get_fsync_default_config(&tx_config->frameSync);
262 get_serial_default_config(&tx_config->serialData);
263 get_fifo_default_config(&tx_config->fifo);
264
265 /* note1: this may be obvious but enabling multiple SAI
266 * channels (or data lines) may lead to FIFO starvation/
267 * overflow if data is not written/read from the respective
268 * TDR/RDR registers.
269 *
270 * note2: the SAI data line should be enabled based on
271 * the direction (TX/RX) we're enabling. Enabling the
272 * data line for the opposite direction will lead to FIFO
273 * overrun/underrun when working with a SYNC direction.
274 *
275 * note3: the TX/RX data line shall be enabled/disabled
276 * via the sai_trigger_() suite to avoid scenarios in
277 * which one configures both direction but only starts
278 * the SYNC direction which would lead to a FIFO underrun.
279 */
280 tx_config->channelMask = 0x0;
281
282 /* TODO: for now, only MCLK1 is supported */
283 tx_config->bitClock.bclkSource = kSAI_BclkSourceMclkOption1;
284
285 /* FSYNC is asserted for tdm_slot_width BCLKs */
286 tx_config->frameSync.frameSyncWidth = bespoke->tdm_slot_width;
287
288 /* serial data common configuration */
289 tx_config->serialData.dataWord0Length = bespoke->tdm_slot_width;
290 tx_config->serialData.dataWordNLength = bespoke->tdm_slot_width;
291 tx_config->serialData.dataFirstBitShifted = bespoke->tdm_slot_width;
292 tx_config->serialData.dataWordNum = bespoke->tdm_slots;
293
294 /* clock provider configuration */
295 switch (cfg->format & DAI_FORMAT_CLOCK_PROVIDER_MASK) {
296 case DAI_CBP_CFP:
297 tx_config->masterSlave = kSAI_Slave;
298 break;
299 case DAI_CBC_CFC:
300 tx_config->masterSlave = kSAI_Master;
301 break;
302 case DAI_CBC_CFP:
303 case DAI_CBP_CFC:
304 LOG_ERR("unsupported provider configuration: %d",
305 cfg->format & DAI_FORMAT_CLOCK_PROVIDER_MASK);
306 return -ENOTSUP;
307 default:
308 LOG_ERR("invalid provider configuration: %d",
309 cfg->format & DAI_FORMAT_CLOCK_PROVIDER_MASK);
310 return -EINVAL;
311 }
312
313 LOG_DBG("SAI is in %d mode", tx_config->masterSlave);
314
315 /* protocol configuration */
316 switch (cfg->format & DAI_FORMAT_PROTOCOL_MASK) {
317 case DAI_PROTO_I2S:
318 /* BCLK is active LOW */
319 tx_config->bitClock.bclkPolarity = kSAI_PolarityActiveLow;
320 /* FSYNC is active LOW */
321 tx_config->frameSync.frameSyncPolarity = kSAI_PolarityActiveLow;
322 break;
323 case DAI_PROTO_DSP_A:
324 /* FSYNC is asserted for a single BCLK */
325 tx_config->frameSync.frameSyncWidth = 1;
326 /* BCLK is active LOW */
327 tx_config->bitClock.bclkPolarity = kSAI_PolarityActiveLow;
328 break;
329 default:
330 LOG_ERR("unsupported DAI protocol: %d",
331 cfg->format & DAI_FORMAT_PROTOCOL_MASK);
332 return -EINVAL;
333 }
334
335 LOG_DBG("SAI uses protocol: %d",
336 cfg->format & DAI_FORMAT_PROTOCOL_MASK);
337
338 /* clock inversion configuration */
339 switch (cfg->format & DAI_FORMAT_CLOCK_INVERSION_MASK) {
340 case DAI_INVERSION_IB_IF:
341 SAI_INVERT_POLARITY(tx_config->bitClock.bclkPolarity);
342 SAI_INVERT_POLARITY(tx_config->frameSync.frameSyncPolarity);
343 break;
344 case DAI_INVERSION_IB_NF:
345 SAI_INVERT_POLARITY(tx_config->bitClock.bclkPolarity);
346 break;
347 case DAI_INVERSION_NB_IF:
348 SAI_INVERT_POLARITY(tx_config->frameSync.frameSyncPolarity);
349 break;
350 case DAI_INVERSION_NB_NF:
351 /* nothing to do here */
352 break;
353 default:
354 LOG_ERR("invalid clock inversion configuration: %d",
355 cfg->format & DAI_FORMAT_CLOCK_INVERSION_MASK);
356 return -EINVAL;
357 }
358
359 LOG_DBG("FSYNC polarity: %d", tx_config->frameSync.frameSyncPolarity);
360 LOG_DBG("BCLK polarity: %d", tx_config->bitClock.bclkPolarity);
361
362 /* duplicate TX configuration */
363 memcpy(rx_config, tx_config, sizeof(sai_transceiver_t));
364
365 tx_config->serialData.dataMaskedWord = ~bespoke->tx_slots;
366 rx_config->serialData.dataMaskedWord = ~bespoke->rx_slots;
367
368 tx_config->fifo.fifoWatermark = sai_cfg->tx_fifo_watermark - 1;
369 rx_config->fifo.fifoWatermark = sai_cfg->rx_fifo_watermark - 1;
370
371 LOG_DBG("RX watermark: %d", sai_cfg->rx_fifo_watermark);
372 LOG_DBG("TX watermark: %d", sai_cfg->tx_fifo_watermark);
373
374 /* set the synchronization mode based on data passed from the DTS */
375 tx_config->syncMode = sai_cfg->tx_sync_mode;
376 rx_config->syncMode = sai_cfg->rx_sync_mode;
377
378 ret = pm_device_runtime_get(dev);
379 if (ret < 0) {
380 LOG_ERR("failed to get() SAI device: %d", ret);
381 return ret;
382 }
383
384 /* commit configuration */
385 SAI_RxSetConfig(UINT_TO_I2S(data->regmap), rx_config);
386 SAI_TxSetConfig(UINT_TO_I2S(data->regmap), tx_config);
387
388 /* a few notes here:
389 * 1) TX and RX operate in the same mode: master or slave.
390 * 2) Setting BCLK's rate needs to be performed explicitly
391 * since SetConfig() doesn't do it for us.
392 * 3) Setting BCLK's rate has to be performed after the
393 * SetConfig() call as that resets the SAI registers.
394 */
395 if (tx_config->masterSlave == kSAI_Master) {
396 SAI_TxSetBitClockRate(UINT_TO_I2S(data->regmap), bespoke->mclk_rate,
397 bespoke->fsync_rate, bespoke->tdm_slot_width,
398 bespoke->tdm_slots);
399
400 SAI_RxSetBitClockRate(UINT_TO_I2S(data->regmap), bespoke->mclk_rate,
401 bespoke->fsync_rate, bespoke->tdm_slot_width,
402 bespoke->tdm_slots);
403 }
404
405 #ifdef CONFIG_SAI_HAS_MCLK_CONFIG_OPTION
406 ret = sai_mclk_config(dev, tx_config->bitClock.bclkSource, bespoke);
407 if (ret < 0) {
408 LOG_ERR("failed to set MCLK configuration");
409 pm_device_runtime_put(dev);
410 return ret;
411 }
412 #endif /* CONFIG_SAI_HAS_MCLK_CONFIG_OPTION */
413
414 #ifdef CONFIG_SAI_IMX93_ERRATA_051421
415 sai_config_set_err_051421(UINT_TO_I2S(data->regmap),
416 sai_cfg, bespoke,
417 rx_config, tx_config);
418 #endif /* CONFIG_SAI_IMX93_ERRATA_051421 */
419
420 /* this is needed so that rates different from FSYNC_RATE
421 * will not be allowed.
422 *
423 * this is because the hardware is configured to match
424 * the topology rates so attempting to play a file using
425 * a different rate from the one configured in the hardware
426 * doesn't work properly.
427 *
428 * if != 0, SOF will raise an error if the PCM rate is
429 * different than the hardware rate (a.k.a this one).
430 */
431 data->cfg.rate = bespoke->fsync_rate;
432 /* SOF note: we don't support a variable number of channels
433 * at the moment so leaving the number of channels as 0 is
434 * unnecessary and leads to issues (e.g: the mixer buffers
435 * use this value to set the number of channels so having
436 * a 0 as this value leads to mixer buffers having 0 channels,
437 * which, in turn, leads to the DAI ending up with 0 channels,
438 * thus resulting in an error)
439 */
440 data->cfg.channels = bespoke->tdm_slots;
441
442 sai_dump_register_data(data->regmap);
443
444 return pm_device_runtime_put(dev);
445 }
446
447 /* SOF note: please be very careful with this function as it does
448 * busy waiting and may mess up your timing in time critial applications
449 * (especially with timer domain). If this becomes unusable, the busy
450 * waiting should be removed altogether and the HW state check should
451 * be performed in sai_trigger_start() or in sai_config_set().
452 *
453 * TODO: seems like the transmitter still remains active (even if 1ms
454 * has passed after doing a sai_trigger_stop()!). Most likely this is
455 * because sai_trigger_stop() immediately stops the data line w/o
456 * checking the HW state of the transmitter/receiver. As such, to get
457 * rid of the busy waiting, the STOP operation may have to be split into
458 * 2 operations: TRIG_STOP and TRIG_POST_STOP.
459 */
sai_dir_disable(struct sai_data * data,enum dai_dir dir)460 static bool sai_dir_disable(struct sai_data *data, enum dai_dir dir)
461 {
462 /* VERY IMPORTANT: DO NOT use SAI_TxEnable/SAI_RxEnable
463 * here as they do not disable the ASYNC direction.
464 * Since the software logic assures that the ASYNC direction
465 * is not disabled before the SYNC direction, we can force
466 * the disablement of the given direction.
467 */
468 sai_tx_rx_force_disable(dir, data->regmap);
469
470 /* please note the difference between the transmitter/receiver's
471 * hardware states and their software states. The software
472 * states can be obtained by reading data->tx/rx_enabled, while
473 * the hardware states can be obtained by reading TCSR/RCSR. The
474 * hardware state can actually differ from the software state.
475 * Here, we're interested in reading the hardware state which
476 * indicates if the transmitter/receiver was actually disabled
477 * or not.
478 */
479 return WAIT_FOR(!SAI_TX_RX_IS_HW_ENABLED(dir, data->regmap),
480 SAI_TX_RX_HW_DISABLE_TIMEOUT, k_busy_wait(1));
481 }
482
sai_tx_rx_disable(struct sai_data * data,const struct sai_config * cfg,enum dai_dir dir)483 static int sai_tx_rx_disable(struct sai_data *data,
484 const struct sai_config *cfg, enum dai_dir dir)
485 {
486 enum dai_dir sync_dir, async_dir;
487 bool ret;
488
489 /* sai_disable() should never be called from ISR context
490 * as it does some busy waiting.
491 */
492 if (k_is_in_isr()) {
493 LOG_ERR("sai_disable() should never be called from ISR context");
494 return -EINVAL;
495 }
496
497 if (cfg->tx_sync_mode == kSAI_ModeAsync &&
498 cfg->rx_sync_mode == kSAI_ModeAsync) {
499 ret = sai_dir_disable(data, dir);
500 if (!ret) {
501 LOG_ERR("timed out while waiting for dir %d disable", dir);
502 return -ETIMEDOUT;
503 }
504 } else {
505 sync_dir = SAI_TX_RX_GET_SYNC_DIR(cfg);
506 async_dir = SAI_TX_RX_GET_ASYNC_DIR(cfg);
507
508 if (dir == sync_dir) {
509 ret = sai_dir_disable(data, sync_dir);
510 if (!ret) {
511 LOG_ERR("timed out while waiting for dir %d disable",
512 sync_dir);
513 return -ETIMEDOUT;
514 }
515
516 if (!SAI_TX_RX_DIR_IS_SW_ENABLED(async_dir, data)) {
517 ret = sai_dir_disable(data, async_dir);
518 if (!ret) {
519 LOG_ERR("timed out while waiting for dir %d disable",
520 async_dir);
521 return -ETIMEDOUT;
522 }
523 }
524 } else {
525 if (!SAI_TX_RX_DIR_IS_SW_ENABLED(sync_dir, data)) {
526 ret = sai_dir_disable(data, async_dir);
527 if (!ret) {
528 LOG_ERR("timed out while waiting for dir %d disable",
529 async_dir);
530 return -ETIMEDOUT;
531 }
532 }
533 }
534 }
535
536 return 0;
537 }
538
sai_trigger_pause(const struct device * dev,enum dai_dir dir)539 static int sai_trigger_pause(const struct device *dev,
540 enum dai_dir dir)
541 {
542 struct sai_data *data;
543 const struct sai_config *cfg;
544 int ret;
545
546 data = dev->data;
547 cfg = dev->config;
548
549 if (dir != DAI_DIR_RX && dir != DAI_DIR_TX) {
550 LOG_ERR("invalid direction: %d", dir);
551 return -EINVAL;
552 }
553
554 /* attempt to change state */
555 ret = sai_update_state(dir, data, DAI_STATE_PAUSED);
556 if (ret < 0) {
557 LOG_ERR("failed to transition to PAUSED from %d. Reason: %d",
558 sai_get_state(dir, data), ret);
559 return ret;
560 }
561
562 LOG_DBG("pause on direction %d", dir);
563
564 ret = sai_tx_rx_disable(data, cfg, dir);
565 if (ret < 0) {
566 return ret;
567 }
568
569 /* disable TX/RX data line */
570 sai_tx_rx_set_dline_mask(dir, data->regmap, 0x0);
571
572 /* update the software state of TX/RX */
573 sai_tx_rx_sw_enable_disable(dir, data, false);
574
575 return 0;
576 }
577
sai_trigger_stop(const struct device * dev,enum dai_dir dir)578 static int sai_trigger_stop(const struct device *dev,
579 enum dai_dir dir)
580 {
581 struct sai_data *data;
582 const struct sai_config *cfg;
583 int ret;
584 uint32_t old_state;
585
586 data = dev->data;
587 cfg = dev->config;
588 old_state = sai_get_state(dir, data);
589
590 if (dir != DAI_DIR_RX && dir != DAI_DIR_TX) {
591 LOG_ERR("invalid direction: %d", dir);
592 return -EINVAL;
593 }
594
595 /* attempt to change state */
596 ret = sai_update_state(dir, data, DAI_STATE_STOPPING);
597 if (ret < 0) {
598 LOG_ERR("failed to transition to STOPPING from %d. Reason: %d",
599 sai_get_state(dir, data), ret);
600 return ret;
601 }
602
603 LOG_DBG("stop on direction %d", dir);
604
605 if (old_state == DAI_STATE_PAUSED) {
606 /* if SAI was previously paused then all that's
607 * left to do is disable the DMA requests and
608 * the data line.
609 */
610 goto out_dmareq_disable;
611 }
612
613 ret = sai_tx_rx_disable(data, cfg, dir);
614 if (ret < 0) {
615 return ret;
616 }
617
618 /* update the software state of TX/RX */
619 sai_tx_rx_sw_enable_disable(dir, data, false);
620
621 /* disable TX/RX data line */
622 sai_tx_rx_set_dline_mask(dir, data->regmap, 0x0);
623
624 out_dmareq_disable:
625 /* disable DMA requests */
626 SAI_TX_RX_DMA_ENABLE_DISABLE(dir, data->regmap, false);
627
628 /* disable error interrupt */
629 SAI_TX_RX_ENABLE_DISABLE_IRQ(dir, data->regmap,
630 kSAI_FIFOErrorInterruptEnable, false);
631
632 irq_disable(cfg->irq);
633
634 return pm_device_runtime_put(dev);
635 }
636
637 /* notes:
638 * 1) The "rx_sync_mode" and "tx_sync_mode" properties force the user to pick from
639 * SYNC and ASYNC for each direction. As such, there are 4 possible combinations
640 * that need to be covered here:
641 * a) TX ASYNC, RX ASYNC
642 * b) TX SYNC, RX ASYNC
643 * c) TX ASYNC, RX SYNC
644 * d) TX SYNC, RX SYNC
645 *
646 * Combination d) is not valid and is covered by a BUILD_ASSERT(). As such, there are 3 valid
647 * combinations that need to be supported. Since the main branch of the IF statement covers
648 * combination a), there's only combinations b) and c) to be covered here.
649 *
650 * 2) We can distinguish between 3 types of directions:
651 * a) The target direction. This is the direction on which we want to perform the
652 * software reset.
653 * b) The SYNC direction. This is, well, the direction that's in SYNC with the other
654 * direction.
655 * c) The ASYNC direction.
656 *
657 * Of course, the target direction may differ from the SYNC or ASYNC directions, but it
658 * can't differ from both of them at the same time (i.e: TARGET != SYNC AND TARGET != ASYNC).
659 *
660 * If the target direction is the same as the SYNC direction then we can safely perform the
661 * software reset on the target direction as there's nothing depending on it. We also want
662 * to do a software reset on the ASYNC direction. We can only do this if the ASYNC direction
663 * wasn't software enabled (i.e: through an explicit trigger_start() call).
664 *
665 * If the target direction is the same as the ASYNC direction then we can only perform a
666 * software reset on it only if the SYNC direction wasn't software enabled (i.e: through an
667 * explicit trigger_start() call).
668 */
sai_tx_rx_sw_reset(struct sai_data * data,const struct sai_config * cfg,enum dai_dir dir)669 static void sai_tx_rx_sw_reset(struct sai_data *data,
670 const struct sai_config *cfg, enum dai_dir dir)
671 {
672 enum dai_dir sync_dir, async_dir;
673
674 if (cfg->tx_sync_mode == kSAI_ModeAsync &&
675 cfg->rx_sync_mode == kSAI_ModeAsync) {
676 /* both directions are ASYNC w.r.t each other. As such, do
677 * software reset only on the targeted direction.
678 */
679 SAI_TX_RX_SW_RESET(dir, data->regmap);
680 } else {
681 sync_dir = SAI_TX_RX_GET_SYNC_DIR(cfg);
682 async_dir = SAI_TX_RX_GET_ASYNC_DIR(cfg);
683
684 if (dir == sync_dir) {
685 SAI_TX_RX_SW_RESET(sync_dir, data->regmap);
686
687 if (!SAI_TX_RX_DIR_IS_SW_ENABLED(async_dir, data)) {
688 SAI_TX_RX_SW_RESET(async_dir, data->regmap);
689 }
690 } else {
691 if (!SAI_TX_RX_DIR_IS_SW_ENABLED(sync_dir, data)) {
692 SAI_TX_RX_SW_RESET(async_dir, data->regmap);
693 }
694 }
695 }
696 }
697
sai_trigger_start(const struct device * dev,enum dai_dir dir)698 static int sai_trigger_start(const struct device *dev,
699 enum dai_dir dir)
700 {
701 struct sai_data *data;
702 const struct sai_config *cfg;
703 uint32_t old_state;
704 int ret, i;
705
706 data = dev->data;
707 cfg = dev->config;
708 old_state = sai_get_state(dir, data);
709
710 /* TX and RX should be triggered independently */
711 if (dir != DAI_DIR_RX && dir != DAI_DIR_TX) {
712 LOG_ERR("invalid direction: %d", dir);
713 return -EINVAL;
714 }
715
716 /* attempt to change state */
717 ret = sai_update_state(dir, data, DAI_STATE_RUNNING);
718 if (ret < 0) {
719 LOG_ERR("failed to transition to RUNNING from %d. Reason: %d",
720 sai_get_state(dir, data), ret);
721 return ret;
722 }
723
724 if (old_state == DAI_STATE_PAUSED) {
725 /* if the SAI has been paused then there's no
726 * point in issuing a software reset. As such,
727 * skip this part and go directly to the TX/RX
728 * enablement.
729 */
730 goto out_enable_dline;
731 }
732
733 LOG_DBG("start on direction %d", dir);
734
735 ret = pm_device_runtime_get(dev);
736 if (ret < 0) {
737 LOG_ERR("failed to get() SAI device: %d", ret);
738 return ret;
739 }
740
741 sai_tx_rx_sw_reset(data, cfg, dir);
742
743 irq_enable(cfg->irq);
744
745 /* enable error interrupt */
746 SAI_TX_RX_ENABLE_DISABLE_IRQ(dir, data->regmap,
747 kSAI_FIFOErrorInterruptEnable, true);
748
749 /* avoid initial underrun by writing a frame's worth of 0s */
750 if (dir == DAI_DIR_TX) {
751 for (i = 0; i < data->cfg.channels; i++) {
752 SAI_WriteData(UINT_TO_I2S(data->regmap), cfg->tx_dline, 0x0);
753 }
754 }
755
756 /* TODO: for now, only DMA mode is supported */
757 SAI_TX_RX_DMA_ENABLE_DISABLE(dir, data->regmap, true);
758
759 out_enable_dline:
760 /* enable TX/RX data line. This translates to TX_DLINE0/RX_DLINE0
761 * being enabled.
762 *
763 * TODO: for now we only support 1 data line per direction.
764 */
765 sai_tx_rx_set_dline_mask(dir, data->regmap,
766 SAI_TX_RX_DLINE_MASK(dir, cfg));
767
768 /* this will also enable the async side */
769 SAI_TX_RX_ENABLE_DISABLE(dir, data->regmap, true);
770
771 /* update the software state of TX/RX */
772 sai_tx_rx_sw_enable_disable(dir, data, true);
773
774 return 0;
775 }
776
sai_trigger(const struct device * dev,enum dai_dir dir,enum dai_trigger_cmd cmd)777 static int sai_trigger(const struct device *dev,
778 enum dai_dir dir,
779 enum dai_trigger_cmd cmd)
780 {
781 switch (cmd) {
782 case DAI_TRIGGER_START:
783 return sai_trigger_start(dev, dir);
784 case DAI_TRIGGER_PAUSE:
785 return sai_trigger_pause(dev, dir);
786 case DAI_TRIGGER_STOP:
787 return sai_trigger_stop(dev, dir);
788 case DAI_TRIGGER_PRE_START:
789 case DAI_TRIGGER_COPY:
790 /* COPY and PRE_START don't require the SAI
791 * driver to do anything at the moment so
792 * mark them as successful via a NULL return
793 *
794 * note: although the rest of the unhandled
795 * trigger commands may be valid, return
796 * an error code for them as they aren't
797 * implemented ATM (since they're not
798 * mandatory for the SAI driver to work).
799 */
800 return 0;
801 default:
802 LOG_ERR("invalid trigger command: %d", cmd);
803 return -EINVAL;
804 }
805
806 CODE_UNREACHABLE;
807 }
808
sai_probe(const struct device * dev)809 static int sai_probe(const struct device *dev)
810 {
811 /* nothing to be done here but sadly mandatory to implement */
812 return 0;
813 }
814
sai_remove(const struct device * dev)815 static int sai_remove(const struct device *dev)
816 {
817 /* nothing to be done here but sadly mandatory to implement */
818 return 0;
819 }
820
821 static DEVICE_API(dai, sai_api) = {
822 .config_set = sai_config_set,
823 .config_get = sai_config_get,
824 .trigger = sai_trigger,
825 .get_properties = sai_get_properties,
826 .probe = sai_probe,
827 .remove = sai_remove,
828 };
829
sai_clks_enable_disable(const struct device * dev,bool enable)830 static int sai_clks_enable_disable(const struct device *dev, bool enable)
831 {
832 int i, ret;
833 const struct sai_config *cfg;
834 void *clk_id;
835
836 cfg = dev->config;
837
838 for (i = 0; i < cfg->clk_data.clock_num; i++) {
839 clk_id = UINT_TO_POINTER(cfg->clk_data.clocks[i]);
840
841 if (enable) {
842 ret = clock_control_on(cfg->clk_data.dev, clk_id);
843 } else {
844 ret = clock_control_off(cfg->clk_data.dev, clk_id);
845 }
846
847 if (ret < 0) {
848 LOG_ERR("failed to gate/ungate clock %u: %d",
849 cfg->clk_data.clocks[i], ret);
850 return ret;
851 }
852 }
853
854 return 0;
855 }
856
sai_pm_action(const struct device * dev,enum pm_device_action action)857 __maybe_unused static int sai_pm_action(const struct device *dev,
858 enum pm_device_action action)
859 {
860 bool enable = true;
861
862 switch (action) {
863 case PM_DEVICE_ACTION_RESUME:
864 break;
865 case PM_DEVICE_ACTION_SUSPEND:
866 enable = false;
867 break;
868 case PM_DEVICE_ACTION_TURN_ON:
869 case PM_DEVICE_ACTION_TURN_OFF:
870 return 0;
871 default:
872 return -ENOTSUP;
873 }
874
875 return sai_clks_enable_disable(dev, enable);
876 }
877
sai_init(const struct device * dev)878 static int sai_init(const struct device *dev)
879 {
880 const struct sai_config *cfg;
881 struct sai_data *data;
882 int ret;
883
884 cfg = dev->config;
885 data = dev->data;
886
887 device_map(&data->regmap, cfg->regmap_phys, cfg->regmap_size, K_MEM_CACHE_NONE);
888
889 #ifndef CONFIG_PM_DEVICE_RUNTIME
890 ret = sai_clks_enable_disable(dev, true);
891 if (ret < 0) {
892 return ret;
893 }
894 #endif /* CONFIG_PM_DEVICE_RUNTIME */
895
896 /* note: optional operation so -ENOENT is allowed (i.e: we
897 * allow the default state to not be defined)
898 */
899 ret = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
900 if (ret < 0 && ret != -ENOENT) {
901 return ret;
902 }
903
904 /* set TX/RX default states */
905 data->tx_state = DAI_STATE_NOT_READY;
906 data->rx_state = DAI_STATE_NOT_READY;
907
908 /* register ISR */
909 cfg->irq_config();
910
911 return pm_device_runtime_enable(dev);
912 }
913
914 #define SAI_INIT(inst) \
915 \
916 PINCTRL_DT_INST_DEFINE(inst); \
917 \
918 BUILD_ASSERT(SAI_FIFO_DEPTH(inst) > 0 && \
919 SAI_FIFO_DEPTH(inst) <= _SAI_FIFO_DEPTH(inst), \
920 "invalid FIFO depth"); \
921 \
922 BUILD_ASSERT(SAI_RX_FIFO_WATERMARK(inst) > 0 && \
923 SAI_RX_FIFO_WATERMARK(inst) <= _SAI_FIFO_DEPTH(inst), \
924 "invalid RX FIFO watermark"); \
925 \
926 BUILD_ASSERT(SAI_TX_FIFO_WATERMARK(inst) > 0 && \
927 SAI_TX_FIFO_WATERMARK(inst) <= _SAI_FIFO_DEPTH(inst), \
928 "invalid TX FIFO watermark"); \
929 \
930 BUILD_ASSERT(IS_ENABLED(CONFIG_SAI_HAS_MCLK_CONFIG_OPTION) || \
931 !DT_INST_PROP(inst, mclk_is_output), \
932 "SAI doesn't support MCLK config but mclk_is_output is specified");\
933 \
934 BUILD_ASSERT(SAI_TX_SYNC_MODE(inst) != SAI_RX_SYNC_MODE(inst) || \
935 SAI_TX_SYNC_MODE(inst) != kSAI_ModeSync, \
936 "transmitter and receiver can't be both SYNC with each other"); \
937 \
938 BUILD_ASSERT(SAI_DLINE_COUNT(inst) != -1, \
939 "bad or unsupported SAI instance. Is the base address correct?"); \
940 \
941 BUILD_ASSERT(SAI_TX_DLINE_INDEX(inst) >= 0 && \
942 (SAI_TX_DLINE_INDEX(inst) < SAI_DLINE_COUNT(inst)), \
943 "invalid TX data line index"); \
944 \
945 BUILD_ASSERT(SAI_RX_DLINE_INDEX(inst) >= 0 && \
946 (SAI_RX_DLINE_INDEX(inst) < SAI_DLINE_COUNT(inst)), \
947 "invalid RX data line index"); \
948 \
949 static const struct dai_properties sai_tx_props_##inst = { \
950 .fifo_address = SAI_TX_FIFO_BASE(inst, SAI_TX_DLINE_INDEX(inst)), \
951 .fifo_depth = SAI_FIFO_DEPTH(inst) * CONFIG_SAI_FIFO_WORD_SIZE, \
952 .dma_hs_id = SAI_TX_RX_DMA_HANDSHAKE(inst, tx), \
953 }; \
954 \
955 static const struct dai_properties sai_rx_props_##inst = { \
956 .fifo_address = SAI_RX_FIFO_BASE(inst, SAI_RX_DLINE_INDEX(inst)), \
957 .fifo_depth = SAI_FIFO_DEPTH(inst) * CONFIG_SAI_FIFO_WORD_SIZE, \
958 .dma_hs_id = SAI_TX_RX_DMA_HANDSHAKE(inst, rx), \
959 }; \
960 \
961 void irq_config_##inst(void) \
962 { \
963 IRQ_CONNECT(DT_INST_IRQN(inst), \
964 0, \
965 sai_isr, \
966 DEVICE_DT_INST_GET(inst), \
967 0); \
968 } \
969 \
970 static struct sai_config sai_config_##inst = { \
971 .regmap_phys = DT_INST_REG_ADDR(inst), \
972 .regmap_size = DT_INST_REG_SIZE(inst), \
973 .irq = DT_INST_IRQN(inst), \
974 .clk_data = SAI_CLOCK_DATA_DECLARE(inst), \
975 .rx_fifo_watermark = SAI_RX_FIFO_WATERMARK(inst), \
976 .tx_fifo_watermark = SAI_TX_FIFO_WATERMARK(inst), \
977 .mclk_is_output = DT_INST_PROP(inst, mclk_is_output), \
978 .tx_props = &sai_tx_props_##inst, \
979 .rx_props = &sai_rx_props_##inst, \
980 .irq_config = irq_config_##inst, \
981 .tx_sync_mode = SAI_TX_SYNC_MODE(inst), \
982 .rx_sync_mode = SAI_RX_SYNC_MODE(inst), \
983 .tx_dline = SAI_TX_DLINE_INDEX(inst), \
984 .rx_dline = SAI_RX_DLINE_INDEX(inst), \
985 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
986 }; \
987 \
988 static struct sai_data sai_data_##inst = { \
989 .cfg.type = DAI_IMX_SAI, \
990 .cfg.dai_index = DT_INST_PROP_OR(inst, dai_index, 0), \
991 }; \
992 \
993 PM_DEVICE_DT_INST_DEFINE(inst, sai_pm_action); \
994 \
995 DEVICE_DT_INST_DEFINE(inst, &sai_init, PM_DEVICE_DT_INST_GET(inst), \
996 &sai_data_##inst, &sai_config_##inst, \
997 POST_KERNEL, CONFIG_DAI_INIT_PRIORITY, \
998 &sai_api); \
999
1000 DT_INST_FOREACH_STATUS_OKAY(SAI_INIT);
1001