1 /*
2  * Copyright 2025 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/drivers/dai.h>
7 #include <zephyr/device.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/drivers/pinctrl.h>
10 #include <zephyr/logging/log.h>
11 
12 #include "fsl_pdm.h"
13 
14 #define DT_DRV_COMPAT nxp_dai_micfil
15 LOG_MODULE_REGISTER(nxp_dai_micfil);
16 
17 #define MICFIL_CLK_ROOT 24576000
18 #define MICFIL_OSR_DEFAULT	16
19 
20 #define UINT_TO_MICFIL(x) ((PDM_Type *)(uintptr_t)(x))
21 
22 #define MICFIL_FIFO_BASE(inst) \
23 	POINTER_TO_UINT(&(UINT_TO_MICFIL(DT_INST_REG_ADDR(inst))->DATACH[0]))
24 
25 #define MICFIL_DMA_HANDSHAKE(inst) \
26 	((DT_INST_DMAS_CELL_BY_IDX(inst, 0, channel) & GENMASK(7, 0)) | \
27 	((DT_INST_DMAS_CELL_BY_IDX(inst, 0, mux) << 8) & GENMASK(15, 8)))
28 
29 struct dai_nxp_micfil_data {
30 	struct dai_config cfg;
31 };
32 
33 struct dai_nxp_micfil_config {
34 	PDM_Type *base;
35 	const struct dai_properties *rx_props;
36 	const struct pinctrl_dev_config *pincfg;
37 };
38 
39 /* this needs to match SOF struct sof_ipc_dai_micfil_params */
40 struct micfil_bespoke_config {
41 	uint32_t pdm_rate;
42 	uint32_t pdm_ch;
43 };
44 
dai_nxp_micfil_trigger_start(const struct device * dev)45 static void dai_nxp_micfil_trigger_start(const struct device *dev)
46 {
47 	const struct dai_nxp_micfil_config *cfg = dev->config;
48 
49 	/* enable DMA requests */
50 	PDM_EnableDMA(cfg->base, true);
51 	/* enable the module */
52 	PDM_Enable(cfg->base, true);
53 }
54 
dai_nxp_micfil_trigger_stop(const struct device * dev)55 static void dai_nxp_micfil_trigger_stop(const struct device *dev)
56 {
57 	const struct dai_nxp_micfil_config *cfg = dev->config;
58 
59 	/* disable DMA requests */
60 	PDM_EnableDMA(cfg->base, false);
61 	/* disable module */
62 	PDM_Enable(cfg->base, false);
63 }
64 
65 static const struct dai_properties
dai_nxp_micfil_get_properties(const struct device * dev,enum dai_dir dir,int stream_id)66 *dai_nxp_micfil_get_properties(const struct device *dev, enum dai_dir dir, int stream_id)
67 {
68 	const struct dai_nxp_micfil_config *cfg = dev->config;
69 
70 	if (dir == DAI_DIR_RX) {
71 		return cfg->rx_props;
72 	}
73 
74 	LOG_ERR("invalid direction %d", dir);
75 	return NULL;
76 }
77 
dai_nxp_micfil_trigger(const struct device * dev,enum dai_dir dir,enum dai_trigger_cmd cmd)78 static int dai_nxp_micfil_trigger(const struct device *dev, enum dai_dir dir,
79 				  enum dai_trigger_cmd cmd)
80 {
81 	if (dir != DAI_DIR_RX) {
82 		LOG_ERR("invalid direction %d", dir);
83 		return -EINVAL;
84 	}
85 
86 	switch (cmd) {
87 	case DAI_TRIGGER_START:
88 		dai_nxp_micfil_trigger_start(dev);
89 		break;
90 	case DAI_TRIGGER_STOP:
91 	case DAI_TRIGGER_PAUSE:
92 		dai_nxp_micfil_trigger_stop(dev);
93 		break;
94 	case DAI_TRIGGER_PRE_START:
95 	case DAI_TRIGGER_COPY:
96 		return 0;
97 	default:
98 		LOG_ERR("invalid trigger cmd %d", cmd);
99 		return -EINVAL;
100 	}
101 
102 	return 0;
103 }
104 
dai_nxp_micfil_get_config(const struct device * dev,struct dai_config * cfg,enum dai_dir dir)105 static int dai_nxp_micfil_get_config(const struct device *dev, struct dai_config *cfg,
106 				     enum dai_dir dir)
107 {
108 	struct dai_nxp_micfil_data *micfil_data = dev->data;
109 
110 	memcpy(cfg, &micfil_data->cfg, sizeof(*cfg));
111 	return 0;
112 }
113 
dai_nxp_micfil_set_config(const struct device * dev,const struct dai_config * cfg,const void * bespoke_cfg)114 static int dai_nxp_micfil_set_config(const struct device *dev,
115 		const struct dai_config *cfg, const void *bespoke_cfg)
116 
117 {
118 	const struct micfil_bespoke_config *bespoke = bespoke_cfg;
119 	const struct dai_nxp_micfil_config *micfil_cfg = dev->config;
120 	pdm_channel_config_t chan_config = { 0 };
121 	pdm_config_t global_config = { 0 };
122 	int ret, i;
123 
124 	if (cfg->type != DAI_IMX_MICFIL) {
125 		LOG_ERR("wrong DAI type: %d", cfg->type);
126 		return -EINVAL;
127 	}
128 
129 	global_config.fifoWatermark = micfil_cfg->rx_props->fifo_depth - 1;
130 	global_config.qualityMode = kPDM_QualityModeVeryLow0;
131 	global_config.cicOverSampleRate = MICFIL_OSR_DEFAULT;
132 
133 	PDM_Init(micfil_cfg->base, &global_config);
134 
135 	for (i = 0; i < bespoke->pdm_ch; i++) {
136 		chan_config.gain = kPDM_DfOutputGain2;
137 		chan_config.cutOffFreq = kPDM_DcRemoverBypass;
138 		PDM_SetChannelConfig(micfil_cfg->base, i, &chan_config);
139 	}
140 
141 	ret = PDM_SetSampleRateConfig(micfil_cfg->base, MICFIL_CLK_ROOT, bespoke->pdm_rate);
142 	if (ret == kStatus_Fail) {
143 		LOG_ERR("Failure to set samplerate config rate %d", bespoke->pdm_rate);
144 		return -EINVAL;
145 	}
146 
147 	return 0;
148 }
149 
dai_nxp_micfil_probe(const struct device * dev)150 static int dai_nxp_micfil_probe(const struct device *dev)
151 {
152 	/* nothing do to here, but mandatory to exist */
153 	return 0;
154 }
155 
dai_nxp_micfil_remove(const struct device * dev)156 static int dai_nxp_micfil_remove(const struct device *dev)
157 {
158 	/* nothing do to here, but mandatory to exist */
159 	return 0;
160 }
161 
162 static DEVICE_API(dai, dai_nxp_micfil_ops) = {
163 	.probe			= dai_nxp_micfil_probe,
164 	.remove			= dai_nxp_micfil_remove,
165 	.config_set		= dai_nxp_micfil_set_config,
166 	.config_get		= dai_nxp_micfil_get_config,
167 	.get_properties		= dai_nxp_micfil_get_properties,
168 	.trigger		= dai_nxp_micfil_trigger,
169 };
170 
micfil_init(const struct device * dev)171 static int micfil_init(const struct device *dev)
172 {
173 	const struct dai_nxp_micfil_config *cfg = dev->config;
174 	int ret;
175 
176 	/* pinctrl is optional so do not return an error if not defined */
177 	ret = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
178 	if (ret < 0 && ret != -ENOENT) {
179 		return ret;
180 	}
181 
182 	return 0;
183 }
184 
185 #define DAI_NXP_MICFIL_INIT(inst)						\
186 PINCTRL_DT_INST_DEFINE(inst);							\
187 static struct dai_nxp_micfil_data dai_nxp_micfil_data_##inst = {		\
188 	.cfg.type = DAI_IMX_MICFIL,						\
189 	.cfg.dai_index = DT_INST_PROP_OR(inst, dai_index, 0),			\
190 };										\
191 										\
192 static const struct dai_properties micfil_rx_props_##inst = {			\
193 	.fifo_address = MICFIL_FIFO_BASE(inst),					\
194 	.fifo_depth = DT_INST_PROP(inst, fifo_depth),			\
195 	.dma_hs_id = MICFIL_DMA_HANDSHAKE(inst),				\
196 };										\
197 										\
198 static const struct dai_nxp_micfil_config dai_nxp_micfil_config_##inst = {	\
199 	.base = UINT_TO_MICFIL(DT_INST_REG_ADDR(inst)),				\
200 	.rx_props = &micfil_rx_props_##inst,					\
201 	.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),				\
202 };										\
203 										\
204 DEVICE_DT_INST_DEFINE(inst, &micfil_init, NULL,					\
205 	      &dai_nxp_micfil_data_##inst, &dai_nxp_micfil_config_##inst,	\
206 	      POST_KERNEL, CONFIG_DAI_INIT_PRIORITY,				\
207 	      &dai_nxp_micfil_ops);						\
208 
209 DT_INST_FOREACH_STATUS_OKAY(DAI_NXP_MICFIL_INIT)
210