diff --git a/app/boards/mt8196_mt8196_adsp.conf b/app/boards/mt8196_mt8196_adsp.conf index 0d44864b6144..e2aca8f4aed2 100644 --- a/app/boards/mt8196_mt8196_adsp.conf +++ b/app/boards/mt8196_mt8196_adsp.conf @@ -4,3 +4,8 @@ # Board-level config goes in Zephyr (and ideally in DTS). App-level # config goes in prj.conf. CONFIG_MTK=y +CONFIG_RIMAGE_SIGNING_SCHEMA="mt8196" +CONFIG_ZEPHYR_NATIVE_DRIVERS=y +CONFIG_DMA=y +CONFIG_DAI=y + diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 9bf4e02f9223..15c39c19e33d 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -208,6 +208,10 @@ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, cfg_params = spec_config; dai_set_link_hda_config(&cfg.link_config, common_config, spec_config); break; + case SOF_DAI_MEDIATEK_AFE: + cfg.type = DAI_MEDIATEK_AFE; + cfg_params = spec_config; + break; default: return -EINVAL; } diff --git a/src/drivers/mediatek/afe/zephyr_mtk_afe_memif.c b/src/drivers/mediatek/afe/zephyr_mtk_afe_memif.c new file mode 100644 index 000000000000..f4da83f6925e --- /dev/null +++ b/src/drivers/mediatek/afe/zephyr_mtk_afe_memif.c @@ -0,0 +1,410 @@ +/* + * Copyright 2026 MediaTek + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#define DT_DRV_COMPAT mediatek_afe_memif_dma + +LOG_MODULE_REGISTER(mtk_afe, CONFIG_SOF_LOG_LEVEL); + +struct mtk_memif_chan_data { + int direction; /* 1 = downlink/playback, 0 = uplink/capture */ + int memif_id; + int dai_id; + int irq_id; + struct mtk_base_afe *afe; + + uint32_t dma_base; + uint32_t dma_size; + uint32_t rptr; + uint32_t wptr; + uint32_t period_size; + + unsigned int channel; + unsigned int rate; + unsigned int format; + + enum { + MEMIF_STATE_INIT = 0, + MEMIF_STATE_CONFIGURED, + MEMIF_STATE_ACTIVE, + MEMIF_STATE_PAUSED, + } state; +}; + +struct mtk_memif_dma_cfg { + uint32_t afe_base_reg; + uint32_t num_channels; +}; + +struct mtk_memif_dma_data { + struct dma_context ctx; + atomic_t channel_flags; + struct mtk_memif_chan_data *channels; + struct mtk_base_afe *afe; + bool afe_probed; +}; + +static int mtk_memif_config(const struct device *dev, uint32_t chan_id, + struct dma_config *config) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + int direction, dai_id, irq_id, ret; + uint32_t dma_addr = 0; + uint32_t dma_size = 0; + + if (chan_id >= data->ctx.dma_channels) { + LOG_ERR("channel %d out of range", chan_id); + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + if (!data->afe_probed) { + struct mtk_base_afe *afe = afe_get(); + + ret = afe_probe(afe); + if (ret < 0) { + LOG_ERR("afe_probe failed: %d", ret); + return ret; + } + data->afe = afe; + data->afe_probed = true; + /* Initialize all channels with afe reference */ + for (uint32_t i = 0; i < data->ctx.dma_channels; i++) { + data->channels[i].afe = afe; + data->channels[i].memif_id = i; + } + } + + direction = afe_memif_get_direction(chan->afe, chan->memif_id); + + /* dma_slot is only 8 bits in Zephyr's struct dma_config, so only the + * low byte of the AFE handshake fits. The low byte contains the + * dai_id (AFE_HS_GET_DAI extracts bits [7:0]). The memif_index + * (bits [23:16]) doesn't fit but isn't needed here since chan_id + * already maps to the correct memif via chan_filter. + */ + dai_id = config->dma_slot & 0xFF; /* AFE_HS_GET_DAI equivalent */ + irq_id = 0; /* IRQ not used in Zephyr mode */ + + switch (config->channel_direction) { + case MEMORY_TO_PERIPHERAL: + if (direction != 1) { + LOG_ERR("channel %d is not a playback memif", chan_id); + return -EINVAL; + } + dma_addr = config->head_block->source_address; + break; + case PERIPHERAL_TO_MEMORY: + if (direction != 0) { + LOG_ERR("channel %d is not a capture memif", chan_id); + return -EINVAL; + } + dma_addr = config->head_block->dest_address; + break; + default: + LOG_ERR("unsupported direction: %d", config->channel_direction); + return -EINVAL; + } + + if (!config->head_block) { + LOG_ERR("no block config provided"); + return -EINVAL; + } + + if (!config->cyclic) { + LOG_ERR("only cyclic configurations are supported"); + return -ENOTSUP; + } + + dma_size = config->head_block->block_size * config->block_count; + + chan->dai_id = dai_id; + chan->irq_id = irq_id; + chan->dma_base = dma_addr; + chan->dma_size = dma_size; + chan->direction = direction; + chan->rptr = 0; + chan->wptr = 0; + chan->period_size = config->head_block->block_size; + + ret = afe_dai_get_config(chan->afe, dai_id, + &chan->channel, &chan->rate, &chan->format); + if (ret < 0) { + LOG_ERR("failed to get DAI config for dai %d", dai_id); + return ret; + } + + switch (config->source_data_size) { + case 2: + chan->format = SOF_IPC_FRAME_S16_LE; + break; + case 4: + chan->format = SOF_IPC_FRAME_S32_LE; + break; + default: + LOG_ERR("unsupported data width: %d", config->source_data_size); + return -ENOTSUP; + } + + ret = afe_memif_set_params(chan->afe, chan->memif_id, + chan->channel, chan->rate, chan->format); + if (ret < 0) { + return ret; + } + + ret = afe_memif_set_addr(chan->afe, chan->memif_id, + chan->dma_base, chan->dma_size); + if (ret < 0) { + return ret; + } + + chan->state = MEMIF_STATE_CONFIGURED; + + LOG_DBG("channel %d configured: memif=%d dai=%d base=0x%x size=%u", + chan_id, chan->memif_id, dai_id, dma_addr, dma_size); + + return 0; +} + +static int mtk_memif_start(const struct device *dev, uint32_t chan_id) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + if (chan->state != MEMIF_STATE_CONFIGURED && + chan->state != MEMIF_STATE_PAUSED) { + LOG_ERR("channel %d not in valid state for start: %d", + chan_id, chan->state); + return -EINVAL; + } + + chan->state = MEMIF_STATE_ACTIVE; + return afe_memif_set_enable(chan->afe, chan->memif_id, 1); +} + +static int mtk_memif_stop(const struct device *dev, uint32_t chan_id) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + switch (chan->state) { + case MEMIF_STATE_CONFIGURED: + return 0; + case MEMIF_STATE_PAUSED: + case MEMIF_STATE_ACTIVE: + break; + default: + return -EINVAL; + } + + chan->state = MEMIF_STATE_CONFIGURED; + return afe_memif_set_enable(chan->afe, chan->memif_id, 0); +} + +static int mtk_memif_suspend(const struct device *dev, uint32_t chan_id) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + if (chan->state != MEMIF_STATE_ACTIVE) { + return -EINVAL; + } + + chan->state = MEMIF_STATE_PAUSED; + return afe_memif_set_enable(chan->afe, chan->memif_id, 0); +} + +static int mtk_memif_resume(const struct device *dev, uint32_t chan_id) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + if (chan->state != MEMIF_STATE_PAUSED) { + return -EINVAL; + } + + chan->state = MEMIF_STATE_ACTIVE; + return afe_memif_set_enable(chan->afe, chan->memif_id, 1); +} + +static int mtk_memif_reload(const struct device *dev, uint32_t chan_id, + uint32_t src, uint32_t dst, size_t size) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + /* update software pointer tracking */ + if (chan->direction) { + chan->wptr = (chan->wptr + size) % chan->dma_size; + } else { + chan->rptr = (chan->rptr + size) % chan->dma_size; + } + + return 0; +} + +static int mtk_memif_get_status(const struct device *dev, uint32_t chan_id, + struct dma_status *stat) +{ + struct mtk_memif_dma_data *data = dev->data; + struct mtk_memif_chan_data *chan; + uint32_t hw_ptr; + + if (chan_id >= data->ctx.dma_channels) { + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + hw_ptr = afe_memif_get_cur_position(chan->afe, chan->memif_id); + if (!hw_ptr) { + stat->read_position = 0; + stat->write_position = 0; + return -EINVAL; + } + + hw_ptr -= chan->dma_base; + + /* Validate hw_ptr is within buffer range */ + if (hw_ptr >= chan->dma_size) { + LOG_WRN("hw_ptr 0x%x out of range (size %u), clamping", + hw_ptr, chan->dma_size); + hw_ptr %= chan->dma_size; + } + + if (chan->direction) { + chan->rptr = hw_ptr; + } else { + chan->wptr = hw_ptr; + } + + stat->read_position = chan->rptr + chan->dma_base; + stat->write_position = chan->wptr + chan->dma_base; + stat->busy = (chan->state == MEMIF_STATE_ACTIVE); + + uint32_t avail = (chan->wptr + chan->dma_size - chan->rptr) % chan->dma_size; + + stat->pending_length = avail; + stat->free = chan->dma_size - avail; + + return 0; +} + +static int mtk_memif_get_attribute(const struct device *dev, + uint32_t type, uint32_t *value) +{ + switch (type) { + case DMA_ATTR_COPY_ALIGNMENT: + case DMA_ATTR_BUFFER_SIZE_ALIGNMENT: + *value = 4; + break; + case DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT: + *value = CONFIG_DCACHE_LINE_SIZE; + break; + case DMA_ATTR_MAX_BLOCK_COUNT: + *value = 4; + break; + default: + return -ENOENT; + } + return 0; +} + +/* Channel filter: ensures dma_request_channel() returns the specific + * channel (memif index) requested via the stream_tag parameter, rather + * than an arbitrary free channel. Each AFE MEMIF channel corresponds to + * a specific hardware MEMIF, so channel IDs must match memif indices. + */ +static bool mtk_memif_chan_filter(const struct device *dev, int channel, + void *filter_param) +{ + uint32_t requested = *(uint32_t *)filter_param; + + return channel == requested; +} + +static DEVICE_API(dma, mtk_afe_memif_api) = { + .config = mtk_memif_config, + .start = mtk_memif_start, + .stop = mtk_memif_stop, + .suspend = mtk_memif_suspend, + .resume = mtk_memif_resume, + .reload = mtk_memif_reload, + .get_status = mtk_memif_get_status, + .get_attribute = mtk_memif_get_attribute, + .chan_filter = mtk_memif_chan_filter, +}; + +#define MTK_AFE_MEMIF_CHAN_DECLARE(idx) {} + +#define MTK_AFE_MEMIF_INIT(inst) \ + static struct mtk_memif_chan_data \ + mtk_memif_channels_##inst[DT_INST_PROP(inst, dma_channels)]; \ + \ + static struct mtk_memif_dma_data mtk_memif_data_##inst = { \ + .ctx.magic = DMA_MAGIC, \ + .ctx.dma_channels = DT_INST_PROP(inst, dma_channels), \ + .channels = mtk_memif_channels_##inst, \ + }; \ + \ + static const struct mtk_memif_dma_cfg mtk_memif_cfg_##inst = { \ + .afe_base_reg = DT_INST_REG_ADDR(inst), \ + .num_channels = DT_INST_PROP(inst, dma_channels), \ + }; \ + \ + static int mtk_memif_init_##inst(const struct device *dev) \ + { \ + struct mtk_memif_dma_data *data = dev->data; \ + \ + data->channel_flags = ATOMIC_INIT(0); \ + data->ctx.atomic = &data->channel_flags; \ + return 0; \ + } \ + \ + DEVICE_DT_INST_DEFINE(inst, mtk_memif_init_##inst, NULL, \ + &mtk_memif_data_##inst, \ + &mtk_memif_cfg_##inst, \ + PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, \ + &mtk_afe_memif_api); + +DT_INST_FOREACH_STATUS_OKAY(MTK_AFE_MEMIF_INIT) diff --git a/src/drivers/mediatek/afe/zephyr_mtk_dai.c b/src/drivers/mediatek/afe/zephyr_mtk_dai.c new file mode 100644 index 000000000000..28d43e69a80f --- /dev/null +++ b/src/drivers/mediatek/afe/zephyr_mtk_dai.c @@ -0,0 +1,162 @@ +/* + * Copyright 2026 MediaTek + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DT_DRV_COMPAT mediatek_afe + +LOG_MODULE_REGISTER(dai_mtk_afe); + +struct dai_mtk_afe_cfg { + uint32_t dai_index; + uint32_t memif_index; + bool downlink; +}; + +struct dai_mtk_afe_data { + struct dai_config cfg; + struct dai_properties tx_props; + struct dai_properties rx_props; +}; + +static int dai_mtk_afe_probe(const struct device *dev) +{ + LOG_DBG("%s: probe", dev->name); + return 0; +} + +static int dai_mtk_afe_remove(const struct device *dev) +{ + LOG_DBG("%s: remove", dev->name); + return 0; +} + +static int dai_mtk_afe_config_set(const struct device *dev, + const struct dai_config *cfg, + const void *bespoke_cfg, size_t size) +{ + const struct dai_mtk_afe_cfg *dev_cfg = dev->config; + struct dai_mtk_afe_data *data = dev->data; + const struct sof_ipc_dai_config *sof_cfg = bespoke_cfg; + struct mtk_base_afe *afe; + int ret; + + if (cfg) { + data->cfg.channels = cfg->channels; + data->cfg.rate = cfg->rate; + data->cfg.format = cfg->format; + data->cfg.word_size = cfg->word_size; + } + + if (sof_cfg) { + int probe_ret; + + afe = afe_get(); + probe_ret = afe_probe(afe); + if (probe_ret < 0) { + LOG_ERR("%s: afe_probe failed: %d", dev->name, probe_ret); + return probe_ret; + } + ret = afe_dai_set_config(afe, dev_cfg->dai_index, + sof_cfg->afe.dai_channels, + sof_cfg->afe.dai_rate, + sof_cfg->afe.dai_format); + if (ret < 0) { + LOG_ERR("%s: afe_dai_set_config failed: %d", + dev->name, ret); + return ret; + } + + LOG_DBG("%s: config_set dai=%u ch=%u rate=%u fmt=%u", + dev->name, dev_cfg->dai_index, + sof_cfg->afe.dai_channels, + sof_cfg->afe.dai_rate, + sof_cfg->afe.dai_format); + } + + return 0; +} + +static int dai_mtk_afe_config_get(const struct device *dev, + struct dai_config *cfg, enum dai_dir dir) +{ + const struct dai_mtk_afe_cfg *dev_cfg = dev->config; + struct dai_mtk_afe_data *data = dev->data; + + if (!cfg) { + return -EINVAL; + } + + *cfg = data->cfg; + cfg->type = DAI_MEDIATEK_AFE; + cfg->dai_index = dev_cfg->dai_index; + + return 0; +} + +static const struct dai_properties *dai_mtk_afe_get_properties( + const struct device *dev, enum dai_dir dir, int stream_id) +{ + const struct dai_mtk_afe_data *data = dev->data; + + if (dir == DAI_DIR_TX) { + return &data->tx_props; + } + return &data->rx_props; +} + +static int dai_mtk_afe_trigger(const struct device *dev, + enum dai_dir dir, enum dai_trigger_cmd cmd) +{ + LOG_DBG("%s: trigger dir=%d cmd=%d", dev->name, dir, cmd); + return 0; +} + +static DEVICE_API(dai, dai_mtk_afe_api) = { + .probe = dai_mtk_afe_probe, + .remove = dai_mtk_afe_remove, + .config_set = dai_mtk_afe_config_set, + .config_get = dai_mtk_afe_config_get, + .get_properties = dai_mtk_afe_get_properties, + .trigger = dai_mtk_afe_trigger, +}; + +#define DAI_MTK_AFE_HANDSHAKE(inst) \ + ((inst) << 16) | DT_INST_PROP(inst, dai_id) + +#define DAI_MTK_AFE_INIT(inst) \ + static struct dai_mtk_afe_data dai_mtk_afe_data_##inst = { \ + .cfg = { \ + .type = DAI_MEDIATEK_AFE, \ + .dai_index = DT_INST_PROP(inst, dai_id), \ + }, \ + .tx_props = { \ + .dma_hs_id = DAI_MTK_AFE_HANDSHAKE(inst), \ + }, \ + .rx_props = { \ + .dma_hs_id = DAI_MTK_AFE_HANDSHAKE(inst), \ + }, \ + }; \ + \ + static const struct dai_mtk_afe_cfg dai_mtk_afe_cfg_##inst = { \ + .dai_index = DT_INST_PROP(inst, dai_id), \ + .memif_index = inst, \ + .downlink = DT_INST_PROP(inst, downlink), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, NULL, NULL, \ + &dai_mtk_afe_data_##inst, \ + &dai_mtk_afe_cfg_##inst, \ + POST_KERNEL, CONFIG_DAI_INIT_PRIORITY, \ + &dai_mtk_afe_api); + +DT_INST_FOREACH_STATUS_OKAY(DAI_MTK_AFE_INIT) diff --git a/src/drivers/mediatek/afe/zephyr_mtk_host_dma.c b/src/drivers/mediatek/afe/zephyr_mtk_host_dma.c new file mode 100644 index 000000000000..c918e53f7cc5 --- /dev/null +++ b/src/drivers/mediatek/afe/zephyr_mtk_host_dma.c @@ -0,0 +1,218 @@ +/* + * Copyright 2026 MediaTek + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +/* used for driver binding */ +#define DT_DRV_COMPAT mediatek_sof_host_dma + +/* macros used to parse DTS properties */ +#define IDENTITY_VARGS(V, ...) IDENTITY(V) + +#define _SOF_HOST_DMA_CHANNEL_INDEX_ARRAY(inst)\ + LISTIFY(DT_INST_PROP_OR(inst, dma_channels, 0), IDENTITY_VARGS, (,)) + +#define _SOF_HOST_DMA_CHANNEL_DECLARE(idx) {} + +#define SOF_HOST_DMA_CHANNELS_DECLARE(inst)\ + FOR_EACH(_SOF_HOST_DMA_CHANNEL_DECLARE,\ + (,), _SOF_HOST_DMA_CHANNEL_INDEX_ARRAY(inst)) + +LOG_MODULE_REGISTER(mtk_sof_host_dma); + +/* Software-based DMA driver for MTK SOF host component. + * MTK DSP can access host memory directly, so this driver + * implements DMA transfers using memcpy + cache management. + */ + +enum channel_state { + CHAN_STATE_INIT = 0, + CHAN_STATE_CONFIGURED, +}; + +struct sof_host_dma_channel { + uint32_t src; + uint32_t dest; + uint32_t size; + uint32_t direction; + enum channel_state state; +}; + +struct sof_host_dma_data { + struct dma_context ctx; + atomic_t channel_flags; + struct sof_host_dma_channel *channels; +}; + +static int channel_change_state(struct sof_host_dma_channel *chan, + enum channel_state next) +{ + switch (chan->state) { + case CHAN_STATE_INIT: + case CHAN_STATE_CONFIGURED: + if (next != CHAN_STATE_CONFIGURED) { + return -EPERM; + } + break; + default: + LOG_ERR("invalid channel previous state: %d", chan->state); + return -EINVAL; + } + + chan->state = next; + return 0; +} + +static int mtk_host_dma_reload(const struct device *dev, uint32_t chan_id, + uint32_t src, uint32_t dst, size_t size) +{ + return 0; +} + +static int mtk_host_dma_config(const struct device *dev, uint32_t chan_id, + struct dma_config *config) +{ + struct sof_host_dma_data *data = dev->data; + struct sof_host_dma_channel *chan; + int ret; + + if (chan_id >= data->ctx.dma_channels) { + LOG_ERR("channel %d is not a valid channel ID", chan_id); + return -EINVAL; + } + + chan = &data->channels[chan_id]; + + ret = channel_change_state(chan, CHAN_STATE_CONFIGURED); + if (ret < 0) { + LOG_ERR("failed to change channel %d's state to CONFIGURED", chan_id); + return ret; + } + + if (config->block_count != 1) { + LOG_ERR("invalid number of blocks: %d", config->block_count); + return -EINVAL; + } + + if (!config->head_block->source_address) { + LOG_ERR("got NULL source address"); + return -EINVAL; + } + + if (!config->head_block->dest_address) { + LOG_ERR("got NULL destination address"); + return -EINVAL; + } + + if (!config->head_block->block_size) { + LOG_ERR("got 0 bytes to copy"); + return -EINVAL; + } + + if (config->channel_direction != HOST_TO_MEMORY && + config->channel_direction != MEMORY_TO_HOST) { + LOG_ERR("invalid channel direction: %d", config->channel_direction); + return -EINVAL; + } + + chan->src = config->head_block->source_address; + chan->dest = config->head_block->dest_address; + chan->size = config->head_block->block_size; + chan->direction = config->channel_direction; + + if (chan->direction == HOST_TO_MEMORY) { + sys_cache_data_invd_range(UINT_TO_POINTER(chan->src), chan->size); + } + + memcpy(UINT_TO_POINTER(chan->dest), UINT_TO_POINTER(chan->src), chan->size); + sys_cache_data_flush_range(UINT_TO_POINTER(chan->dest), chan->size); + + return 0; +} + +static int mtk_host_dma_start(const struct device *dev, uint32_t chan_id) +{ + return 0; +} + +static int mtk_host_dma_stop(const struct device *dev, uint32_t chan_id) +{ + return 0; +} + +static int mtk_host_dma_suspend(const struct device *dev, uint32_t chan_id) +{ + return 0; +} + +static int mtk_host_dma_resume(const struct device *dev, uint32_t chan_id) +{ + return 0; +} + +static int mtk_host_dma_get_status(const struct device *dev, + uint32_t chan_id, struct dma_status *stat) +{ + return 0; +} + +static int mtk_host_dma_get_attribute(const struct device *dev, + uint32_t type, uint32_t *val) +{ + switch (type) { + case DMA_ATTR_COPY_ALIGNMENT: + case DMA_ATTR_BUFFER_SIZE_ALIGNMENT: + *val = sizeof(uint32_t); + break; + case DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT: + *val = PLATFORM_DCACHE_ALIGN; + break; + default: + LOG_ERR("invalid attribute type: %d", type); + return -EINVAL; + } + + return 0; +} + +static DEVICE_API(dma, mtk_host_dma_api) = { + .reload = mtk_host_dma_reload, + .config = mtk_host_dma_config, + .start = mtk_host_dma_start, + .stop = mtk_host_dma_stop, + .suspend = mtk_host_dma_suspend, + .resume = mtk_host_dma_resume, + .get_status = mtk_host_dma_get_status, + .get_attribute = mtk_host_dma_get_attribute, +}; + +static int mtk_host_dma_init(const struct device *dev) +{ + struct sof_host_dma_data *data = dev->data; + + data->channel_flags = ATOMIC_INIT(0); + data->ctx.atomic = &data->channel_flags; + + return 0; +} + +static struct sof_host_dma_channel channels[] = { + SOF_HOST_DMA_CHANNELS_DECLARE(0), +}; + +static struct sof_host_dma_data mtk_host_dma_data = { + .ctx.magic = DMA_MAGIC, + .ctx.dma_channels = ARRAY_SIZE(channels), + .channels = channels, +}; + +DEVICE_DT_INST_DEFINE(0, mtk_host_dma_init, NULL, + &mtk_host_dma_data, NULL, + PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, + &mtk_host_dma_api); diff --git a/src/ipc/ipc3/host-page-table.c b/src/ipc/ipc3/host-page-table.c index 997ee6683352..e90203a79dce 100644 --- a/src/ipc/ipc3/host-page-table.c +++ b/src/ipc/ipc3/host-page-table.c @@ -98,7 +98,8 @@ static int ipc_get_page_descriptors(struct sof_dma *dmac, uint8_t *page_table, * reload() function which may not be the case for all * vendors. */ - if (!IS_ENABLED(CONFIG_DMA_NXP_SOF_HOST_DMA)) { + if (!IS_ENABLED(CONFIG_DMA_NXP_SOF_HOST_DMA) && + !IS_ENABLED(CONFIG_DMA_MTK_SOF_HOST_DMA)) { tr_err(&ipc_tr, "DMAC not supported for page transfer"); return -ENOTSUP; } diff --git a/src/lib/dai.c b/src/lib/dai.c index bd7c02edcb2d..e9e07b9202f8 100644 --- a/src/lib/dai.c +++ b/src/lib/dai.c @@ -191,6 +191,9 @@ const struct device *zephyr_dev[] = { #if CONFIG_DAI_INTEL_UAOL DT_FOREACH_STATUS_OKAY(intel_uaol_dai, GET_DEVICE_LIST) #endif +#if DT_HAS_COMPAT_STATUS_OKAY(mediatek_afe) + DT_FOREACH_STATUS_OKAY(mediatek_afe, GET_DEVICE_LIST) +#endif }; const struct device **dai_get_device_list(size_t *count) @@ -301,6 +304,9 @@ static void dai_set_device_params(struct dai *d) d->dma_dev = SOF_DMA_DEV_SW; d->dma_caps = SOF_DMA_CAP_SW; break; + case SOF_DAI_MEDIATEK_AFE: + d->dma_dev = SOF_DMA_DEV_AFE_MEMIF; + break; default: break; } diff --git a/src/platform/Kconfig b/src/platform/Kconfig index 2de2c5ed00da..89ac707dffdf 100644 --- a/src/platform/Kconfig +++ b/src/platform/Kconfig @@ -312,7 +312,7 @@ config MT8365 config MTK bool "Build for Mediatek (Zephyr)" - select SCHEDULE_DMA_MULTI_CHANNEL + select SCHEDULE_DMA_MULTI_CHANNEL if !ZEPHYR_NATIVE_DRIVERS select HOST_PTABLE help Select if your target is a Mediatek DSP. Builds Zephyr firmware. diff --git a/src/platform/mtk/dai.c b/src/platform/mtk/dai.c index 17fbc665dfbe..2e1d85a89dfc 100644 --- a/src/platform/mtk/dai.c +++ b/src/platform/mtk/dai.c @@ -1,9 +1,14 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright(c) 2024 Google LLC. All rights reserved. // Author: Andy Ross +#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS #include +#endif #include #include +#include +#include +#include /* The legacy driver stores register addresses as an offset from an * arbitrary base address (which is not actually a unified block of @@ -158,6 +163,7 @@ static struct mtk_base_memif_data afe_memifs[] = { DT_FOREACH_STATUS_OKAY(mediatek_afe, EMPTY_STRUCT) }; +#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS static struct dai mtk_dais[] = { DT_FOREACH_STATUS_OKAY(mediatek_afe, EMPTY_STRUCT) }; @@ -210,6 +216,7 @@ static const struct dai_info mtk_dai_info = { .dai_type_array = mtk_dai_types, .num_dai_types = ARRAY_SIZE(mtk_dai_types), }; +#endif /* !CONFIG_ZEPHYR_NATIVE_DRIVERS */ #if defined(CONFIG_SOC_SERIES_MT818X) || defined(CONFIG_SOC_MT8195) static unsigned int mtk_afe2adsp_addr(unsigned int addr) @@ -324,7 +331,7 @@ struct mtk_base_afe_platform mtk_afe_platform = { .memif_32bit_supported = 0, .irq_datas = NULL, .irqs_size = 0, - .dais_size = ARRAY_SIZE(mtk_dais), + .dais_size = ARRAY_SIZE(afes), .afe_fs = mtk_afe_fs, .irq_fs = mtk_afe_fs_timing, #if defined(CONFIG_SOC_SERIES_MT818X) || defined(CONFIG_SOC_MT8195) @@ -342,6 +349,7 @@ int mtk_dai_init(struct sof *sof) afe_memifs[i].id = i; cfg_convert(&afes[i], &afe_memifs[i]); +#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS /* Also initialize the dais array */ extern const struct dai_driver afe_dai_driver; @@ -362,6 +370,7 @@ int mtk_dai_init(struct sof *sof) int hs = (i << 16) | di; mtk_dais[di].plat_data.fifo[0].handshake = hs; +#endif } /* DTS stores the direction as a boolean property, but the @@ -377,7 +386,9 @@ int mtk_dai_init(struct sof *sof) for (/**/; i < ARRAY_SIZE(afes); i++) __ASSERT_NO_MSG(!afes[i].downlink); +#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS sof->dai_info = &mtk_dai_info; sof->dma_info = &mtk_dma_info; +#endif return 0; } diff --git a/src/platform/mtk/platform.c b/src/platform/mtk/platform.c index c189af1f0a16..5fbb704548fb 100644 --- a/src/platform/mtk/platform.c +++ b/src/platform/mtk/platform.c @@ -157,8 +157,13 @@ struct ipc_data_host_buffer *ipc_platform_get_host_buffer(struct ipc *ipc) int platform_ipc_init(struct ipc *ipc) { mtk_host_buffer.page_table = hostbuf_ptable; +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + mtk_host_buffer.dmac = sof_dma_get(SOF_DMA_DIR_HMEM_TO_LMEM, 0, + SOF_DMA_DEV_HOST, SOF_DMA_ACCESS_SHARED); +#else mtk_host_buffer.dmac = dma_get(DMA_DIR_HMEM_TO_LMEM, 0, DMA_DEV_HOST, DMA_ACCESS_SHARED); +#endif schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(zipc_task_uuid), &ipc_task_ops, ipc, 0, 0); @@ -210,6 +215,38 @@ void clocks_init(struct sof *sof) sof->clocks = clks; } +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS +/* + * Allocate SOF-level channel descriptor arrays (struct dma_chan_data[]) for + * each sof_dma entry. Native Zephyr DMA drivers manage their own internal + * channel state but do not allocate the SOF-layer chan[] array that + * dai-zephyr.c and host-zephyr.c rely on (e.g. dd->chan = &dd->dma->chan[i]). + * Legacy DMA drivers (dummy-dma, afe-memif) did this in their own probe(); + * in the native path we do it here after dmac_init() registers the DMA info. + */ +static void mtk_dma_chan_alloc(struct sof *sof) +{ + int i, j; + + for (i = 0; i < sof->dma_info->num_dmas; i++) { + struct sof_dma *d = &sof->dma_info->dma_array[i]; + + if (!d->plat_data.channels || d->chan) + continue; + + d->chan = rzalloc(SOF_MEM_FLAG_KERNEL, + d->plat_data.channels * sizeof(struct dma_chan_data)); + if (!d->chan) + continue; + + for (j = 0; j < d->plat_data.channels; j++) { + d->chan[j].dma = d; + d->chan[j].index = j; + } + } +} +#endif + int platform_init(struct sof *sof) { clocks_init(sof); @@ -217,11 +254,18 @@ int platform_init(struct sof *sof) sof->platform_timer_domain = zephyr_domain_init(PLATFORM_DEFAULT_CLOCK); scheduler_init_ll(sof->platform_timer_domain); mtk_dai_init(sof); +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + /* In native mode, DMA is registered in sof/zephyr/lib/dma.c */ + dmac_init(sof); + mtk_dma_chan_alloc(sof); +#endif ipc_init(sof); +#if CONFIG_SCHEDULE_DMA_MULTI_CHANNEL sof->platform_dma_domain = dma_multi_chan_domain_init(&sof->dma_info->dma_array[0], sof->dma_info->num_dmas, PLATFORM_DEFAULT_CLOCK, false); +#endif sa_init(sof, CONFIG_SYSTICK_PERIOD); return 0; } diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 91598a776db0..677c7f12d64a 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -518,11 +518,25 @@ if (CONFIG_SOC_FAMILY_MTK) zephyr_library_sources( ${SOF_PLATFORM_PATH}/mtk/platform.c ${SOF_PLATFORM_PATH}/mtk/dai.c - ${SOF_DRIVERS_PATH}/generic/dummy-dma.c - ${SOF_SRC_PATH}/drivers/mediatek/afe/afe-memif.c - ${SOF_SRC_PATH}/drivers/mediatek/afe/afe-dai.c ${SOF_SRC_PATH}/drivers/mediatek/afe/afe-drv.c ) + + if (CONFIG_ZEPHYR_NATIVE_DRIVERS) + # Native Zephyr DMA/DAI path + zephyr_library_sources( + lib/dma.c + ${SOF_SRC_PATH}/drivers/mediatek/afe/zephyr_mtk_afe_memif.c + ${SOF_SRC_PATH}/drivers/mediatek/afe/zephyr_mtk_host_dma.c + ${SOF_SRC_PATH}/drivers/mediatek/afe/zephyr_mtk_dai.c + ) + else() + # Legacy DMA path + zephyr_library_sources( + ${SOF_DRIVERS_PATH}/generic/dummy-dma.c + ${SOF_SRC_PATH}/drivers/mediatek/afe/afe-memif.c + ${SOF_SRC_PATH}/drivers/mediatek/afe/afe-dai.c + ) + endif() endif() # Building for native_posix-based whole-OS host emulator diff --git a/zephyr/Kconfig b/zephyr/Kconfig index f1d1896c4234..998abae8a969 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -169,6 +169,14 @@ config ZEPHYR_NATIVE_DRIVERS dai-zephyr will be used instead of legacy xtos version. +config DMA_MTK_SOF_HOST_DMA + bool "MediaTek SOF host DMA driver" + default y if SOC_FAMILY_MTK && ZEPHYR_NATIVE_DRIVERS + help + Software-based DMA driver for MTK SOF host component. + MTK DSP can access host memory directly, so this driver + implements DMA transfers using memcpy + cache management. + config DMA_DOMAIN bool "Enable the usage of DMA domain." help diff --git a/zephyr/include/sof/lib/dma.h b/zephyr/include/sof/lib/dma.h index 509495634097..9bfc45d8316a 100644 --- a/zephyr/include/sof/lib/dma.h +++ b/zephyr/include/sof/lib/dma.h @@ -275,7 +275,7 @@ int dmac_init(struct sof *sof); #include "sof_dma.h" #else #include "dma-legacy.h" -#endif /* !CONFIG_ZEPHYR_NATIVE_DRIVERS */ +#endif /* CONFIG_ZEPHYR_NATIVE_DRIVERS */ #if defined(CONFIG_SCHEDULE_DMA_MULTI_CHANNEL) || \ defined(CONFIG_SCHEDULE_DMA_SINGLE_CHANNEL) diff --git a/zephyr/lib/dma.c b/zephyr/lib/dma.c index 380c31d48843..9ec981ea7e8c 100644 --- a/zephyr/lib/dma.c +++ b/zephyr/lib/dma.c @@ -239,6 +239,28 @@ SHARED_DATA struct sof_dma dma[] = { .z_dev = DEVICE_DT_GET(DT_NODELABEL(acp_sdw_dma)), }, #endif +#if DT_NODE_HAS_STATUS(DT_NODELABEL(afe_memif_dma), okay) +{ + .plat_data = { + .dir = SOF_DMA_DIR_MEM_TO_DEV | SOF_DMA_DIR_DEV_TO_MEM, + .devs = SOF_DMA_DEV_AFE_MEMIF, + .channels = DT_PROP(DT_NODELABEL(afe_memif_dma), dma_channels), + .period_count = 4, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(afe_memif_dma)), +}, +#endif +#if DT_NODE_HAS_STATUS(DT_NODELABEL(host_dma), okay) +{ + .plat_data = { + .dir = SOF_DMA_DIR_HMEM_TO_LMEM | SOF_DMA_DIR_LMEM_TO_HMEM, + .devs = SOF_DMA_DEV_HOST, + .channels = DT_PROP(DT_NODELABEL(host_dma), dma_channels), + .period_count = 4, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(host_dma)), +}, +#endif }; const struct dma_info lib_dma = {