| /* |
| * Copyright 2023 NXP |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /* |
| * Based on DMIC driver sample, which is: |
| * Copyright (c) 2021 Nordic Semiconductor ASA |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/audio/dmic.h> |
| #include <zephyr/ztest.h> |
| |
| static const struct device *dmic_dev = DEVICE_DT_GET(DT_ALIAS(dmic_dev)); |
| |
| #if DT_HAS_COMPAT_STATUS_OKAY(nxp_dmic) |
| #define PDM_CHANNELS 4 /* Two L/R pairs of channels */ |
| #define SAMPLE_BIT_WIDTH 16 |
| #define BYTES_PER_SAMPLE sizeof(int16_t) |
| #define SLAB_ALIGN 4 |
| #define MAX_SAMPLE_RATE 48000 |
| /* Milliseconds to wait for a block to be read. */ |
| #define READ_TIMEOUT 1000 |
| /* Size of a block for 100 ms of audio data. */ |
| #define BLOCK_SIZE(_sample_rate, _number_of_channels) \ |
| (BYTES_PER_SAMPLE * (_sample_rate / 10) * _number_of_channels) |
| #else |
| #error "Unsupported DMIC device" |
| #endif |
| |
| /* Driver will allocate blocks from this slab to receive audio data into them. |
| * Application, after getting a given block from the driver and processing its |
| * data, needs to free that block. |
| */ |
| #define MAX_BLOCK_SIZE BLOCK_SIZE(MAX_SAMPLE_RATE, PDM_CHANNELS) |
| #define BLOCK_COUNT 8 |
| K_MEM_SLAB_DEFINE_STATIC(mem_slab, MAX_BLOCK_SIZE, BLOCK_COUNT, SLAB_ALIGN); |
| |
| static struct pcm_stream_cfg pcm_stream = { |
| .pcm_width = SAMPLE_BIT_WIDTH, |
| .mem_slab = &mem_slab, |
| }; |
| static struct dmic_cfg dmic_cfg = { |
| .io = { |
| /* These fields can be used to limit the PDM clock |
| * configurations that the driver is allowed to use |
| * to those supported by the microphone. |
| */ |
| .min_pdm_clk_freq = 1000000, |
| .max_pdm_clk_freq = 3500000, |
| .min_pdm_clk_dc = 40, |
| .max_pdm_clk_dc = 60, |
| }, |
| .streams = &pcm_stream, |
| .channel = { |
| .req_num_streams = 1, |
| }, |
| }; |
| |
| /* Verify that dmic_trigger fails when DMIC is not configured |
| * this test must run first, before DMIC has been configured |
| */ |
| ZTEST(dmic, test_0_start_fail) |
| { |
| int ret; |
| |
| zassert_true(device_is_ready(dmic_dev), "DMIC device is not ready"); |
| ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START); |
| zassert_not_equal(ret, 0, "DMIC trigger should fail when DMIC is not configured"); |
| } |
| |
| static int do_pdm_transfer(const struct device *dmic, |
| struct dmic_cfg *cfg) |
| { |
| int ret; |
| void *buffer; |
| uint32_t size; |
| |
| TC_PRINT("PCM output rate: %u, channels: %u\n", |
| cfg->streams[0].pcm_rate, cfg->channel.req_num_chan); |
| |
| ret = dmic_configure(dmic, cfg); |
| if (ret < 0) { |
| TC_PRINT("DMIC configuration failed: %d\n", ret); |
| return ret; |
| } |
| |
| /* Check that the driver is properly populating the "act*" fields */ |
| zassert_equal(cfg->channel.act_num_chan, |
| cfg->channel.req_num_chan, |
| "DMIC configure should populate act_num_chan field"); |
| zassert_equal(cfg->channel.act_chan_map_lo, |
| cfg->channel.req_chan_map_lo, |
| "DMIC configure should populate act_chan_map_lo field"); |
| zassert_equal(cfg->channel.act_chan_map_hi, |
| cfg->channel.req_chan_map_hi, |
| "DMIC configure should populate act_chan_map_hi field"); |
| ret = dmic_trigger(dmic, DMIC_TRIGGER_START); |
| if (ret < 0) { |
| TC_PRINT("DMIC start trigger failed: %d\n", ret); |
| return ret; |
| } |
| |
| /* We read more than the total BLOCK_COUNT to insure the DMIC |
| * driver correctly reallocates memory slabs as it exhausts existing |
| * ones. |
| */ |
| for (int i = 0; i < (2 * BLOCK_COUNT); i++) { |
| ret = dmic_read(dmic, 0, &buffer, &size, READ_TIMEOUT); |
| if (ret < 0) { |
| TC_PRINT("DMIC read failed: %d\n", ret); |
| return ret; |
| } |
| |
| TC_PRINT("%d - got buffer %p of %u bytes\n", i, buffer, size); |
| k_mem_slab_free(&mem_slab, buffer); |
| } |
| |
| ret = dmic_trigger(dmic, DMIC_TRIGGER_STOP); |
| if (ret < 0) { |
| TC_PRINT("DMIC stop trigger failed: %d\n", ret); |
| return ret; |
| } |
| return 0; |
| } |
| |
| |
| /* Verify that the DMIC can transfer from a single channel */ |
| ZTEST(dmic, test_single_channel) |
| { |
| dmic_cfg.channel.req_num_chan = 1; |
| dmic_cfg.channel.req_chan_map_lo = |
| dmic_build_channel_map(0, 0, PDM_CHAN_LEFT); |
| dmic_cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE; |
| dmic_cfg.streams[0].block_size = |
| BLOCK_SIZE(dmic_cfg.streams[0].pcm_rate, |
| dmic_cfg.channel.req_num_chan); |
| zassert_equal(do_pdm_transfer(dmic_dev, &dmic_cfg), 0, |
| "Single channel transfer failed"); |
| } |
| |
| /* Verify that the DMIC can transfer from a L/R channel pair */ |
| ZTEST(dmic, test_stereo_channel) |
| { |
| dmic_cfg.channel.req_num_chan = 2; |
| dmic_cfg.channel.req_chan_map_lo = |
| dmic_build_channel_map(0, 0, PDM_CHAN_LEFT) | |
| dmic_build_channel_map(1, 0, PDM_CHAN_RIGHT); |
| dmic_cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE; |
| dmic_cfg.streams[0].block_size = |
| BLOCK_SIZE(dmic_cfg.streams[0].pcm_rate, |
| dmic_cfg.channel.req_num_chan); |
| zassert_equal(do_pdm_transfer(dmic_dev, &dmic_cfg), 0, |
| "L/R channel transfer failed"); |
| dmic_cfg.channel.req_chan_map_lo = |
| dmic_build_channel_map(0, 0, PDM_CHAN_RIGHT) | |
| dmic_build_channel_map(1, 0, PDM_CHAN_LEFT); |
| zassert_equal(do_pdm_transfer(dmic_dev, &dmic_cfg), 0, |
| "R/L channel transfer failed"); |
| } |
| |
| /* Test DMIC with maximum number of channels */ |
| ZTEST(dmic, test_max_channel) |
| { |
| enum pdm_lr lr; |
| uint8_t pdm_hw_chan; |
| |
| dmic_cfg.channel.req_num_chan = PDM_CHANNELS; |
| dmic_cfg.channel.req_chan_map_lo = 0; |
| dmic_cfg.channel.req_chan_map_hi = 0; |
| for (uint8_t i = 0; i < PDM_CHANNELS; i++) { |
| lr = ((i % 2) == 0) ? PDM_CHAN_LEFT : PDM_CHAN_RIGHT; |
| pdm_hw_chan = i >> 1; |
| if (i < 4) { |
| dmic_cfg.channel.req_chan_map_lo |= |
| dmic_build_channel_map(i, pdm_hw_chan, lr); |
| } else { |
| dmic_cfg.channel.req_chan_map_hi |= |
| dmic_build_channel_map(i, pdm_hw_chan, lr); |
| } |
| } |
| |
| dmic_cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE; |
| dmic_cfg.streams[0].block_size = |
| BLOCK_SIZE(dmic_cfg.streams[0].pcm_rate, |
| dmic_cfg.channel.req_num_chan); |
| zassert_equal(do_pdm_transfer(dmic_dev, &dmic_cfg), 0, |
| "Maximum channel transfer failed"); |
| } |
| |
| /* Test pausing and restarting a channel */ |
| ZTEST(dmic, test_pause_restart) |
| { |
| int ret, i; |
| void *buffer; |
| uint32_t size; |
| |
| dmic_cfg.channel.req_num_chan = 1; |
| dmic_cfg.channel.req_chan_map_lo = |
| dmic_build_channel_map(0, 0, PDM_CHAN_LEFT); |
| dmic_cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE; |
| dmic_cfg.streams[0].block_size = |
| BLOCK_SIZE(dmic_cfg.streams[0].pcm_rate, |
| dmic_cfg.channel.req_num_chan); |
| ret = dmic_configure(dmic_dev, &dmic_cfg); |
| zassert_equal(ret, 0, "DMIC configure failed"); |
| |
| /* Start the DMIC, and pause it immediately */ |
| ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START); |
| zassert_equal(ret, 0, "DMIC start failed"); |
| ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_PAUSE); |
| zassert_equal(ret, 0, "DMIC pause failed"); |
| /* There may be some buffers in the DMIC queue, but a read |
| * should eventually time out while it is paused |
| */ |
| for (i = 0; i < (2 * BLOCK_COUNT); i++) { |
| ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT); |
| if (ret < 0) { |
| break; |
| } |
| k_mem_slab_free(&mem_slab, buffer); |
| } |
| zassert_not_equal(ret, 0, "DMIC is paused, reads should timeout"); |
| TC_PRINT("Queue drained after %d reads\n", i); |
| /* Unpause the DMIC */ |
| ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_RELEASE); |
| zassert_equal(ret, 0, "DMIC release failed"); |
| /* Reads should not timeout now */ |
| for (i = 0; i < (2 * BLOCK_COUNT); i++) { |
| ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT); |
| if (ret < 0) { |
| break; |
| } |
| k_mem_slab_free(&mem_slab, buffer); |
| } |
| zassert_equal(ret, 0, "DMIC is active, reads should succeed"); |
| TC_PRINT("%d reads completed\n", (2 * BLOCK_COUNT)); |
| /* Stop the DMIC, and repeat the same tests */ |
| ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP); |
| zassert_equal(ret, 0, "DMIC stop failed"); |
| /* Versus a pause, DMIC reads should immediately stop once DMIC times |
| * out |
| */ |
| ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT); |
| zassert_not_equal(ret, 0, "DMIC read should timeout when DMIC is stopped"); |
| ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START); |
| zassert_equal(ret, 0, "DMIC restart failed"); |
| /* Reads should not timeout now */ |
| for (i = 0; i < (2 * BLOCK_COUNT); i++) { |
| ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT); |
| if (ret < 0) { |
| break; |
| } |
| k_mem_slab_free(&mem_slab, buffer); |
| } |
| zassert_equal(ret, 0, "DMIC is active, reads should succeed"); |
| TC_PRINT("%d reads completed\n", (2 * BLOCK_COUNT)); |
| /* Test is over. Stop the DMIC */ |
| ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP); |
| zassert_equal(ret, 0, "DMIC stop failed"); |
| } |
| |
| /* Verify that channel map without adjacent L/R pairs fails */ |
| ZTEST(dmic, test_bad_pair) |
| { |
| int ret; |
| |
| dmic_cfg.channel.req_num_chan = 2; |
| dmic_cfg.channel.req_chan_map_lo = |
| dmic_build_channel_map(0, 0, PDM_CHAN_RIGHT) | |
| dmic_build_channel_map(1, 0, PDM_CHAN_RIGHT); |
| dmic_cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE; |
| dmic_cfg.streams[0].block_size = |
| BLOCK_SIZE(dmic_cfg.streams[0].pcm_rate, |
| dmic_cfg.channel.req_num_chan); |
| ret = dmic_configure(dmic_dev, &dmic_cfg); |
| zassert_not_equal(ret, 0, "DMIC configure should fail with " |
| "two of same channel in map"); |
| |
| dmic_cfg.channel.req_num_chan = 2; |
| dmic_cfg.channel.req_chan_map_lo = |
| dmic_build_channel_map(0, 0, PDM_CHAN_RIGHT) | |
| dmic_build_channel_map(1, 1, PDM_CHAN_LEFT); |
| ret = dmic_configure(dmic_dev, &dmic_cfg); |
| zassert_not_equal(ret, 0, "DMIC configure should fail with " |
| "non adjacent channels in map"); |
| } |
| |
| ZTEST_SUITE(dmic, NULL, NULL, NULL, NULL, NULL); |