1 /*
2 * Copyright (c) 2023-2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdint.h>
8 #include <stdlib.h>
9
10 #include <sample_usbd.h>
11 #include "feedback.h"
12
13 #include <zephyr/cache.h>
14 #include <zephyr/device.h>
15 #include <zephyr/usb/usbd.h>
16 #include <zephyr/usb/class/usbd_uac2.h>
17 #include <zephyr/drivers/i2s.h>
18 #include <zephyr/logging/log.h>
19
20 LOG_MODULE_REGISTER(uac2_sample, LOG_LEVEL_INF);
21
22 #define HEADPHONES_OUT_TERMINAL_ID UAC2_ENTITY_ID(DT_NODELABEL(out_terminal))
23
24 #define SAMPLE_FREQUENCY (SAMPLES_PER_SOF * 1000)
25 #define SAMPLE_BIT_WIDTH 16
26 #define NUMBER_OF_CHANNELS 2
27 #define BYTES_PER_SAMPLE DIV_ROUND_UP(SAMPLE_BIT_WIDTH, 8)
28 #define BYTES_PER_SLOT (BYTES_PER_SAMPLE * NUMBER_OF_CHANNELS)
29 #define MIN_BLOCK_SIZE ((SAMPLES_PER_SOF - 1) * BYTES_PER_SLOT)
30 #define BLOCK_SIZE (SAMPLES_PER_SOF * BYTES_PER_SLOT)
31 #define MAX_BLOCK_SIZE ((SAMPLES_PER_SOF + 1) * BYTES_PER_SLOT)
32
33 /* Absolute minimum is 5 buffers (1 actively consumed by I2S, 2nd queued as next
34 * buffer, 3rd acquired by USB stack to receive data to, and 2 to handle SOF/I2S
35 * offset errors), but add 2 additional buffers to prevent out of memory errors
36 * when USB host decides to perform rapid terminal enable/disable cycles.
37 */
38 #define I2S_BUFFERS_COUNT 7
39 K_MEM_SLAB_DEFINE_STATIC(i2s_tx_slab, ROUND_UP(MAX_BLOCK_SIZE, UDC_BUF_GRANULARITY),
40 I2S_BUFFERS_COUNT, UDC_BUF_ALIGN);
41
42 struct usb_i2s_ctx {
43 const struct device *i2s_dev;
44 bool terminal_enabled;
45 bool i2s_started;
46 /* Number of blocks written, used to determine when to start I2S.
47 * Overflows are not a problem becuse this variable is not necessary
48 * after I2S is started.
49 */
50 uint8_t i2s_blocks_written;
51 struct feedback_ctx *fb;
52 };
53
uac2_terminal_update_cb(const struct device * dev,uint8_t terminal,bool enabled,bool microframes,void * user_data)54 static void uac2_terminal_update_cb(const struct device *dev, uint8_t terminal,
55 bool enabled, bool microframes,
56 void *user_data)
57 {
58 struct usb_i2s_ctx *ctx = user_data;
59
60 /* This sample has only one terminal therefore the callback can simply
61 * ignore the terminal variable.
62 */
63 __ASSERT_NO_MSG(terminal == HEADPHONES_OUT_TERMINAL_ID);
64 /* This sample is for Full-Speed only devices. */
65 __ASSERT_NO_MSG(microframes == false);
66
67 ctx->terminal_enabled = enabled;
68 if (ctx->i2s_started && !enabled) {
69 i2s_trigger(ctx->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP);
70 ctx->i2s_started = false;
71 ctx->i2s_blocks_written = 0;
72 feedback_reset_ctx(ctx->fb);
73 }
74 }
75
uac2_get_recv_buf(const struct device * dev,uint8_t terminal,uint16_t size,void * user_data)76 static void *uac2_get_recv_buf(const struct device *dev, uint8_t terminal,
77 uint16_t size, void *user_data)
78 {
79 ARG_UNUSED(dev);
80 struct usb_i2s_ctx *ctx = user_data;
81 void *buf = NULL;
82 int ret;
83
84 if (terminal == HEADPHONES_OUT_TERMINAL_ID) {
85 __ASSERT_NO_MSG(size <= MAX_BLOCK_SIZE);
86
87 if (!ctx->terminal_enabled) {
88 LOG_ERR("Buffer request on disabled terminal");
89 return NULL;
90 }
91
92 ret = k_mem_slab_alloc(&i2s_tx_slab, &buf, K_NO_WAIT);
93 if (ret != 0) {
94 buf = NULL;
95 }
96 }
97
98 return buf;
99 }
100
uac2_data_recv_cb(const struct device * dev,uint8_t terminal,void * buf,uint16_t size,void * user_data)101 static void uac2_data_recv_cb(const struct device *dev, uint8_t terminal,
102 void *buf, uint16_t size, void *user_data)
103 {
104 struct usb_i2s_ctx *ctx = user_data;
105 int ret;
106
107 if (!ctx->terminal_enabled) {
108 k_mem_slab_free(&i2s_tx_slab, buf);
109 return;
110 }
111
112 if (!size) {
113 /* Zero fill to keep I2S going. If this is transient error, then
114 * this is probably best we can do. Otherwise, host will likely
115 * either disable terminal (or the cable will be disconnected)
116 * which will stop I2S.
117 */
118 size = BLOCK_SIZE;
119 memset(buf, 0, size);
120 sys_cache_data_flush_range(buf, size);
121 }
122
123 LOG_DBG("Received %d data to input terminal %d", size, terminal);
124
125 ret = i2s_write(ctx->i2s_dev, buf, size);
126 if (ret < 0) {
127 ctx->i2s_started = false;
128 ctx->i2s_blocks_written = 0;
129 feedback_reset_ctx(ctx->fb);
130
131 /* Most likely underrun occurred, prepare I2S restart */
132 i2s_trigger(ctx->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_PREPARE);
133
134 ret = i2s_write(ctx->i2s_dev, buf, size);
135 if (ret < 0) {
136 /* Drop data block, will try again on next frame */
137 k_mem_slab_free(&i2s_tx_slab, buf);
138 }
139 }
140
141 if (ret == 0) {
142 ctx->i2s_blocks_written++;
143 }
144 }
145
uac2_buf_release_cb(const struct device * dev,uint8_t terminal,void * buf,void * user_data)146 static void uac2_buf_release_cb(const struct device *dev, uint8_t terminal,
147 void *buf, void *user_data)
148 {
149 /* This sample does not send audio data so this won't be called */
150 }
151
152 /* Variables for debug use to facilitate simple how feedback value affects
153 * audio data rate experiments. These debug variables can also be used to
154 * determine how well the feedback regulator deals with errors. The values
155 * are supposed to be modified by debugger.
156 *
157 * Setting use_hardcoded_feedback to true, essentially bypasses the feedback
158 * regulator and makes host send hardcoded_feedback samples every 16384 SOFs
159 * (when operating at Full-Speed).
160 *
161 * The feedback at Full-Speed is Q10.14 value. For 48 kHz audio sample rate,
162 * there are nominally 48 samples every SOF. The corresponding value is thus
163 * 48 << 14. Such feedback value would result in host sending always 48 samples.
164 * Now, if we want to receive more samples (because 1 ms according to audio
165 * sink is shorter than 1 ms according to USB Host 500 ppm SOF timer), then
166 * the feedback value has to be increased. The fractional part is 14-bit wide
167 * and therefore increment by 1 means 1 additional sample every 2**14 SOFs.
168 * (48 << 14) + 1 therefore results in host sending 48 samples 16383 times and
169 * 49 samples 1 time during every 16384 SOFs.
170 *
171 * Similarly, if we want to receive less samples (because 1 ms according to
172 * audio signk is longer than 1 ms according to USB Host), then the feedback
173 * value has to be decreased. (48 << 14) - 1 therefore results in host sending
174 * 48 samples 16383 times and 47 samples 1 time during every 16384 SOFs.
175 *
176 * If the feedback value differs by more than 1 (i.e. LSB), then the +1/-1
177 * samples packets are generally evenly distributed. For example feedback value
178 * (48 << 14) + (1 << 5) results in 48 samples 511 times and 49 samples 1 time
179 * during every 512 SOFs.
180 *
181 * For High-Speed above changes slightly, because the feedback format is Q16.16
182 * and microframes are used. The 48 kHz audio sample rate is achieved by sending
183 * 6 samples every SOF (microframe). The nominal value is the average number of
184 * samples to send every microframe and therefore for 48 kHz the nominal value
185 * is (6 << 16).
186 */
187 static volatile bool use_hardcoded_feedback;
188 static volatile uint32_t hardcoded_feedback = (48 << 14) + 1;
189
uac2_feedback_cb(const struct device * dev,uint8_t terminal,void * user_data)190 static uint32_t uac2_feedback_cb(const struct device *dev, uint8_t terminal,
191 void *user_data)
192 {
193 /* Sample has only one UAC2 instance with one terminal so both can be
194 * ignored here.
195 */
196 ARG_UNUSED(dev);
197 ARG_UNUSED(terminal);
198 struct usb_i2s_ctx *ctx = user_data;
199
200 if (use_hardcoded_feedback) {
201 return hardcoded_feedback;
202 } else {
203 return feedback_value(ctx->fb);
204 }
205 }
206
uac2_sof(const struct device * dev,void * user_data)207 static void uac2_sof(const struct device *dev, void *user_data)
208 {
209 ARG_UNUSED(dev);
210 struct usb_i2s_ctx *ctx = user_data;
211
212 if (ctx->i2s_started) {
213 feedback_process(ctx->fb);
214 }
215
216 /* We want to maintain 3 SOFs delay, i.e. samples received during SOF n
217 * should be on I2S during SOF n+3. This provides enough wiggle room
218 * for software scheduling that effectively eliminates "buffers not
219 * provided in time" problem.
220 *
221 * ">= 2" translates into 3 SOFs delay because the timeline is:
222 * USB SOF n
223 * OUT DATA0 n received from host
224 * USB SOF n+1
225 * DATA0 n is available to UDC driver (See Universal Serial Bus
226 * Specification Revision 2.0 5.12.5 Data Prebuffering) and copied
227 * to I2S buffer before SOF n+2; i2s_blocks_written = 1
228 * OUT DATA0 n+1 received from host
229 * USB SOF n+2
230 * DATA0 n+1 is copied; i2s_block_written = 2
231 * OUT DATA0 n+2 received from host
232 * USB SOF n+3
233 * This function triggers I2S start
234 * DATA0 n+2 is copied; i2s_block_written is no longer relevant
235 * OUT DATA0 n+3 received from host
236 */
237 if (!ctx->i2s_started && ctx->terminal_enabled &&
238 ctx->i2s_blocks_written >= 2) {
239 i2s_trigger(ctx->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);
240 ctx->i2s_started = true;
241 feedback_start(ctx->fb, ctx->i2s_blocks_written);
242 }
243 }
244
245 static struct uac2_ops usb_audio_ops = {
246 .sof_cb = uac2_sof,
247 .terminal_update_cb = uac2_terminal_update_cb,
248 .get_recv_buf = uac2_get_recv_buf,
249 .data_recv_cb = uac2_data_recv_cb,
250 .buf_release_cb = uac2_buf_release_cb,
251 .feedback_cb = uac2_feedback_cb,
252 };
253
254 static struct usb_i2s_ctx main_ctx;
255
main(void)256 int main(void)
257 {
258 const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(uac2_headphones));
259 struct usbd_context *sample_usbd;
260 struct i2s_config config;
261 int ret;
262
263 main_ctx.i2s_dev = DEVICE_DT_GET(DT_NODELABEL(i2s_tx));
264
265 if (!device_is_ready(main_ctx.i2s_dev)) {
266 printk("%s is not ready\n", main_ctx.i2s_dev->name);
267 return 0;
268 }
269
270 config.word_size = SAMPLE_BIT_WIDTH;
271 config.channels = NUMBER_OF_CHANNELS;
272 config.format = I2S_FMT_DATA_FORMAT_I2S;
273 config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
274 config.frame_clk_freq = SAMPLE_FREQUENCY;
275 config.mem_slab = &i2s_tx_slab;
276 config.block_size = MAX_BLOCK_SIZE;
277 config.timeout = 0;
278
279 ret = i2s_configure(main_ctx.i2s_dev, I2S_DIR_TX, &config);
280 if (ret < 0) {
281 printk("Failed to configure TX stream: %d\n", ret);
282 return 0;
283 }
284
285 main_ctx.fb = feedback_init();
286
287 usbd_uac2_set_ops(dev, &usb_audio_ops, &main_ctx);
288
289 sample_usbd = sample_usbd_init_device(NULL);
290 if (sample_usbd == NULL) {
291 return -ENODEV;
292 }
293
294 ret = usbd_enable(sample_usbd);
295 if (ret) {
296 return ret;
297 }
298
299 return 0;
300 }
301