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 <zephyr/devicetree.h>
10 
11 #include "mpxxdtyy.h"
12 
13 #define LOG_LEVEL CONFIG_AUDIO_DMIC_LOG_LEVEL
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(mpxxdtyy);
16 
17 #define CHANNEL_MASK	0x55
18 
19 static uint8_t ch_demux[128] = {
20   0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03,
21   0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03,
22   0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07,
23   0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07,
24   0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03,
25   0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03,
26   0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07,
27   0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x06, 0x07,
28   0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b,
29   0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b,
30   0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f,
31   0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f,
32   0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b,
33   0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b, 0x0a, 0x0b,
34   0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f,
35   0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f
36 };
37 
left_channel(uint8_t a,uint8_t b)38 static uint8_t left_channel(uint8_t a, uint8_t b)
39 {
40 	return ch_demux[a & CHANNEL_MASK] | (ch_demux[b & CHANNEL_MASK] << 4);
41 }
42 
right_channel(uint8_t a,uint8_t b)43 static uint8_t right_channel(uint8_t a, uint8_t b)
44 {
45 	a >>= 1;
46 	b >>= 1;
47 	return ch_demux[a & CHANNEL_MASK] | (ch_demux[b & CHANNEL_MASK] << 4);
48 }
49 
sw_filter_lib_init(const struct device * dev,struct dmic_cfg * cfg)50 uint16_t sw_filter_lib_init(const struct device *dev, struct dmic_cfg *cfg)
51 {
52 	struct mpxxdtyy_data *const data = dev->data;
53 	TPDMFilter_InitStruct *pdm_filter = &data->pdm_filter[0];
54 	uint16_t factor;
55 	uint32_t audio_freq = cfg->streams->pcm_rate;
56 	int i;
57 
58 	/* calculate oversampling factor based on pdm clock */
59 	for (factor = 64U; factor <= 128U; factor += 64U) {
60 		uint32_t pdm_bit_clk = (audio_freq * factor *
61 				     cfg->channel.req_num_chan);
62 
63 		if (pdm_bit_clk >= cfg->io.min_pdm_clk_freq &&
64 		    pdm_bit_clk <= cfg->io.max_pdm_clk_freq) {
65 			break;
66 		}
67 	}
68 
69 	if (factor != 64U && factor != 128U) {
70 		return 0;
71 	}
72 
73 	for (i = 0; i < cfg->channel.req_num_chan; i++) {
74 		/* init the filter lib */
75 		pdm_filter[i].LP_HZ = audio_freq / 2U;
76 		pdm_filter[i].HP_HZ = 10;
77 		pdm_filter[i].Fs = audio_freq;
78 		pdm_filter[i].Out_MicChannels = cfg->channel.req_num_chan;
79 		pdm_filter[i].In_MicChannels = cfg->channel.req_num_chan;
80 		pdm_filter[i].Decimation = factor;
81 		pdm_filter[i].MaxVolume = 64;
82 
83 		Open_PDM_Filter_Init(&data->pdm_filter[i]);
84 	}
85 
86 	return factor;
87 }
88 
sw_filter_lib_run(TPDMFilter_InitStruct * pdm_filter,void * pdm_block,void * pcm_block,size_t pdm_size,size_t pcm_size)89 int sw_filter_lib_run(TPDMFilter_InitStruct *pdm_filter,
90 		      void *pdm_block, void *pcm_block,
91 		      size_t pdm_size, size_t pcm_size)
92 {
93 	int i, j;
94 	int pdm_offset;
95 	uint8_t a, b;
96 
97 	if (pdm_block == NULL || pcm_block == NULL || pdm_filter == NULL) {
98 		return -EINVAL;
99 	}
100 
101 	for (i = 0; i < pdm_size/2; i++) {
102 		switch (pdm_filter[0].In_MicChannels) {
103 		case 1: /* MONO */
104 			((uint16_t *)pdm_block)[i] = HTONS(((uint16_t *)pdm_block)[i]);
105 			break;
106 
107 		case 2: /* STEREO */
108 			if (pdm_filter[0].In_MicChannels > 1) {
109 				a = ((uint8_t *)pdm_block)[2*i];
110 				b = ((uint8_t *)pdm_block)[2*i + 1];
111 
112 				((uint8_t *)pdm_block)[2*i] = left_channel(a, b);
113 				((uint8_t *)pdm_block)[2*i + 1] = right_channel(a, b);
114 			}
115 			break;
116 
117 		default:
118 			return -EINVAL;
119 		}
120 	}
121 
122 	for (j = 0; j < pcm_size / 2; j += pdm_filter[0].Fs / 1000) {
123 		/*
124 		 * The number of PDM bytes per PCM sample is the decimation factor
125 		 * divided by the number of bits per byte (8). We need to skip a number of
126 		 * PDM bytes equivalent to the number of PCM samples, times the number of
127 		 * channels.
128 		 */
129 		pdm_offset = j * (pdm_filter[0].Decimation / 8) * pdm_filter[0].In_MicChannels;
130 
131 		for (i = 0; i < pdm_filter[0].In_MicChannels; i++) {
132 			switch (pdm_filter[0].Decimation) {
133 			case 64:
134 				Open_PDM_Filter_64(&((uint8_t *) pdm_block)[pdm_offset + i],
135 						&((uint16_t *) pcm_block)[j + i],
136 						pdm_filter->MaxVolume,
137 						&pdm_filter[i]);
138 				break;
139 
140 			case 128:
141 				Open_PDM_Filter_128(&((uint8_t *) pdm_block)[pdm_offset + i],
142 						&((uint16_t *) pcm_block)[j + i],
143 						pdm_filter->MaxVolume,
144 						&pdm_filter[i]);
145 				break;
146 
147 			default:
148 				return -EINVAL;
149 			}
150 		}
151 	}
152 
153 	return 0;
154 }
155 
156 static const struct _dmic_ops mpxxdtyy_driver_api = {
157 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2s)
158 	.configure		= mpxxdtyy_i2s_configure,
159 	.trigger		= mpxxdtyy_i2s_trigger,
160 	.read			= mpxxdtyy_i2s_read,
161 #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2s) */
162 };
163 
mpxxdtyy_initialize(const struct device * dev)164 static int mpxxdtyy_initialize(const struct device *dev)
165 {
166 	const struct mpxxdtyy_config *config = dev->config;
167 	struct mpxxdtyy_data *const data = dev->data;
168 
169 	if (!device_is_ready(config->comm_master)) {
170 		return -ENODEV;
171 	}
172 
173 	data->state = DMIC_STATE_INITIALIZED;
174 	return 0;
175 }
176 
177 static const struct mpxxdtyy_config mpxxdtyy_config = {
178 	.comm_master = DEVICE_DT_GET(DT_INST_BUS(0)),
179 };
180 
181 static struct mpxxdtyy_data mpxxdtyy_data;
182 
183 DEVICE_DT_INST_DEFINE(0, mpxxdtyy_initialize, NULL, &mpxxdtyy_data,
184 		      &mpxxdtyy_config, POST_KERNEL,
185 		      CONFIG_AUDIO_DMIC_INIT_PRIORITY, &mpxxdtyy_driver_api);
186