1 /*
2  * Copyright (c) 2018 STMicroelectronics
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT st_mpxxdtyy
8 
9 #include "mpxxdtyy.h"
10 #include <zephyr/drivers/i2s.h>
11 
12 #define LOG_LEVEL CONFIG_AUDIO_DMIC_LOG_LEVEL
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_DECLARE(mpxxdtyy);
15 
16 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2s)
17 
18 #define NUM_RX_BLOCKS			4
19 #define PDM_BLOCK_MAX_SIZE_BYTES	512
20 
21 K_MEM_SLAB_DEFINE(rx_pdm_i2s_mslab, PDM_BLOCK_MAX_SIZE_BYTES, NUM_RX_BLOCKS, 1);
22 
mpxxdtyy_i2s_read(const struct device * dev,uint8_t stream,void ** buffer,size_t * size,int32_t timeout)23 int mpxxdtyy_i2s_read(const struct device *dev, uint8_t stream, void **buffer,
24 		      size_t *size, int32_t timeout)
25 {
26 	int ret;
27 	const struct mpxxdtyy_config *config = dev->config;
28 	struct mpxxdtyy_data *const data = dev->data;
29 	void *pdm_block, *pcm_block;
30 	size_t pdm_size;
31 	TPDMFilter_InitStruct *pdm_filter = &data->pdm_filter[0];
32 
33 	ret = i2s_read(config->comm_master, &pdm_block, &pdm_size);
34 	if (ret != 0) {
35 		LOG_ERR("read failed (%d)", ret);
36 		return ret;
37 	}
38 
39 	ret = k_mem_slab_alloc(data->pcm_mem_slab,
40 			       &pcm_block, K_NO_WAIT);
41 	if (ret < 0) {
42 		return ret;
43 	}
44 
45 	sw_filter_lib_run(pdm_filter, pdm_block, pcm_block, pdm_size,
46 			  data->pcm_mem_size);
47 	k_mem_slab_free(&rx_pdm_i2s_mslab, pdm_block);
48 
49 	*buffer = pcm_block;
50 	*size = data->pcm_mem_size;
51 
52 	return 0;
53 }
54 
mpxxdtyy_i2s_trigger(const struct device * dev,enum dmic_trigger cmd)55 int mpxxdtyy_i2s_trigger(const struct device *dev, enum dmic_trigger cmd)
56 {
57 	int ret;
58 	const struct mpxxdtyy_config *config = dev->config;
59 	struct mpxxdtyy_data *const data = dev->data;
60 	enum i2s_trigger_cmd i2s_cmd;
61 	enum dmic_state tmp_state;
62 
63 	switch (cmd) {
64 	case DMIC_TRIGGER_START:
65 		if (data->state == DMIC_STATE_CONFIGURED) {
66 			tmp_state = DMIC_STATE_ACTIVE;
67 			i2s_cmd = I2S_TRIGGER_START;
68 		} else {
69 			return 0;
70 		}
71 		break;
72 	case DMIC_TRIGGER_STOP:
73 		if (data->state == DMIC_STATE_ACTIVE) {
74 			tmp_state = DMIC_STATE_CONFIGURED;
75 			i2s_cmd = I2S_TRIGGER_STOP;
76 		} else {
77 			return 0;
78 		}
79 		break;
80 	default:
81 		return -EINVAL;
82 	}
83 
84 	ret = i2s_trigger(config->comm_master, I2S_DIR_RX, i2s_cmd);
85 	if (ret != 0) {
86 		LOG_ERR("trigger failed with %d error", ret);
87 		return ret;
88 	}
89 
90 	data->state = tmp_state;
91 	return 0;
92 }
93 
mpxxdtyy_i2s_configure(const struct device * dev,struct dmic_cfg * cfg)94 int mpxxdtyy_i2s_configure(const struct device *dev, struct dmic_cfg *cfg)
95 {
96 	int ret;
97 	const struct mpxxdtyy_config *config = dev->config;
98 	struct mpxxdtyy_data *const data = dev->data;
99 	uint8_t chan_size = cfg->streams->pcm_width;
100 	uint32_t audio_freq = cfg->streams->pcm_rate;
101 	uint16_t factor;
102 
103 	/* PCM buffer size */
104 	data->pcm_mem_slab = cfg->streams->mem_slab;
105 	data->pcm_mem_size = cfg->streams->block_size;
106 
107 	/* check requested min pdm frequency */
108 	if (cfg->io.min_pdm_clk_freq < MPXXDTYY_MIN_PDM_FREQ ||
109 	    cfg->io.min_pdm_clk_freq > cfg->io.max_pdm_clk_freq) {
110 		return -EINVAL;
111 	}
112 
113 	/* check requested max pdm frequency */
114 	if (cfg->io.max_pdm_clk_freq > MPXXDTYY_MAX_PDM_FREQ ||
115 	    cfg->io.max_pdm_clk_freq < cfg->io.min_pdm_clk_freq) {
116 		return -EINVAL;
117 	}
118 
119 	factor = sw_filter_lib_init(dev, cfg);
120 	if (factor == 0U) {
121 		return -EINVAL;
122 	}
123 
124 	/* configure I2S channels */
125 	struct i2s_config i2s_cfg;
126 
127 	i2s_cfg.word_size = chan_size;
128 	i2s_cfg.channels = cfg->channel.req_num_chan;
129 	i2s_cfg.format = I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED |
130 			 I2S_FMT_BIT_CLK_INV;
131 	i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER;
132 	i2s_cfg.frame_clk_freq = audio_freq * factor / chan_size;
133 	i2s_cfg.block_size = data->pcm_mem_size * (factor / chan_size);
134 	i2s_cfg.mem_slab = &rx_pdm_i2s_mslab;
135 	i2s_cfg.timeout = 2000;
136 
137 	ret = i2s_configure(config->comm_master, I2S_DIR_RX, &i2s_cfg);
138 	if (ret != 0) {
139 		LOG_ERR("I2S device configuration error");
140 		return ret;
141 	}
142 
143 	data->state = DMIC_STATE_CONFIGURED;
144 	return 0;
145 }
146 #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2s) */
147