Skip to content

Commit ff2ac0b

Browse files
committed
samples: usb: uac2: Support High-Speed operation
Allow the samples to work both at Full-Speed and High-Speed exposing the same capabilities at both speeds. Signed-off-by: Tomasz Moń <[email protected]>
1 parent 337913b commit ff2ac0b

File tree

12 files changed

+185
-91
lines changed

12 files changed

+185
-91
lines changed

samples/subsys/usb/uac2_explicit_feedback/app.overlay

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
compatible = "zephyr,uac2";
1212
status = "okay";
1313
full-speed;
14+
high-speed;
1415
audio-function = <AUDIO_FUNCTION_OTHER>;
1516

1617
uac_aclk: aclk {

samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,3 @@
22
CONFIG_NRFX_GPPI=y
33
CONFIG_NRFX_TIMER131=y
44
CONFIG_NRFX_GPIOTE130=y
5-
6-
# Sample is Full-Speed only, prevent High-Speed enumeration
7-
CONFIG_UDC_DRIVER_HIGH_SPEED_SUPPORT_ENABLED=n
8-
CONFIG_USBD_MAX_SPEED_FULL=y

samples/subsys/usb/uac2_explicit_feedback/src/feedback.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@
99

1010
#include <stdint.h>
1111

12-
/* Nominal number of samples received on each SOF. This sample is currently
13-
* supporting only 48 kHz sample rate.
14-
*/
15-
#define SAMPLES_PER_SOF 48
12+
/* This sample is currently supporting only 48 kHz sample rate. */
13+
#define SAMPLE_RATE 48000
1614

1715
struct feedback_ctx *feedback_init(void);
18-
void feedback_reset_ctx(struct feedback_ctx *ctx);
16+
void feedback_reset_ctx(struct feedback_ctx *ctx, bool microframes);
1917
void feedback_process(struct feedback_ctx *ctx);
20-
void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued);
18+
void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued,
19+
bool microframes);
2120
uint32_t feedback_value(struct feedback_ctx *ctx);
22-
21+
#define SAMPLES_PER_SOF 48
2322
#endif /* FEEDBACK_H_ */

samples/subsys/usb/uac2_explicit_feedback/src/feedback_dummy.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,49 @@
55
*/
66

77
#include <zephyr/kernel.h>
8+
#include <zephyr/usb/usbd.h>
89
#include "feedback.h"
910

1011
#warning "No target specific feedback code, overruns/underruns will occur"
1112

12-
#define FEEDBACK_K 10
13+
#define FEEDBACK_FS_K 10
14+
#define FEEDBACK_FS_SHIFT 4
15+
#define FEEDBACK_HS_K 13
16+
#define FEEDBACK_HS_SHIFT 3
17+
18+
static struct feedback_ctx {
19+
bool high_speed;
20+
} fb_ctx;
1321

1422
struct feedback_ctx *feedback_init(void)
1523
{
16-
return NULL;
24+
return &fb_ctx;
1725
}
1826

1927
void feedback_process(struct feedback_ctx *ctx)
2028
{
2129
ARG_UNUSED(ctx);
2230
}
2331

24-
void feedback_reset_ctx(struct feedback_ctx *ctx)
32+
void feedback_reset_ctx(struct feedback_ctx *ctx, bool microframes)
2533
{
26-
ARG_UNUSED(ctx);
34+
ctx->high_speed = microframes;
2735
}
2836

29-
void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued)
37+
void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued,
38+
bool microframes)
3039
{
31-
ARG_UNUSED(ctx);
3240
ARG_UNUSED(i2s_blocks_queued);
41+
42+
ctx->high_speed = microframes;
3343
}
3444

3545
uint32_t feedback_value(struct feedback_ctx *ctx)
3646
{
3747
/* Always request nominal number of samples */
38-
return SAMPLES_PER_SOF << FEEDBACK_K;
48+
if (USBD_SUPPORTS_HIGH_SPEED && ctx->high_speed) {
49+
return (SAMPLE_RATE / 8000) << (FEEDBACK_HS_K + FEEDBACK_HS_SHIFT);
50+
}
51+
52+
return (SAMPLE_RATE / 1000) << (FEEDBACK_FS_K + FEEDBACK_FS_SHIFT);
3953
}

samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf.c

Lines changed: 79 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,31 @@ static const nrfx_timer_t feedback_timer_instance =
8484
* Full-Speed isochronous feedback is Q10.10 unsigned integer left-justified in
8585
* the 24-bits so it has Q10.14 format. This sample application puts zeroes to
8686
* the 4 least significant bits (does not use the bits for extra precision).
87+
*
88+
* High-Speed isochronous feedback is Q12.13 unsigned integer aligned in the
89+
* 32-bits so the binary point is located between second and third byte so it
90+
* has Q16.16 format. This sample applications puts zeroes to the 3 least
91+
* significant bits (does not use the bits for extra precision).
8792
*/
88-
#define FEEDBACK_K 10
93+
#define FEEDBACK_FS_K 10
94+
#define FEEDBACK_HS_K 13
8995
#if defined(CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER)
90-
#define FEEDBACK_P 1
96+
#define FEEDBACK_FS_P 1
97+
#define FEEDBACK_HS_P 1
9198
#else
92-
#define FEEDBACK_P 5
99+
#define FEEDBACK_FS_P 5
100+
#define FEEDBACK_HS_P 5
93101
#endif
94102

95103
#define FEEDBACK_FS_SHIFT 4
104+
#define FEEDBACK_HS_SHIFT 3
96105

97106
static struct feedback_ctx {
98107
uint32_t fb_value;
99108
int32_t rel_sof_offset;
100109
int32_t base_sof_offset;
110+
int counts_per_sof;
111+
bool high_speed;
101112
union {
102113
/* For edge counting */
103114
struct {
@@ -190,7 +201,7 @@ struct feedback_ctx *feedback_init(void)
190201

191202
feedback_target_init();
192203

193-
feedback_reset_ctx(&fb_ctx);
204+
feedback_reset_ctx(&fb_ctx, false);
194205

195206
if (IS_ENABLED(CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER)) {
196207
err = feedback_edge_counter_setup();
@@ -239,6 +250,24 @@ struct feedback_ctx *feedback_init(void)
239250
return &fb_ctx;
240251
}
241252

253+
static uint32_t nominal_feedback_value(struct feedback_ctx *ctx)
254+
{
255+
if (ctx->high_speed) {
256+
return (SAMPLE_RATE / 8000) << (FEEDBACK_HS_K + FEEDBACK_HS_SHIFT);
257+
}
258+
259+
return (SAMPLE_RATE / 1000) << (FEEDBACK_FS_K + FEEDBACK_FS_SHIFT);
260+
}
261+
262+
static uint32_t feedback_period(struct feedback_ctx *ctx)
263+
{
264+
if (ctx->high_speed) {
265+
return BIT(FEEDBACK_HS_K - FEEDBACK_HS_P);
266+
}
267+
268+
return BIT(FEEDBACK_FS_K - FEEDBACK_FS_P);
269+
}
270+
242271
static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc,
243272
uint32_t framestart_cc)
244273
{
@@ -255,16 +284,16 @@ static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc,
255284
* when regulated and therefore the relative clock frequency
256285
* discrepancies are essentially negligible.
257286
*/
258-
clks_per_edge = sof_cc / (SAMPLES_PER_SOF << FEEDBACK_P);
287+
clks_per_edge = sof_cc / ctx->counts_per_sof;
259288
sof_cc /= MAX(clks_per_edge, 1);
260289
framestart_cc /= MAX(clks_per_edge, 1);
261290
}
262291

263292
/* /2 because we treat the middle as a turning point from being
264293
* "too late" to "too early".
265294
*/
266-
if (framestart_cc > (SAMPLES_PER_SOF << FEEDBACK_P)/2) {
267-
sof_offset = framestart_cc - (SAMPLES_PER_SOF << FEEDBACK_P);
295+
if (framestart_cc > ctx->counts_per_sof/2) {
296+
sof_offset = framestart_cc - ctx->counts_per_sof;
268297
} else {
269298
sof_offset = framestart_cc;
270299
}
@@ -279,40 +308,46 @@ static void update_sof_offset(struct feedback_ctx *ctx, uint32_t sof_cc,
279308

280309
if (sof_offset >= 0) {
281310
abs_diff = sof_offset - ctx->rel_sof_offset;
282-
base_change = -(SAMPLES_PER_SOF << FEEDBACK_P);
311+
base_change = -ctx->counts_per_sof;
283312
} else {
284313
abs_diff = ctx->rel_sof_offset - sof_offset;
285-
base_change = SAMPLES_PER_SOF << FEEDBACK_P;
314+
base_change = ctx->counts_per_sof;
286315
}
287316

288317
/* Adjust base offset only if the change happened through the
289318
* outer bound. The actual changes should be significantly lower
290319
* than the threshold here.
291320
*/
292-
if (abs_diff > (SAMPLES_PER_SOF << FEEDBACK_P)/2) {
321+
if (abs_diff > ctx->counts_per_sof/2) {
293322
ctx->base_sof_offset += base_change;
294323
}
295324
}
296325

297326
ctx->rel_sof_offset = sof_offset;
298327
}
299328

300-
static inline int32_t offset_to_correction(int32_t offset)
329+
static inline int32_t offset_to_correction(struct feedback_ctx *ctx, int32_t offset)
301330
{
302-
return -(offset / BIT(FEEDBACK_P)) * BIT(FEEDBACK_FS_SHIFT);
331+
if (ctx->high_speed) {
332+
return -(offset / BIT(FEEDBACK_HS_P)) * BIT(FEEDBACK_HS_SHIFT);
333+
}
334+
335+
return -(offset / BIT(FEEDBACK_FS_P)) * BIT(FEEDBACK_FS_SHIFT);
303336
}
304337

305338
static int32_t pi_update(struct feedback_ctx *ctx)
306339
{
340+
const uint32_t scaling = ctx->high_speed ? BIT(13 - FEEDBACK_HS_P) :
341+
BIT(10 - FEEDBACK_FS_P);
307342
int32_t sof_offset = ctx->rel_sof_offset + ctx->base_sof_offset;
308-
/* SOF offset is measured in pow(2, -FEEDBACK_P) samples, i.e. when
309-
* FEEDBACK_P is 0, offset is in samples, and for 1 -> half-samples,
343+
/* SOF offset is measured in pow(2, -FEEDBACK_FS_P) samples, i.e. when
344+
* FEEDBACK_FS_P is 0, offset is in samples, and for 1 -> half-samples,
310345
* 2 -> quarter-samples, 3 -> eightth-samples and so on.
311346
* In order to simplify the PI controller description here, normalize
312347
* the offset to 1/1024 samples (alternatively it can be treated as
313348
* samples in Q10 fixed point format) and use it as Process Variable.
314349
*/
315-
int32_t PV = BIT(10 - FEEDBACK_P) * sof_offset;
350+
int32_t PV = scaling * sof_offset;
316351
/* The control goal is to keep I2S FRAMESTART as close as possible to
317352
* USB SOF and therefore Set Point is 0.
318353
*/
@@ -374,40 +409,47 @@ void feedback_process(struct feedback_ctx *ctx)
374409
ctx->fb_counter += sof_cc;
375410
ctx->fb_periods++;
376411

377-
if (ctx->fb_periods == BIT(FEEDBACK_K - FEEDBACK_P)) {
412+
if (ctx->fb_periods == feedback_period(ctx)) {
378413

379-
/* fb_counter holds Q10.10 value, left-justify it */
380-
fb = ctx->fb_counter << FEEDBACK_FS_SHIFT;
414+
if (ctx->high_speed) {
415+
fb = ctx->fb_counter << FEEDBACK_HS_SHIFT;
416+
} else {
417+
/* fb_counter holds Q10.10 value, left-justify it */
418+
fb = ctx->fb_counter << FEEDBACK_FS_SHIFT;
419+
}
381420

382421
/* Align I2S FRAMESTART to USB SOF by adjusting reported
383422
* feedback value. This is endpoint specific correction
384423
* mentioned but not specified in USB 2.0 Specification.
385424
*/
386-
if (abs(offset) > BIT(FEEDBACK_P)) {
387-
fb += offset_to_correction(offset);
425+
if (abs(offset) > BIT(ctx->high_speed ? FEEDBACK_HS_P : FEEDBACK_FS_P)) {
426+
fb += offset_to_correction(ctx, offset);
388427
}
389428

390429
ctx->fb_value = fb;
391430
ctx->fb_counter = 0;
392431
ctx->fb_periods = 0;
393432
}
394433
} else {
434+
const uint32_t zero_lsb_mask = ctx->high_speed ? 0x7 : 0xF;
435+
395436
/* Use PI controller to generate required feedback deviation
396437
* from nominal feedback value.
397438
*/
398-
fb = SAMPLES_PER_SOF << (FEEDBACK_K + FEEDBACK_FS_SHIFT);
439+
fb = nominal_feedback_value(ctx);
399440
/* Clear the additional LSB bits in feedback value, i.e. do not
400441
* use the optional extra resolution.
401442
*/
402-
fb += pi_update(ctx) & ~0xF;
443+
fb += pi_update(ctx) & ~zero_lsb_mask;
403444
ctx->fb_value = fb;
404445
}
405446
}
406447

407-
void feedback_reset_ctx(struct feedback_ctx *ctx)
448+
void feedback_reset_ctx(struct feedback_ctx *ctx, bool microframes)
408449
{
409450
/* Reset feedback to nominal value */
410-
ctx->fb_value = SAMPLES_PER_SOF << (FEEDBACK_K + FEEDBACK_FS_SHIFT);
451+
ctx->high_speed = microframes;
452+
ctx->fb_value = nominal_feedback_value(ctx);
411453
if (IS_ENABLED(CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER)) {
412454
ctx->fb_counter = 0;
413455
ctx->fb_periods = 0;
@@ -416,19 +458,28 @@ void feedback_reset_ctx(struct feedback_ctx *ctx)
416458
}
417459
}
418460

419-
void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued)
461+
void feedback_start(struct feedback_ctx *ctx, int i2s_blocks_queued,
462+
bool microframes)
420463
{
464+
ctx->high_speed = microframes;
465+
ctx->fb_value = nominal_feedback_value(ctx);
466+
467+
if (microframes) {
468+
ctx->counts_per_sof = (SAMPLE_RATE / 8000) << FEEDBACK_HS_P;
469+
} else {
470+
ctx->counts_per_sof = (SAMPLE_RATE / 1000) << FEEDBACK_FS_P;
471+
}
472+
421473
/* I2S data was supposed to go out at SOF, but it is inevitably
422474
* delayed due to triggering I2S start by software. Set relative
423475
* SOF offset value in a way that ensures that values past "half
424476
* frame" are treated as "too late" instead of "too early"
425477
*/
426-
ctx->rel_sof_offset = (SAMPLES_PER_SOF << FEEDBACK_P) / 2;
478+
ctx->rel_sof_offset = ctx->counts_per_sof / 2;
427479
/* If there are more than 2 I2S blocks queued, use feedback regulator
428480
* to correct the situation.
429481
*/
430-
ctx->base_sof_offset = (i2s_blocks_queued - 2) *
431-
(SAMPLES_PER_SOF << FEEDBACK_P);
482+
ctx->base_sof_offset = (i2s_blocks_queued - 2) * ctx->counts_per_sof;
432483
}
433484

434485
uint32_t feedback_value(struct feedback_ctx *ctx)

0 commit comments

Comments
 (0)