1 /*
2  * Copyright 2024 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef ZEPHYR_DRIVERS_DAI_NXP_ESAI_H_
8 #define ZEPHYR_DRIVERS_DAI_NXP_ESAI_H_
9 
10 #include <zephyr/logging/log.h>
11 #include <zephyr/drivers/dai.h>
12 #include <zephyr/device.h>
13 #include <zephyr/dt-bindings/dai/esai.h>
14 
15 #include <fsl_esai.h>
16 
17 LOG_MODULE_REGISTER(nxp_dai_esai);
18 
19 /* used for binding the driver */
20 #define DT_DRV_COMPAT nxp_dai_esai
21 
22 /* workaround the fact that device_map() doesn't exist for SoCs with no MMU */
23 #ifndef DEVICE_MMIO_IS_IN_RAM
24 #define device_map(virt, phys, size, flags) *(virt) = (phys)
25 #endif /* DEVICE_MMIO_IS_IN_RAM */
26 
27 /* macros used for parsing DTS data */
28 
29 #define _ESAI_FIFO_DEPTH(inst)\
30 	FSL_FEATURE_ESAI_FIFO_SIZEn(UINT_TO_ESAI(DT_INST_REG_ADDR(inst)))
31 
32 /* used to fetch the depth of the FIFO. If the "fifo-depth" property is
33  * not specified, the FIFO depth that will be reported to the upper layers
34  * will be 128 * 4 (which is the maximum value, or, well, the actual FIFO
35  * depth)
36  */
37 #define ESAI_FIFO_DEPTH(inst)\
38 	DT_INST_PROP_OR(inst, fifo_depth, _ESAI_FIFO_DEPTH(inst))
39 
40 /* used to fetch the TX FIFO watermark value. If the "tx-fifo-watermark"
41  * property is not specified, this will be set to half of the FIFO depth.
42  */
43 #define ESAI_TX_FIFO_WATERMARK(inst)\
44 	DT_INST_PROP_OR(inst, tx_fifo_watermark, (_ESAI_FIFO_DEPTH(inst) / 2))
45 
46 /* used to fetch the RX FIFO watermark value. If the "rx-fifo-watermark"
47  * property is not specified, this will be set to half of the FIFO depth.
48  */
49 #define ESAI_RX_FIFO_WATERMARK(inst)\
50 	DT_INST_PROP_OR(inst, rx_fifo_watermark, (_ESAI_FIFO_DEPTH(inst) / 2))
51 
52 /* use to fetch the handshake value for a given direction. The handshake
53  * is computed as follows:
54  *	handshake = CHANNEL_ID | (MUX_VALUE << 8)
55  * The channel ID and MUX value are each encoded in 8 bits.
56  */
57 #define ESAI_TX_RX_DMA_HANDSHAKE(inst, dir)\
58 	((DT_INST_DMAS_CELL_BY_NAME(inst, dir, channel) & GENMASK(7, 0)) |\
59 	((DT_INST_DMAS_CELL_BY_NAME(inst, dir, mux) << 8) & GENMASK(15, 8)))
60 
61 /* used to fetch the word width. If the "word-width" property is not specified,
62  * this will default to 24.
63  */
64 #define ESAI_WORD_WIDTH(inst) DT_INST_PROP_OR(inst, word_width, 24)
65 
66 /* utility macros */
67 
68 /* convert uint to ESAI_Type * */
69 #define UINT_TO_ESAI(x) ((ESAI_Type *)(uintptr_t)(x))
70 
71 /* invert a clock's polarity. This works because a clock's polarity
72  * is expressed as a 0 or as a 1.
73  */
74 #define ESAI_INVERT_POLARITY(polarity) (polarity) = !(polarity)
75 
76 #define _ESAI_SLOT_WORD_WIDTH_IS_VALID(width) (!(((width) - 8) % 4))
77 
78 /* used to check if a slot/word width combination is valid */
79 #define ESAI_SLOT_WORD_WIDTH_IS_VALID(slot_width, word_width)\
80 	(_ESAI_SLOT_WORD_WIDTH_IS_VALID(slot_width) && \
81 	_ESAI_SLOT_WORD_WIDTH_IS_VALID(word_width) && \
82 	((word_width) < 32) && ((word_width) <= (slot_width)))
83 
84 /* used to convert slot/word width combination to a value that can be written
85  * to TCR's TSWS or RCR's RSWS.
86  */
87 #define ESAI_SLOT_FORMAT(s, w)\
88 	((w) < 24 ? ((s) - (w) + (((w) - 8) / 4)) : ((s) < 32 ? 0x1e : 0x1f))
89 
90 /* used to compute the word alignment based on the word width value.
91  * This returns a value that can be written to TFCR's TWA or RFCR's
92  * RWA.
93  */
94 #define ESAI_WORD_ALIGNMENT(word_width) ((32 - (word_width)) / 4)
95 
96 #define _ESAI_RX_FIFO_USAGE_EN(mask)\
97 	(((mask) << ESAI_RFCR_RE0_SHIFT) &\
98 	(ESAI_RFCR_RE0_MASK | ESAI_RFCR_RE1_MASK |\
99 	 ESAI_RFCR_RE2_MASK | ESAI_RFCR_RE3_MASK))
100 
101 #define _ESAI_TX_FIFO_USAGE_EN(mask)\
102 	(((mask) << ESAI_TFCR_TE0_SHIFT) &\
103 	(ESAI_TFCR_TE0_MASK | ESAI_TFCR_TE1_MASK | ESAI_TFCR_TE2_MASK |\
104 	 ESAI_TFCR_TE3_MASK | ESAI_TFCR_TE4_MASK | ESAI_TFCR_TE5_MASK))
105 
106 /* used to fetch the mask for setting TX/RX FIFO usage. By FIFO usage
107  * we mean we allow receivers/transmitters to use the "global" TX/RX
108  * FIFO (i.e: the FIFO that's common to all transmitters/receivers).
109  * More specifically, this macro returns the mask required for setting
110  * TFCR's TEx fields or RFCR's REx fields.
111  */
112 #define ESAI_TX_RX_FIFO_USAGE_EN(dir, mask)\
113 	((dir) == DAI_DIR_TX ? _ESAI_TX_FIFO_USAGE_EN(mask) :\
114 	 _ESAI_RX_FIFO_USAGE_EN(mask))
115 
116 #define _ESAI_TX_EN(mask)\
117 	(((mask) << ESAI_TCR_TE0_SHIFT) &\
118 	(ESAI_TCR_TE0_MASK | ESAI_TCR_TE1_MASK | ESAI_TCR_TE2_MASK |\
119 	 ESAI_TCR_TE3_MASK | ESAI_TCR_TE4_MASK | ESAI_TCR_TE5_MASK))
120 
121 #define _ESAI_RX_EN(mask)\
122 	(((mask) << ESAI_RCR_RE0_SHIFT) &\
123 	(ESAI_RCR_RE0_MASK | ESAI_RCR_RE1_MASK | ESAI_RCR_RE2_MASK |\
124 	 ESAI_RCR_RE3_MASK))
125 
126 /* used to fetch the mask for enabling transmitters/receivers.
127  * More specifically, this refers to TCR's TEx bits or RCR's REx
128  * bits.
129  */
130 #define ESAI_TX_RX_EN(dir, mask)\
131 	((dir) == DAI_DIR_TX ? _ESAI_TX_EN(mask) : _ESAI_RX_EN(mask))
132 
133 /* used to fetch the base address of the TX FIFO */
134 #define ESAI_TX_FIFO_BASE(inst)\
135 	POINTER_TO_UINT(&(UINT_TO_ESAI(DT_INST_REG_ADDR(inst))->ETDR))
136 
137 /* used to fetch the base address of the RX FIFO */
138 #define ESAI_RX_FIFO_BASE(inst)\
139 	POINTER_TO_UINT(&(UINT_TO_ESAI(DT_INST_REG_ADDR(inst))->ERDR))
140 
141 /* used to check if an ESAI pin is used. An ESAI pin is considered to
142  * be used if PDC and PC bits for that pin are set (i.e: pin is in ESAI
143  * mode).
144  *
145  * The ESAI pins support 4 functionalities which can be configured
146  * via PCRC and PRRC:
147  *	1) Disconnected
148  *	2) GPIO input
149  *	3) GPIO output
150  *	4) ESAI
151  */
152 #define ESAI_PIN_IS_USED(data, which)\
153 	(((data)->pcrc & BIT(which)) && ((data->prrc) & BIT(which)))
154 
155 struct esai_data {
156 	mm_reg_t regmap;
157 	struct dai_config cfg;
158 	/* transmitter state */
159 	enum dai_state tx_state;
160 	/* receiver state */
161 	enum dai_state rx_state;
162 	/* value to be committed to PRRC. This is computed
163 	 * during esai_init() and committed during config_set()
164 	 * stage.
165 	 */
166 	uint32_t prrc;
167 	/* value to be committed to PCRC. Computed and committed
168 	 * during the same stages as PRRC.
169 	 */
170 	uint32_t pcrc;
171 };
172 
173 struct esai_config {
174 	uint32_t regmap_phys;
175 	uint32_t regmap_size;
176 	const struct dai_properties *tx_props;
177 	const struct dai_properties *rx_props;
178 	uint32_t rx_fifo_watermark;
179 	uint32_t tx_fifo_watermark;
180 	uint32_t word_width;
181 	uint32_t *pinmodes;
182 	uint32_t pinmodes_size;
183 	uint32_t *clock_cfg;
184 	uint32_t clock_cfg_size;
185 };
186 
187 /* this needs to perfectly match SOF's struct sof_ipc_dai_esai_params */
188 struct esai_bespoke_config {
189 	uint32_t reserved0;
190 
191 	uint16_t reserved1;
192 	uint16_t mclk_id;
193 	uint32_t mclk_direction;
194 
195 	/* clock-related data */
196 	uint32_t mclk_rate;
197 	uint32_t fsync_rate;
198 	uint32_t bclk_rate;
199 
200 	/* TDM-related data */
201 	uint32_t tdm_slots;
202 	uint32_t rx_slots;
203 	uint32_t tx_slots;
204 	uint16_t tdm_slot_width;
205 
206 	uint16_t reserved2;
207 };
208 
209 struct esai_transceiver_config {
210 	/* enable/disable the HCLK prescaler */
211 	bool hclk_prescaler_en;
212 	/* controls the divison value of HCLK (i.e: TPM0-TPM7) */
213 	uint32_t hclk_div_ratio;
214 	/* controls the division value of HCLK before reaching
215 	 * BCLK consumers (i.e: TFP0-TFP3)
216 	 */
217 	uint32_t bclk_div_ratio;
218 	/* should the HCLK divison be bypassed or not?
219 	 * If in bypass, HCLK pad will be the same as EXTAL
220 	 */
221 	bool hclk_bypass;
222 
223 	/* HCLK direction - input or output */
224 	esai_clock_direction_t hclk_dir;
225 	/* HCLK source - EXTAL or IPG clock */
226 	esai_hclk_source_t hclk_src;
227 	/* HCLK polarity - LOW or HIGH */
228 	esai_clock_polarity_t hclk_polarity;
229 
230 	/* BCLK direction - input or output */
231 	esai_clock_direction_t bclk_dir;
232 	/* BCLK polarity - LOW or HIGH */
233 	esai_clock_polarity_t bclk_polarity;
234 
235 	/* FSYNC direction - input or output */
236 	esai_clock_direction_t fsync_dir;
237 	/* FSYNC polarity - LOW or HIGH */
238 	esai_clock_polarity_t fsync_polarity;
239 
240 	/* should FSYNC be bit-wide or word-wide? */
241 	bool fsync_is_bit_wide;
242 	/* enable/disable padding word with zeros. If
243 	 * disabled, pad will be done using last/first
244 	 * bit - see TCR's PADC bit for more info.
245 	 */
246 	bool zero_pad_en;
247 	/* should FSYNC be asserted before MSB transmission
248 	 * or alongside it?
249 	 */
250 	bool fsync_early;
251 
252 	/* FSYNC divison value - for network mode this is
253 	 * the same as the number of slots - 1.
254 	 */
255 	uint32_t fsync_div;
256 
257 	/* slot format - see TCR's TSWS or RCR's RSWS */
258 	esai_slot_format_t slot_format;
259 	/* mode - network or normal
260 	 * TODO: at the moment, only network mode is supported.
261 	 */
262 	esai_mode_t mode;
263 
264 	/* controls whether MSB or LSB is transmitted first */
265 	esai_shift_direction_t data_order;
266 
267 	/* controls the word alignment inside a slot. If enabled
268 	 * word is left-aligned, otherwise it will be right-aligned.
269 	 * For details, see TCR/RCR's TWA/RWA.
270 	 */
271 	bool data_left_aligned;
272 	/* TX/RX watermark value */
273 	uint32_t watermark;
274 
275 	/* concatenation of TSMA+TSMB/RSMA+RSMB. Controls which
276 	 * slots should be High-Z or data.
277 	 */
278 	uint32_t slot_mask;
279 	/* controls the alignment of data written to FIFO.
280 	 * See TFCR's TWA or RFCR's RWA for more details.
281 	 */
282 	uint32_t word_alignment;
283 };
284 
esai_parse_clock_config(const struct esai_config * cfg,struct esai_transceiver_config * tx_cfg,struct esai_transceiver_config * rx_cfg)285 static int esai_parse_clock_config(const struct esai_config *cfg,
286 				   struct esai_transceiver_config *tx_cfg,
287 				   struct esai_transceiver_config *rx_cfg)
288 {
289 	int i;
290 	uint32_t crt_clock, crt_dir;
291 
292 	for (i = 0; i < cfg->clock_cfg_size; i += 2) {
293 		crt_clock = cfg->clock_cfg[i];
294 		crt_dir = cfg->clock_cfg[i + 1];
295 
296 		/* sanity checks */
297 		if (crt_clock > ESAI_CLOCK_FST) {
298 			LOG_ERR("invalid clock configuration ID: %d", crt_clock);
299 			return -EINVAL;
300 		}
301 
302 		if (crt_dir > ESAI_CLOCK_OUTPUT) {
303 			LOG_ERR("invalid clock configuration direction: %d", crt_dir);
304 			return -EINVAL;
305 		}
306 
307 		switch (crt_clock) {
308 		case ESAI_CLOCK_HCKT:
309 			tx_cfg->hclk_dir = crt_dir;
310 			break;
311 		case ESAI_CLOCK_HCKR:
312 			rx_cfg->hclk_dir = crt_dir;
313 			break;
314 		case ESAI_CLOCK_SCKT:
315 			tx_cfg->bclk_dir = crt_dir;
316 			break;
317 		case ESAI_CLOCK_SCKR:
318 			rx_cfg->bclk_dir = crt_dir;
319 			break;
320 		case ESAI_CLOCK_FST:
321 			tx_cfg->fsync_dir = crt_dir;
322 			break;
323 		case ESAI_CLOCK_FSR:
324 			rx_cfg->fsync_dir = crt_dir;
325 			break;
326 		}
327 	}
328 
329 	return 0;
330 }
331 
esai_parse_pinmodes(const struct esai_config * cfg,struct esai_data * data)332 static int esai_parse_pinmodes(const struct esai_config *cfg,
333 			      struct esai_data *data)
334 {
335 	int i;
336 	uint32_t pin, pin_mode;
337 
338 	/* initially, the assumption is that all pins are in ESAI mode */
339 	data->pcrc = ESAI_PCRC_PC_MASK;
340 	data->prrc = ESAI_PRRC_PDC_MASK;
341 
342 	for (i = 0; i < cfg->pinmodes_size; i += 2) {
343 		pin = cfg->pinmodes[i];
344 		pin_mode = cfg->pinmodes[i + 1];
345 
346 		if (pin > ESAI_PIN_SDO0 || pin_mode > ESAI_PIN_ESAI) {
347 			return -EINVAL;
348 		}
349 
350 		switch (pin_mode) {
351 		case ESAI_PIN_DISCONNECTED:
352 			data->pcrc &= ~BIT(pin);
353 			data->prrc &= ~BIT(pin);
354 			break;
355 		case ESAI_PIN_GPIO_INPUT:
356 			data->pcrc &= ~BIT(pin);
357 			break;
358 		case ESAI_PIN_GPIO_OUTPUT:
359 			data->prrc &= ~BIT(pin);
360 			break;
361 		case ESAI_PIN_ESAI:
362 			/* nothing to be done here, this is the default */
363 			break;
364 		}
365 	}
366 
367 	return 0;
368 }
369 
esai_get_state(struct esai_data * data,enum dai_dir dir)370 static inline uint32_t esai_get_state(struct esai_data *data,
371 				      enum dai_dir dir)
372 {
373 	if (dir == DAI_DIR_RX) {
374 		return data->rx_state;
375 	} else {
376 		return data->tx_state;
377 	}
378 }
379 
esai_update_state(struct esai_data * data,enum dai_dir dir,enum dai_state new_state)380 static inline int esai_update_state(struct esai_data *data,
381 				    enum dai_dir dir, enum dai_state new_state)
382 {
383 	enum dai_state old_state = esai_get_state(data, dir);
384 
385 	LOG_DBG("attempting state transition from %d to %d", old_state, new_state);
386 
387 	switch (new_state) {
388 	case DAI_STATE_NOT_READY:
389 		/* initial state, transition is not possible */
390 		return -EPERM;
391 	case DAI_STATE_READY:
392 		if (old_state != DAI_STATE_NOT_READY &&
393 		    old_state != DAI_STATE_READY &&
394 		    old_state != DAI_STATE_STOPPING) {
395 			return -EPERM;
396 		}
397 		break;
398 	case DAI_STATE_RUNNING:
399 		if (old_state != DAI_STATE_STOPPING &&
400 		    old_state != DAI_STATE_READY) {
401 			return -EPERM;
402 		}
403 		break;
404 	case DAI_STATE_STOPPING:
405 		if (old_state != DAI_STATE_RUNNING) {
406 			return -EPERM;
407 		}
408 		break;
409 	default:
410 		LOG_ERR("invalid new state: %d", new_state);
411 		return -EINVAL;
412 	}
413 
414 	if (dir == DAI_DIR_RX) {
415 		data->rx_state = new_state;
416 	} else {
417 		data->tx_state = new_state;
418 	}
419 
420 	return 0;
421 }
422 
esai_tx_rx_enable_disable_fifo(ESAI_Type * base,enum dai_dir dir,bool enable)423 static inline void esai_tx_rx_enable_disable_fifo(ESAI_Type *base,
424 						  enum dai_dir dir,
425 						  bool enable)
426 {
427 	if (enable) {
428 		if (dir == DAI_DIR_RX) {
429 			base->RFCR |= ESAI_RFCR_RFE_MASK;
430 		} else {
431 			base->TFCR |= ESAI_TFCR_TFE_MASK;
432 		}
433 	} else {
434 		if (dir == DAI_DIR_RX) {
435 			base->RFCR &= ~ESAI_RFCR_RFE_MASK;
436 		} else {
437 			base->TFCR &= ~ESAI_TFCR_TFE_MASK;
438 		}
439 	}
440 }
441 
esai_tx_rx_enable_disable(ESAI_Type * base,enum dai_dir dir,uint32_t which,bool enable)442 static inline void esai_tx_rx_enable_disable(ESAI_Type *base,
443 					     enum dai_dir dir,
444 					     uint32_t which, bool enable)
445 {
446 	uint32_t val = ESAI_TX_RX_EN(dir, which);
447 
448 	if (enable) {
449 		if (dir == DAI_DIR_RX) {
450 			base->RCR |= val;
451 		} else {
452 			base->TCR |= val;
453 		}
454 	} else {
455 		if (dir == DAI_DIR_RX) {
456 			base->RCR &= ~val;
457 		} else {
458 			base->TCR &= ~val;
459 		}
460 	}
461 }
462 
esai_tx_rx_enable_disable_fifo_usage(ESAI_Type * base,enum dai_dir dir,uint32_t which,bool enable)463 static inline void esai_tx_rx_enable_disable_fifo_usage(ESAI_Type *base,
464 							enum dai_dir dir,
465 							uint32_t which, bool enable)
466 {
467 	uint32_t val = ESAI_TX_RX_FIFO_USAGE_EN(dir, which);
468 
469 	if (enable) {
470 		if (dir == DAI_DIR_RX) {
471 			base->RFCR |= val;
472 		} else {
473 			base->TFCR |= val;
474 		}
475 	} else {
476 		if (dir == DAI_DIR_RX) {
477 			base->RFCR &= ~val;
478 		} else {
479 			base->TFCR &= ~val;
480 		}
481 	}
482 }
483 
esai_dump_xceiver_config(struct esai_transceiver_config * cfg)484 static inline void esai_dump_xceiver_config(struct esai_transceiver_config *cfg)
485 {
486 	LOG_DBG("HCLK prescaler enable: %d", cfg->hclk_prescaler_en);
487 	LOG_DBG("HCLK divider ratio: %d", cfg->hclk_div_ratio);
488 	LOG_DBG("BCLK divider ratio: %d", cfg->bclk_div_ratio);
489 	LOG_DBG("HCLK bypass: %d", cfg->hclk_bypass);
490 
491 	LOG_DBG("HCLK direction: %d", cfg->hclk_dir);
492 	LOG_DBG("HCLK source: %d", cfg->hclk_src);
493 	LOG_DBG("HCLK polarity: %d", cfg->hclk_polarity);
494 
495 	LOG_DBG("BCLK direction: %d", cfg->bclk_dir);
496 	LOG_DBG("BCLK polarity: %d", cfg->bclk_polarity);
497 
498 	LOG_DBG("FSYNC direction: %d", cfg->fsync_dir);
499 	LOG_DBG("FSYNC polarity: %d", cfg->fsync_polarity);
500 
501 	LOG_DBG("FSYNC is bit wide: %d", cfg->fsync_is_bit_wide);
502 	LOG_DBG("zero pad enable: %d", cfg->zero_pad_en);
503 	LOG_DBG("FSYNC asserted early: %d", cfg->fsync_early);
504 
505 	LOG_DBG("watermark: %d", cfg->watermark);
506 	LOG_DBG("slot mask: 0x%x", cfg->slot_mask);
507 	LOG_DBG("word alignment: 0x%x", cfg->word_alignment);
508 }
509 
esai_dump_register_data(ESAI_Type * base)510 static inline void esai_dump_register_data(ESAI_Type *base)
511 {
512 	LOG_DBG("ECR: 0x%x", base->ECR);
513 	LOG_DBG("ESR: 0x%x", base->ESR);
514 	LOG_DBG("TFCR: 0x%x", base->TFCR);
515 	LOG_DBG("TFSR: 0x%x", base->TFSR);
516 	LOG_DBG("RFCR: 0x%x", base->RFCR);
517 	LOG_DBG("RFSR: 0x%x", base->RFSR);
518 	LOG_DBG("TSR: 0x%x", base->TSR);
519 	LOG_DBG("SAISR: 0x%x", base->SAISR);
520 	LOG_DBG("SAICR: 0x%x", base->SAICR);
521 	LOG_DBG("TCR: 0x%x", base->TCR);
522 	LOG_DBG("TCCR: 0x%x", base->TCCR);
523 	LOG_DBG("RCR: 0x%x", base->RCR);
524 	LOG_DBG("RCCR: 0x%x", base->RCCR);
525 	LOG_DBG("TSMA: 0x%x", base->TSMA);
526 	LOG_DBG("TSMB: 0x%x", base->TSMB);
527 	LOG_DBG("RSMA: 0x%x", base->RSMA);
528 	LOG_DBG("RSMB: 0x%x", base->RSMB);
529 	LOG_DBG("PRRC: 0x%x", base->PRRC);
530 	LOG_DBG("PCRC: 0x%x", base->PCRC);
531 }
532 
533 #endif /* ZEPHYR_DRIVERS_DAI_NXP_ESAI_H_ */
534