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