Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/dai/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ add_subdirectory_ifdef(CONFIG_DAI_INTEL_HDA intel/hda)
add_subdirectory_ifdef(CONFIG_DAI_NXP_SAI nxp/sai)
add_subdirectory_ifdef(CONFIG_DAI_NXP_ESAI nxp/esai)
add_subdirectory_ifdef(CONFIG_DAI_NXP_MICFIL nxp/micfil)
add_subdirectory_ifdef(CONFIG_DAI_VIRTUAL virtual)
1 change: 1 addition & 0 deletions drivers/dai/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ source "drivers/dai/intel/hda/Kconfig.hda"
source "drivers/dai/nxp/sai/Kconfig.sai"
source "drivers/dai/nxp/esai/Kconfig.esai"
source "drivers/dai/nxp/micfil/Kconfig.micfil"
source "drivers/dai/virtual/Kconfig.virtual"

endif # DAI
5 changes: 5 additions & 0 deletions drivers/dai/virtual/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright (c) 2025 Suraj Sonawane <[email protected]>
# SPDX-License-Identifier: Apache-2.0

zephyr_library()
zephyr_library_sources_ifdef(CONFIG_DAI_VIRTUAL virtual_dai.c)
11 changes: 11 additions & 0 deletions drivers/dai/virtual/Kconfig.virtual
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright (c) 2025, Suraj Sonawane <[email protected]@gmail.com>
# SPDX-License-Identifier: Apache-2.0

config DAI_VIRTUAL
bool "Virtual DAI driver"
depends on DT_HAS_ZEPHYR_VIRTUAL_DAI_ENABLED
help
Select this to enable support for a Virtual DAI driver.
This driver is useful for debugging and rapid prototyping without
real hardware. It simulates audio input/output behavior in software
and is useful in memory-to-memory pipelines.
112 changes: 112 additions & 0 deletions drivers/dai/virtual/virtual_dai.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2025 Suraj Sonawane <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/dai.h>
#include <zephyr/pm/device_runtime.h>
#include <string.h>

#define DT_DRV_COMPAT zephyr_virtual_dai
LOG_MODULE_REGISTER(virtual_dai, CONFIG_DAI_LOG_LEVEL);

struct virtual_dai_data {
struct dai_config cfg;
};

static int virtual_dai_probe(const struct device *dev)
{
return 0;
}

static int virtual_dai_remove(const struct device *dev)
{
return 0;
}

static int virtual_dai_config_set(const struct device *dev,
const struct dai_config *cfg,
const void *bespoke_data)
{
if (cfg->type != DAI_VIRTUAL) {
LOG_ERR("wrong DAI type: %d", cfg->type);
return -EINVAL;
}

return 0;
}

static int virtual_dai_config_get(const struct device *dev,
struct dai_config *cfg,
enum dai_dir dir)
{
struct virtual_dai_data *data = dev->data;

/* dump content of the DAI configuration */
memcpy(cfg, &data->cfg, sizeof(*cfg));

return 0;
}

static const struct dai_properties *
virtual_dai_get_properties(const struct device *dev, enum dai_dir dir, int stream_id)
{
return NULL;
}

static int virtual_dai_trigger(const struct device *dev,
enum dai_dir dir,
enum dai_trigger_cmd cmd)
{
switch (cmd) {
case DAI_TRIGGER_START:
LOG_DBG("virtual_dai: START (dir=%d)", dir);
return 0;
case DAI_TRIGGER_STOP:
LOG_DBG("virtual_dai: STOP (dir=%d)", dir);
return 0;
case DAI_TRIGGER_PAUSE:
LOG_DBG("virtual_dai: PAUSE (dir=%d)", dir);
return 0;
case DAI_TRIGGER_PRE_START:
case DAI_TRIGGER_COPY:
LOG_DBG("virtual_dai: DAI_TRIGGER_COPY");
return 0;
default:
LOG_WRN("virtual_dai: Unknown trigger %d", cmd);
return -EINVAL;
}

CODE_UNREACHABLE;
}

static DEVICE_API(dai, virtual_dai_api) = {
.probe = virtual_dai_probe,
.remove = virtual_dai_remove,
.config_set = virtual_dai_config_set,
.config_get = virtual_dai_config_get,
.get_properties = virtual_dai_get_properties,
.trigger = virtual_dai_trigger,
};

static int virtual_dai_init(const struct device *dev)
{
return 0;
}

#define VIRTUAL_DAI_INIT(inst) \
static struct virtual_dai_data virtual_dai_data_##inst = { \
.cfg.type = DAI_VIRTUAL, \
.cfg.dai_index = DT_INST_PROP_OR(inst, dai_index, 0), \
}; \
\
DEVICE_DT_INST_DEFINE(inst, \
&virtual_dai_init, NULL, \
&virtual_dai_data_##inst, NULL, \
POST_KERNEL, CONFIG_DAI_INIT_PRIORITY, \
&virtual_dai_api);
Comment on lines +101 to +110
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static struct virtual_dai_data virtual_dai_data_##inst = { \
.cfg.type = DAI_VIRTUAL, \
.cfg.dai_index = DT_INST_PROP_OR(inst, dai_index, 0), \
}; \
\
DEVICE_DT_INST_DEFINE(inst, \
&virtual_dai_init, NULL, \
&virtual_dai_data_##inst, NULL, \
POST_KERNEL, CONFIG_DAI_INIT_PRIORITY, \
&virtual_dai_api);
static struct virtual_dai_data virtual_dai_data_##inst = { \
.cfg.type = DAI_VIRTUAL, \
.cfg.dai_index = DT_INST_PROP_OR(inst, dai_index, 0), \
}; \
\
DEVICE_DT_INST_DEFINE(inst, \
&virtual_dai_init, NULL, \
&virtual_dai_data_##inst, NULL, \
POST_KERNEL, CONFIG_DAI_INIT_PRIORITY, \
&virtual_dai_api);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rruuaanng Thanks for the suggestion. Can you clarify the reason for these whitespace changes?


DT_INST_FOREACH_STATUS_OKAY(VIRTUAL_DAI_INIT);
18 changes: 18 additions & 0 deletions dts/bindings/dai/zephyr,virtual-dai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2025 Suraj Sonawane <[email protected]>
# SPDX-License-Identifier: Apache-2.0

title: Zephyr binding for a Virtual DAI (Digital Audio Interface)

description: |
This DAI is not connected to real hardware and is used for software-only
audio routing or testing within SOF + Zephyr systems.

compatible: "zephyr,virtual-dai"

include: base.yaml

properties:
dai-index:
type: int
description: |
DAI index used to identify this virtual DAI in IPC and topology.
1 change: 1 addition & 0 deletions include/zephyr/drivers/dai.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ enum dai_type {
DAI_INTEL_HDA_NHLT, /**< nhlt Intel HD/A */
DAI_INTEL_ALH_NHLT, /**< nhlt Intel ALH */
DAI_IMX_MICFIL, /**< i.MX PDM MICFIL */
DAI_VIRTUAL, /**< Virtual DAI*/
};

/**
Expand Down
10 changes: 10 additions & 0 deletions tests/drivers/dai/virtual_dai_api/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2025 Suraj Sonawane <[email protected]>
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(virtual_dai_api)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
1 change: 1 addition & 0 deletions tests/drivers/dai/virtual_dai_api/boards/native_sim.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_DAI_VIRTUAL=y
12 changes: 12 additions & 0 deletions tests/drivers/dai/virtual_dai_api/boards/native_sim.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright (c) 2025, Suraj Sonawane <[email protected]>
* SPDX-License-Identifier: Apache-2.0
*/

/ {
virtual_dai: virtual_dai {
compatible = "zephyr,virtual-dai";
dai-index = <7>;
status = "okay";
};
};
3 changes: 3 additions & 0 deletions tests/drivers/dai/virtual_dai_api/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_ZTEST=y
CONFIG_DAI=y
CONFIG_LOG=y
110 changes: 110 additions & 0 deletions tests/drivers/dai/virtual_dai_api/src/test_virtual_dai.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2025 Suraj Sonawane <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/ztest.h>
#include <zephyr/drivers/dai.h>
#include <zephyr/device.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(test_virtual_dai, CONFIG_DAI_LOG_LEVEL);

/* Get the virtual DAI device */
static const struct device *const virtual_dai_dev = DEVICE_DT_GET(DT_NODELABEL(virtual_dai));

ZTEST_SUITE(virtual_dai, NULL, NULL, NULL, NULL, NULL);

/* Test 1: Verify device exists and is ready */
ZTEST(virtual_dai, test_device_exists)
{
zassert_not_null(virtual_dai_dev, "Virtual DAI device should exist");
zassert_true(device_is_ready(virtual_dai_dev), "Device should be ready");
}

/* Test 2: Test dai_config_set using values from dai_config_get */
ZTEST(virtual_dai, test_dai_config_set_using_retrieved_config)
{
struct dai_config config;
int ret;

/* First get the configuration */
ret = dai_config_get(virtual_dai_dev, &config, 0);
zassert_ok(ret, "dai_config_get should succeed");

/* Log the configuration */
LOG_INF("Config: type=%d, dai_index=%d, rate=%d, channels=%d",
config.type, config.dai_index);

/* Set the configuration */
ret = dai_config_set(virtual_dai_dev, &config, NULL);
zassert_ok(ret, "dai_config_set should return success (0)");
}

/* Test 3: Test dai_config_set with invalid type (should fail) */
ZTEST(virtual_dai, test_dai_config_set_invalid_type)
{
struct dai_config config;
struct dai_config invalid_config;
int ret;

/* Get the current configuration to see what type is valid */
ret = dai_config_get(virtual_dai_dev, &config, 0);
zassert_ok(ret, "dai_config_get should succeed");

/* Create an invalid configuration (use a different type) */
invalid_config.type = config.type + 100; /* Invalid type */
invalid_config.dai_index = config.dai_index;

/* This should fail with -EINVAL */
ret = dai_config_set(virtual_dai_dev, &invalid_config, NULL);
zassert_equal(ret, -EINVAL, "dai_config_set should return -EINVAL for invalid type");
}

/* Test 4: Test dai_trigger commands */
ZTEST(virtual_dai, test_dai_trigger_commands)
{
int ret;

/* Test START trigger*/
ret = dai_trigger(virtual_dai_dev, 0, DAI_TRIGGER_START); /* dir = 0 (TX) */
zassert_ok(ret, "START trigger should return success (0)");

/* Test STOP trigger*/
ret = dai_trigger(virtual_dai_dev, 0, DAI_TRIGGER_STOP);
zassert_ok(ret, "STOP trigger should return success (0)");

/* Test PAUSE trigger*/
ret = dai_trigger(virtual_dai_dev, 0, DAI_TRIGGER_PAUSE);
zassert_ok(ret, "PAUSE trigger should return success (0)");

/* Test COPY trigger*/
ret = dai_trigger(virtual_dai_dev, 0, DAI_TRIGGER_COPY);
zassert_ok(ret, "COPY trigger should return success (0)");

/* Test invalid trigger command */
ret = dai_trigger(virtual_dai_dev, 0, 99); /* Invalid command */
zassert_equal(ret, -EINVAL, "Should return -EINVAL for invalid trigger");
}

/* Test 5: Test dai_get_properties */
ZTEST(virtual_dai, test_dai_get_properties)
{
const struct dai_properties *props;

props = dai_get_properties(virtual_dai_dev, 0, 0); /* dir = 0 (TX), stream_id = 0 */
zassert_is_null(props, "dai_get_properties should return NULL");
}

/* Test 6: Test dai_probe and dai_remove functions */
ZTEST(virtual_dai, test_probe_remove)
{
int ret;

ret = dai_probe(virtual_dai_dev);
zassert_ok(ret, "Probe should succeed");

ret = dai_remove(virtual_dai_dev);
zassert_ok(ret, "Remove should succeed");
}
7 changes: 7 additions & 0 deletions tests/drivers/dai/virtual_dai_api/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
common:
tags:
- dai
- drivers
tests:
drivers.dai.virtual_dai_api:
depends_on: dai