diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 921c03c68902..986f0a8f361f 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -33,15 +33,32 @@ #include #include +#include +#include + static const struct comp_driver comp_dai; +LOG_MODULE_REGISTER(dai_comp, CONFIG_SOF_LOG_LEVEL); + /* c2b00d27-ffbc-4150-a51a-245c79c5e54b */ DECLARE_SOF_RT_UUID("dai", dai_comp_uuid, 0xc2b00d27, 0xffbc, 0x4150, 0xa5, 0x1a, 0x24, 0x5c, 0x79, 0xc5, 0xe5, 0x4b); DECLARE_TR_CTX(dai_comp_tr, SOF_UUID(dai_comp_uuid), LOG_LEVEL_INFO); -static void dai_atomic_trigger(void *arg, enum notify_id type, void *data); +#if CONFIG_COMP_DAI_GROUP + +static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd); + +static void dai_atomic_trigger(void *arg, enum notify_id type, void *data) +{ + struct comp_dev *dev = arg; + struct dai_data *dd = comp_get_drvdata(dev); + struct dai_group *group = dd->group; + + /* Atomic context set by the last DAI to receive trigger command */ + group->trigger_ret = dai_comp_trigger_internal(dev, group->trigger_cmd); +} /* Assign DAI to a group */ int dai_assign_group(struct comp_dev *dev, uint32_t group_id) @@ -75,6 +92,125 @@ int dai_assign_group(struct comp_dev *dev, uint32_t group_id) return 0; } +#endif + +static int dai_trigger_op(struct dai *dai, int cmd, int direction) +{ + const struct device *dev = dai->drv; + enum dai_trigger_cmd zephyr_cmd; + + switch (cmd) { + case COMP_TRIGGER_STOP: + zephyr_cmd = DAI_TRIGGER_STOP; + break; + case COMP_TRIGGER_START: + case COMP_TRIGGER_RELEASE: + zephyr_cmd = DAI_TRIGGER_START; + break; + case COMP_TRIGGER_PAUSE: + zephyr_cmd = DAI_TRIGGER_PAUSE; + break; + case COMP_TRIGGER_PRE_START: + case COMP_TRIGGER_PRE_RELEASE: + zephyr_cmd = DAI_TRIGGER_PRE_START; + break; + default: + return -EINVAL; + } + + return dai_trigger(dev, direction, zephyr_cmd); +} + +/* called from src/ipc/ipc3/handler.c and src/ipc/ipc4/dai.c */ +int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, + void *spec_config) +{ + const struct device *dev = dai->drv; + struct sof_ipc_dai_config *sof_cfg; + struct dai_config cfg; + void *cfg_params; + bool is_blob; + + sof_cfg = spec_config; + cfg.dai_index = common_config->dai_index; + is_blob = common_config->is_config_blob; + cfg.format = sof_cfg->format; + cfg.options = sof_cfg->flags; + + switch (common_config->type) { + case SOF_DAI_INTEL_SSP: + cfg.type = is_blob ? DAI_INTEL_SSP_NHLT : DAI_INTEL_SSP; + cfg_params = is_blob ? spec_config : &sof_cfg->ssp; + break; + case SOF_DAI_INTEL_ALH: + cfg.type = is_blob ? DAI_INTEL_ALH_NHLT : DAI_INTEL_ALH; + cfg_params = is_blob ? spec_config : &sof_cfg->alh; + break; + case SOF_DAI_INTEL_DMIC: + cfg.type = is_blob ? DAI_INTEL_DMIC_NHLT : DAI_INTEL_DMIC; + cfg_params = is_blob ? spec_config : &sof_cfg->dmic; + break; + default: + return -EINVAL; + } + + return dai_config_set(dev, &cfg, cfg_params); +} + +static int dai_get_hw_params(struct dai *dai, struct sof_ipc_stream_params *params, int dir) +{ + const struct dai_config *cfg = dai_config_get(dai->drv, dir); + + params->rate = cfg->rate; + params->buffer_fmt = 0; + params->channels = cfg->channels; + + switch (cfg->word_size) { + case 16: + params->frame_fmt = SOF_IPC_FRAME_S16_LE; + break; + case 24: + params->frame_fmt = SOF_IPC_FRAME_S24_4LE; + break; + case 32: + params->frame_fmt = SOF_IPC_FRAME_S32_LE; + break; + default: + tr_warn(&dai_comp_tr, "not supported word size %u", cfg->word_size); + } + + return 0; +} + +/* called from ipc/ipc3/dai.c */ +int dai_get_handshake(struct dai *dai, int direction, int stream_id) +{ + const struct dai_properties *props = dai_get_properties(dai->drv, direction, stream_id); + + return props->dma_hs_id; +} + +/* called from ipc/ipc3/dai.c and ipc/ipc4/dai.c */ +int dai_get_fifo_depth(struct dai *dai, int direction) +{ + const struct dai_properties *props = dai_get_properties(dai->drv, direction, 0); + + return props->fifo_address; +} + +int dai_get_stream_id(struct dai *dai, int direction) +{ + const struct dai_properties *props = dai_get_properties(dai->drv, direction, 0); + + return props->stream_id; +} + +static int dai_get_fifo(struct dai *dai, int direction, int stream_id) +{ + const struct dai_properties *props = dai_get_properties(dai->drv, direction, stream_id); + + return props->fifo_address; +} /* this is called by DMA driver every time descriptor has completed */ static void dai_dma_cb(void *arg, enum notify_id type, void *data) @@ -94,7 +230,7 @@ static void dai_dma_cb(void *arg, enum notify_id type, void *data) /* stop dma copy for pause/stop/xrun */ if (dev->state != COMP_STATE_ACTIVE || dd->xrun) { /* stop the DAI */ - dai_trigger(dd->dai, COMP_TRIGGER_STOP, dev->direction); + dai_trigger_op(dd->dai, COMP_TRIGGER_STOP, dev->direction); /* tell DMA not to reload */ next->status = DMA_CB_STATUS_END; @@ -161,9 +297,9 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, void *spec) { struct comp_dev *dev; - struct ipc_config_dai *dai = spec; + struct ipc_config_dai *dai_cfg = spec; struct dai_data *dd; - uint32_t dir, caps, dma_dev; + uint32_t dir; comp_cl_dbg(&comp_dai, "dai_new()"); @@ -180,21 +316,19 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, comp_set_drvdata(dev, dd); - dd->dai = dai_get(dai->type, dai->dai_index, DAI_CREAT); + dd->dai = dai_get(dai_cfg->type, dai_cfg->dai_index, DAI_CREAT); if (!dd->dai) { comp_cl_err(&comp_dai, "dai_new(): dai_get() failed to create DAI."); goto error; } - dd->ipc_config = *dai; - /* request GP LP DMA with shared access privilege */ - dir = dai->direction == SOF_IPC_STREAM_PLAYBACK ? - DMA_DIR_MEM_TO_DEV : DMA_DIR_DEV_TO_MEM; + dd->ipc_config = *dai_cfg; - caps = dai_get_info(dd->dai, DAI_INFO_DMA_CAPS); - dma_dev = dai_get_info(dd->dai, DAI_INFO_DMA_DEV); + /* request GP LP DMA with shared access privilege */ + dir = dai_cfg->direction == SOF_IPC_STREAM_PLAYBACK ? + DMA_DIR_MEM_TO_DEV : DMA_DIR_DEV_TO_MEM; - dd->dma = dma_get(dir, caps, dma_dev, DMA_ACCESS_SHARED); + dd->dma = dma_get(dir, dd->dai->dma_caps, dd->dai->dma_dev, DMA_ACCESS_SHARED); if (!dd->dma) { comp_cl_err(&comp_dai, "dai_new(): dma_get() failed to get shared access to DMA."); goto error; @@ -267,27 +401,12 @@ static int dai_comp_get_hw_params(struct comp_dev *dev, return 0; } -static int dai_comp_hw_params(struct comp_dev *dev, - struct sof_ipc_stream_params *params) +static int dai_comp_hw_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) { - struct dai_data *dd = comp_get_drvdata(dev); - int ret; - - comp_dbg(dev, "dai_comp_hw_params()"); - - /* configure hw dai stream params */ - ret = dai_hw_params(dd->dai, params); - if (ret < 0) { - comp_err(dev, "dai_comp_hw_params(): dai_hw_params failed ret %d", - ret); - return ret; - } - return 0; } -static int dai_verify_params(struct comp_dev *dev, - struct sof_ipc_stream_params *params) +static int dai_verify_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) { struct sof_ipc_stream_params hw_params; @@ -351,8 +470,7 @@ static int dai_playback_params(struct comp_dev *dev, uint32_t period_bytes, config->dest_width = config->src_width; config->cyclic = 1; config->irq_disabled = pipeline_is_timer_driven(dev->pipeline); - config->dest_dev = dai_get_handshake(dd->dai, dev->direction, - dd->stream_id); + config->dest_dev = dai_get_handshake(dd->dai, dev->direction, dd->stream_id); config->is_scheduling_source = comp_is_scheduling_source(dev); config->period = dev->pipeline->period; @@ -469,7 +587,7 @@ static int dai_capture_params(struct comp_dev *dev, uint32_t period_bytes, config->period = dev->pipeline->period; /* TODO: Make this code platform-specific or move it driver callback */ - if (dai_get_info(dd->dai, DAI_INFO_TYPE) == SOF_DAI_INTEL_DMIC) { + if (dd->dai->type == SOF_DAI_INTEL_DMIC) { /* For DMIC the DMA src and dest widths should always be 4 bytes * due to 32 bit FIFO packer. Setting width to 2 bytes for * 16 bit format would result in recording at double rate. @@ -557,8 +675,7 @@ static int dai_capture_params(struct comp_dev *dev, uint32_t period_bytes, return err; } -static int dai_params(struct comp_dev *dev, - struct sof_ipc_stream_params *params) +static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params) { struct sof_ipc_stream_params hw_params = *params; struct dai_data *dd = comp_get_drvdata(dev); @@ -623,7 +740,7 @@ static int dai_params(struct comp_dev *dev, err = dma_get_attribute(dd->dma, DMA_ATTR_BUFFER_ALIGNMENT, &align); if (err < 0 || !align) { - comp_err(dev, "dai_params(): could not get valid dma buffer alignment, err = %d, align = %u", + comp_err(dev, "dai_params(): no valid dma buffer alignment, err = %d, align = %u", err, align); return -EINVAL; } @@ -631,7 +748,7 @@ static int dai_params(struct comp_dev *dev, err = dma_get_attribute(dd->dma, DMA_ATTR_BUFFER_PERIOD_COUNT, &period_count); if (err < 0 || !period_count) { - comp_err(dev, "dai_params(): could not get valid dma buffer period count, err = %d, period_count = %u", + comp_err(dev, "dai_params(): no valid dma buffer period count, err = %d, period_count = %u", err, period_count); return -EINVAL; } @@ -791,7 +908,6 @@ static int dai_prepare(struct comp_dev *dev) } ret = dma_config(dd->chan->dma->z_dev, dd->chan->index, dd->z_config); - if (ret < 0) comp_set_state(dev, COMP_TRIGGER_RESET); @@ -806,8 +922,8 @@ static int dai_reset(struct comp_dev *dev) comp_info(dev, "dai_reset()"); /* - * DMA channel release should be skipped now for DAI's that support the two-step stop option - * It will be done when the host sends the DAI_CONFIG IPC during hw_free. + * DMA channel release should be skipped now for DAI's that support the two-step stop + * option. It will be done when the host sends the DAI_CONFIG IPC during hw_free. */ if (!dd->delayed_dma_stop) dai_dma_release(dev); @@ -869,7 +985,7 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) return ret; /* start the DAI */ - dai_trigger(dd->dai, cmd, dev->direction); + dai_trigger_op(dd->dai, cmd, dev->direction); } else { dd->xrun = 0; } @@ -897,7 +1013,7 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) return ret; /* start the DAI */ - dai_trigger(dd->dai, cmd, dev->direction); + dai_trigger_op(dd->dai, cmd, dev->direction); ret = dma_start(dd->chan->dma->z_dev, dd->chan->index); if (ret < 0) return ret; @@ -924,16 +1040,16 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) */ #if CONFIG_DMA_SUSPEND_DRAIN ret = dma_stop(dd->chan->dma->z_dev, dd->chan->index); - dai_trigger(dd->dai, cmd, dev->direction); + dai_trigger_op(dd->dai, cmd, dev->direction); #else - dai_trigger(dd->dai, cmd, dev->direction); + dai_trigger_op(dd->dai, cmd, dev->direction); ret = dma_stop(dd->chan->dma->z_dev, dd->chan->index); #endif break; case COMP_TRIGGER_PAUSE: comp_dbg(dev, "dai_comp_trigger_internal(), PAUSE"); ret = dma_suspend(dd->chan->dma->z_dev, dd->chan->index); - dai_trigger(dd->dai, cmd, dev->direction); + dai_trigger_op(dd->dai, cmd, dev->direction); break; case COMP_TRIGGER_PRE_START: case COMP_TRIGGER_PRE_RELEASE: @@ -941,7 +1057,7 @@ static int dai_comp_trigger_internal(struct comp_dev *dev, int cmd) if (dd->xrun) dd->xrun = 0; else - dai_trigger(dd->dai, cmd, dev->direction); + dai_trigger_op(dd->dai, cmd, dev->direction); break; } @@ -1004,16 +1120,6 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) return ret; } -static void dai_atomic_trigger(void *arg, enum notify_id type, void *data) -{ - struct comp_dev *dev = arg; - struct dai_data *dd = comp_get_drvdata(dev); - struct dai_group *group = dd->group; - - /* Atomic context set by the last DAI to receive trigger command */ - group->trigger_ret = dai_comp_trigger_internal(dev, group->trigger_cmd); -} - /* report xrun occurrence */ static void dai_report_xrun(struct comp_dev *dev, uint32_t bytes) { @@ -1107,9 +1213,6 @@ static int dai_copy(struct comp_dev *dev) return 0; } - if (dd->dai->drv->ops.copy) - dd->dai->drv->ops.copy(dd->dai); - struct dma_cb_data next = { .channel = dd->chan, .elem = { .size = copy_bytes }, @@ -1143,11 +1246,11 @@ static int dai_copy(struct comp_dev *dev) * DAI must be prepared before this function is used (for DMA information). If not, an error * is returned. */ -static int dai_ts_config(struct comp_dev *dev) +static int dai_ts_config_op(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); - struct timestamp_cfg *cfg = &dd->ts_config; struct ipc_config_dai *dai = &dd->ipc_config; + struct dai_ts_cfg cfg; comp_dbg(dev, "dai_ts_config()"); if (!dd->chan) { @@ -1155,49 +1258,74 @@ static int dai_ts_config(struct comp_dev *dev) return -EINVAL; } - cfg->type = dd->dai->drv->type; - cfg->direction = dai->direction; - cfg->index = dd->dai->index; - cfg->dma_id = dd->dma->plat_data.id; - cfg->dma_chan_index = dd->chan->index; - cfg->dma_chan_count = dd->dma->plat_data.channels; - if (!dd->dai->drv->ts_ops.ts_config) - return -ENXIO; + switch (dai->type) { + case SOF_DAI_INTEL_SSP: + cfg.type = DAI_INTEL_SSP; + break; + case SOF_DAI_INTEL_ALH: + cfg.type = DAI_INTEL_ALH; + break; + case SOF_DAI_INTEL_DMIC: + cfg.type = DAI_INTEL_DMIC; + break; + default: + comp_err(dev, "dai_ts_config(), not supported dai type"); + return -EINVAL; + } + + cfg.direction = dai->direction; + cfg.index = dd->dai->index; + cfg.dma_id = dd->dma->plat_data.id; + cfg.dma_chan_index = dd->chan->index; + cfg.dma_chan_count = dd->dma->plat_data.channels; - return dd->dai->drv->ts_ops.ts_config(dd->dai, cfg); + return dai_ts_config(dd->dai->drv, &cfg); } -static int dai_ts_start(struct comp_dev *dev) +static int dai_ts_start_op(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); + struct dai_ts_cfg cfg; comp_dbg(dev, "dai_ts_start()"); - if (!dd->dai->drv->ts_ops.ts_start) - return -ENXIO; - return dd->dai->drv->ts_ops.ts_start(dd->dai, &dd->ts_config); + return dai_ts_start(dd->dai->drv, &cfg); } -static int dai_ts_stop(struct comp_dev *dev) +static int dai_ts_get_op(struct comp_dev *dev, struct timestamp_data *tsd) { struct dai_data *dd = comp_get_drvdata(dev); + struct dai_ts_data tsdata; + struct dai_ts_cfg cfg; + int ret; - comp_dbg(dev, "dai_ts_stop()"); - if (!dd->dai->drv->ts_ops.ts_stop) - return -ENXIO; + comp_dbg(dev, "dai_ts_get()"); - return dd->dai->drv->ts_ops.ts_stop(dd->dai, &dd->ts_config); + ret = dai_ts_get(dd->dai->drv, &cfg, &tsdata); + + if (ret < 0) + return ret; + + /* todo convert to timestamp_data */ + + return ret; } -static int dai_ts_get(struct comp_dev *dev, struct timestamp_data *tsd) +static int dai_ts_stop_op(struct comp_dev *dev) { struct dai_data *dd = comp_get_drvdata(dev); + struct dai_ts_cfg cfg; - comp_dbg(dev, "dai_ts_get()"); - if (!dd->dai->drv->ts_ops.ts_get) - return -ENXIO; + comp_dbg(dev, "dai_ts_stop()"); - return dd->dai->drv->ts_ops.ts_get(dd->dai, &dd->ts_config, tsd); + return dai_ts_stop(dd->dai->drv, &cfg); +} + +uint32_t dai_get_init_delay_ms(struct dai *dai) +{ + const struct dai_properties *props = dai_get_properties(dai->drv, 0, 0); + + return props->reg_init_delay; } static const struct comp_driver comp_dai = { @@ -1213,12 +1341,12 @@ static const struct comp_driver comp_dai = { .copy = dai_copy, .prepare = dai_prepare, .reset = dai_reset, - .dai_config = dai_config, .position = dai_position, - .dai_ts_config = dai_ts_config, - .dai_ts_start = dai_ts_start, - .dai_ts_stop = dai_ts_stop, - .dai_ts_get = dai_ts_get, + .dai_config = dai_config, + .dai_ts_config = dai_ts_config_op, + .dai_ts_start = dai_ts_start_op, + .dai_ts_stop = dai_ts_stop_op, + .dai_ts_get = dai_ts_get_op, }, }; @@ -1228,8 +1356,7 @@ static SHARED_DATA struct comp_driver_info comp_dai_info = { UT_STATIC void sys_comp_dai_init(void) { - comp_register(platform_shared_get(&comp_dai_info, - sizeof(comp_dai_info))); + comp_register(platform_shared_get(&comp_dai_info, sizeof(comp_dai_info))); } DECLARE_MODULE(sys_comp_dai_init); diff --git a/src/include/sof/lib/dai-legacy.h b/src/include/sof/lib/dai-legacy.h new file mode 100644 index 000000000000..90d2667429c1 --- /dev/null +++ b/src/include/sof/lib/dai-legacy.h @@ -0,0 +1,566 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Keyon Jie + * + * Renamed from dai.h to dai-legacy.h because of Zephyr refactoring and + * the file content has not changed. + */ + +/** + * \file include/sof/lib/dai.h + * \brief DAI Drivers definition + * \author Liam Girdwood + * \author Keyon Jie + */ + +#ifndef __SOF_LIB_DAI_LEGACY_H__ +#define __SOF_LIB_DAI_LEGACY_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct dai; +struct sof_ipc_stream_params; + +/** \addtogroup sof_dai_drivers DAI Drivers + * DAI Drivers API specification. + * @{ + */ + +#define DAI_CLOCK_IN 0 +#define DAI_CLOCK_OUT 1 + +#define DAI_DIR_PLAYBACK 0 +#define DAI_DIR_CAPTURE 1 + +#define DAI_NUM_SLOT_MAPS 8 + +#define DAI_INFO_TYPE 0 +#define DAI_INFO_DMA_CAPS 1 +#define DAI_INFO_DMA_DEV 2 + +/* DAI flags */ + +/** \brief IRQ used for copy() timer */ +#define DAI_FLAGS_IRQ_CB BIT(0) + +/* DAI get() flags */ + +/** \brief If the device does not exist it will be created */ +#define DAI_CREAT BIT(0) + +/** + * \brief DAI operations - all optional + * + * DAI drivers may allocate private data, + * which can be set with 'dai_set_drvdata' and retrieved with 'dai_get_drvdata'. + * If a single DAI instance can have multiple DMA links and/or there is + * some other possibility of the same instance being used in multiple + * contexts at the same time, the private data should be allocated in the + * SOF_MEM_ZONE_SHARED. + */ +struct dai_ops { + int (*set_config)(struct dai *dai, struct ipc_config_dai *config, + void *spec_config); + int (*trigger)(struct dai *dai, int cmd, int direction); + int (*get_hw_params)(struct dai *dai, + struct sof_ipc_stream_params *params, int dir); + int (*hw_params)(struct dai *dai, struct sof_ipc_stream_params *params); + int (*get_handshake)(struct dai *dai, int direction, int stream_id); + int (*get_fifo)(struct dai *dai, int direction, int stream_id); + int (*probe)(struct dai *dai); + int (*remove)(struct dai *dai); + uint32_t (*get_init_delay_ms)(struct dai *dai); + int (*get_fifo_depth)(struct dai *dai, int direction); + void (*copy)(struct dai *dai); /* Can be used by DAIs to prepare for data copying */ +}; + +struct timestamp_cfg { + uint32_t walclk_rate; /* Rate in Hz, e.g. 19200000 */ + int type; /* SSP, DMIC, HDA, etc. */ + int direction; /* Playback, capture */ + int index; /* For SSPx to select correct timestamp register */ + int dma_id; /* GPDMA id*/ + int dma_chan_index; /* Used GPDMA channel */ + int dma_chan_count; /* Channels in single GPDMA */ +}; + +struct timestamp_data { + uint64_t walclk; /* Wall clock */ + uint64_t sample; /* Sample count */ + uint32_t walclk_rate; /* Rate in Hz, e.g. 19200000 */ +}; + +struct timestamp_ops { + int (*ts_config)(struct dai *dai, struct timestamp_cfg *cfg); + int (*ts_start)(struct dai *dai, struct timestamp_cfg *cfg); + int (*ts_stop)(struct dai *dai, struct timestamp_cfg *cfg); + int (*ts_get)(struct dai *dai, struct timestamp_cfg *cfg, + struct timestamp_data *tsd); +}; + +struct dai_driver { + uint32_t type; /**< type, one of SOF_DAI_... */ + const struct sof_uuid_entry *uid; + struct tr_ctx *tctx; + uint32_t dma_caps; + uint32_t dma_dev; + struct dai_ops ops; + struct timestamp_ops ts_ops; +}; + +/** + * \brief DAI slot map to audio channel + */ +struct dai_slot_map { + uint32_t channel; /**< channel ID - CHAN_ID_ */ + uint32_t slot; /**< physical slot index */ +}; + +struct dai_plat_fifo_data { + uint32_t offset; + uint32_t width; + uint32_t depth; + uint32_t watermark; + uint32_t handshake; +}; + +/** + * \brief DAI platform data + */ +struct dai_plat_data { + uint32_t base; + int irq; + const char *irq_name; + uint32_t flags; + struct dai_plat_fifo_data fifo[2]; +}; + +/** + * \brief DAI runtime data + */ +struct dai_data { + /* local DMA config */ + struct dma_chan_data *chan; + uint32_t stream_id; + struct dma_sg_config config; + struct comp_dev *dai_dev; + struct comp_buffer *dma_buffer; + struct comp_buffer *local_buffer; + struct timestamp_cfg ts_config; + struct dai *dai; + struct dma *dma; + struct dai_group *group; /**< NULL if no group assigned */ + int xrun; /* true if we are doing xrun recovery */ + + pcm_converter_func process; /* processing function */ + + uint32_t dai_pos_blks; /* position in bytes (nearest block) */ + uint64_t start_position; /* position on start */ + uint32_t period_bytes; /**< number of bytes per one period */ + + /* host can read back this value without IPC */ + uint64_t *dai_pos; + + struct ipc_config_dai ipc_config; /* generic common config */ + void *dai_spec_config; /* dai specific config from the host */ + + uint64_t wallclock; /* wall clock at stream start */ + + /* + * flag indicating two-step stop/pause for DAI comp and DAI DMA. + * DAI stop occurs during STREAM_TRIG_STOP IPC and DMA stop during DAI_CONFIG IPC with + * the SOF_DAI_CONFIG_FLAGS_HW_FREE flag. + * DAI pause occurs during STREAM_TRIG_PAUSE IPC and DMA pause during DAI_CONFIG IPC with + * the SOF_DAI_CONFIG_FLAGS_PAUSE flag. + */ + bool delayed_dma_stop; +}; + +struct dai { + uint32_t index; /**< index */ + struct k_spinlock lock; /**< locking mechanism */ + int sref; /**< simple ref counter, guarded by lock */ + struct dai_plat_data plat_data; + const struct dai_driver *drv; + const struct dai_data *dd; + void *priv_data; +}; + +/** + * \brief Array of DAIs grouped by type. + */ +struct dai_type_info { + uint32_t type; /**< Type */ + struct dai *dai_array; /**< Array of DAIs */ + size_t num_dais; /**< Number of elements in dai_array */ +}; + +/* dai tracing */ +#define trace_dai_drv_get_tr_ctx(drv_p) ((drv_p)->tctx) +#define trace_dai_drv_get_id(drv_p) (-1) +#define trace_dai_drv_get_subid(drv_p) (-1) + +#define trace_dai_get_tr_ctx(dai_p) ((dai_p)->drv->tctx) +#define trace_dai_get_id(dai_p) ((dai_p)->drv->type) +#define trace_dai_get_subid(dai_p) ((dai_p)->index) + +#if defined(__ZEPHYR__) && defined(CONFIG_ZEPHYR_LOG) +/* driver level tracing */ +#define dai_cl_err(drv_p, __e, ...) LOG_ERR(__e, ##__VA_ARGS__) + +#define dai_cl_warn(drv_p, __e, ...) LOG_WRN(__e, ##__VA_ARGS__) + +#define dai_cl_info(drv_p, __e, ...) LOG_INF(__e, ##__VA_ARGS__) + +#define dai_cl_dbg(drv_p, __e, ...) LOG_DBG(__e, ##__VA_ARGS__) + +/* device level tracing */ +#define dai_err(dai_p, __e, ...) LOG_ERR(__e, ##__VA_ARGS__) + +#define dai_warn(dai_p, __e, ...) LOG_WRN(__e, ##__VA_ARGS__) + +#define dai_info(dai_p, __e, ...) LOG_INF(__e, ##__VA_ARGS__) + +#define dai_dbg(dai_p, __e, ...) LOG_DBG(__e, ##__VA_ARGS__) + +#else +/* class (driver) level (no device object) tracing */ + +#define dai_cl_err(drv_p, __e, ...) \ + trace_dev_err(trace_dai_dvr_get_tr_ctx, \ + trace_dai_drv_get_id, \ + trace_dai_drv_get_subid, \ + drv_p, __e, ##__VA_ARGS__) + +#define dai_cl_warn(drv_p, __e, ...) \ + trace_dev_warn(trace_dai_drv_get_tr_ctx,\ + trace_dai_drv_get_id, \ + trace_dai_drv_get_subid, \ + drv_p, __e, ##__VA_ARGS__) + +#define dai_cl_info(drv_p, __e, ...) \ + trace_dev_info(trace_dai_drv_get_tr_ctx,\ + trace_dai_drv_get_id, \ + trace_dai_drv_get_subid, \ + drv_p, __e, ##__VA_ARGS__) + +#define dai_cl_dbg(drv_p, __e, ...) \ + trace_dev_dbg(trace_dai_drv_get_tr_ctx, \ + trace_dai_drv_get_id, \ + trace_dai_drv_get_subid, \ + drv_p, __e, ##__VA_ARGS__) + +/* device tracing */ + +#define dai_err(dai_p, __e, ...) \ + trace_dev_err(trace_dai_get_tr_ctx, \ + trace_dai_get_id, \ + trace_dai_get_subid, dai_p, __e, ##__VA_ARGS__) + +#define dai_warn(dai_p, __e, ...) \ + trace_dev_warn(trace_dai_get_tr_ctx, \ + trace_dai_get_id, \ + trace_dai_get_subid, dai_p, __e, ##__VA_ARGS__) + +#define dai_info(dai_p, __e, ...) \ + trace_dev_info(trace_dai_get_tr_ctx, \ + trace_dai_get_id, \ + trace_dai_get_subid, dai_p, __e, ##__VA_ARGS__) + +#define dai_dbg(dai_p, __e, ...) \ + trace_dev_dbg(trace_dai_get_tr_ctx, \ + trace_dai_get_id, \ + trace_dai_get_subid, dai_p, __e, ##__VA_ARGS__) + +#endif /* #if defined(__ZEPHYR__) && defined(CONFIG_ZEPHYR_LOG) */ + +/** + * \brief API to request DAI group + * + * Returns a DAI group for the given ID and + * increments the counter of DAIs in the group. + * + * If a group for the given ID doesn't exist, + * it will either return NULL or allocate a new group structure + * if the CREATE flag is supplied. + * + * \param[in] group_id Group ID + * \param[in] flags Flags (CREATE) + */ +struct dai_group *dai_group_get(uint32_t group_id, uint32_t flags); + +/** + * \brief API to release DAI group + * + * Decrements the DAI counter inside the group. + * + * \param[in] group Group + */ +void dai_group_put(struct dai_group *group); + +/** + * \brief DAI group information + */ +struct dai_group { + /** + * Group ID + */ + uint32_t group_id; + + /** + * Number of DAIs in this group + */ + uint32_t num_dais; + + /** + * Number of DAIs to receive a trigger before processing begins + */ + uint32_t trigger_counter; + + /** + * Trigger command to propagate + */ + int trigger_cmd; + + /** + * Last trigger error + */ + int trigger_ret; + + /** + * Group list + */ + struct list_item list; +}; + +/** + * \brief Holds information about array of DAIs grouped by type. + */ +struct dai_info { + const struct dai_type_info *dai_type_array; + size_t num_dai_types; +}; + +/** + * \brief API to initialize a platform DAI. + * + * \param[in] sof Pointer to firmware main context. + */ +int dai_init(struct sof *sof); + +/** + * \brief API to request a platform DAI. + * + * \param[in] type Type of requested DAI. + * \param[in] index Index of requested DAI. + * \param[in] flags Flags (CREATE) + */ +struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags); + +/** + * \brief API to release a platform DAI. + * + * @param[in] dai DAI to relese. + */ +void dai_put(struct dai *dai); + +#define dai_set_drvdata(dai, data) \ + (dai->priv_data = data) +#define dai_get_drvdata(dai) \ + dai->priv_data +#define dai_base(dai) \ + dai->plat_data.base +#define dai_irq(dai) \ + dai->plat_data.irq +#define dai_fifo(dai, direction) \ + dai->plat_data.fifo[direction].offset + +/** + * \brief Digital Audio interface formatting + */ +static inline int dai_set_config(struct dai *dai, struct ipc_config_dai *config, + void *spec_config) +{ + return dai->drv->ops.set_config(dai, config, spec_config); +} + +/** + * \brief Digital Audio interface trigger + */ +static inline int dai_trigger(struct dai *dai, int cmd, int direction) +{ + return dai->drv->ops.trigger(dai, cmd, direction); +} + +/** + * \brief Get Digital Audio interface stream parameters + */ +static inline int dai_get_hw_params(struct dai *dai, + struct sof_ipc_stream_params *params, + int dir) +{ + return dai->drv->ops.get_hw_params(dai, params, dir); +} + +/** + * \brief Configure Digital Audio interface stream parameters + */ +static inline int dai_hw_params(struct dai *dai, + struct sof_ipc_stream_params *params) +{ + if (dai->drv->ops.hw_params) + return dai->drv->ops.hw_params(dai, params); + + return 0; +} + +/** + * \brief Get Digital Audio interface DMA Handshake + */ +static inline int dai_get_handshake(struct dai *dai, int direction, + int stream_id) +{ + return dai->drv->ops.get_handshake(dai, direction, stream_id); +} + +/** + * \brief Get Digital Audio interface FIFO address + */ +static inline int dai_get_fifo(struct dai *dai, int direction, + int stream_id) +{ + return dai->drv->ops.get_fifo(dai, direction, stream_id); +} + +/** + * \brief Digital Audio interface Probe + */ +static inline int dai_probe(struct dai *dai) +{ + return dai->drv->ops.probe(dai); +} + +/** + * \brief Digital Audio interface Remove + */ +static inline int dai_remove(struct dai *dai) +{ + return dai->drv->ops.remove(dai); +} + +/** + * \brief Get DAI initial delay in milliseconds + */ +static inline uint32_t dai_get_init_delay_ms(struct dai *dai) +{ + if (dai && dai->drv->ops.get_init_delay_ms) + return dai->drv->ops.get_init_delay_ms(dai); + + return 0; +} + +static inline int dai_get_fifo_depth(struct dai *dai, int direction) +{ + if (dai && dai->drv->ops.get_fifo_depth) + return dai->drv->ops.get_fifo_depth(dai, direction); + + return 0; +} + +/** + * \brief Get driver specific DAI information + */ +static inline int dai_get_info(struct dai *dai, int info) +{ + int ret; + + switch (info) { + case DAI_INFO_TYPE: + ret = dai->drv->type; + break; + case DAI_INFO_DMA_CAPS: + ret = dai->drv->dma_caps; + break; + case DAI_INFO_DMA_DEV: + ret = dai->drv->dma_dev; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static inline void dai_write(struct dai *dai, uint32_t reg, uint32_t value) +{ + io_reg_write(dai_base(dai) + reg, value); +} + +static inline uint32_t dai_read(struct dai *dai, uint32_t reg) +{ + return io_reg_read(dai_base(dai) + reg); +} + +static inline void dai_update_bits(struct dai *dai, uint32_t reg, + uint32_t mask, uint32_t value) +{ + io_reg_update_bits(dai_base(dai) + reg, mask, value); +} + +static inline const struct dai_info *dai_info_get(void) +{ + return sof_get()->dai_info; +} + +/** + * \brief Configure DMA channel for DAI + */ +int dai_config_dma_channel(struct comp_dev *dev, void *config); + +/** + * \brief Reset DAI DMA config + */ +void dai_dma_release(struct comp_dev *dev); + +/** + * \brief Configure DAI physical interface. + */ +int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_config, + void *spec_config); + +/** + * \brief Assign DAI to a group for simultaneous triggering. + */ +int dai_assign_group(struct comp_dev *dev, uint32_t group_id); + +/** + * \brief dai position for host driver. + */ +int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn); + +/** + * \brief update dai dma position for host driver. + */ +void dai_dma_position_update(struct comp_dev *dev); +/** @}*/ + +#endif /* __SOF_LIB_DAI_LEGACY_H__ */ diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h new file mode 100644 index 000000000000..0bc976c39e58 --- /dev/null +++ b/src/include/sof/lib/dai-zephyr.h @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * Author: Liam Girdwood + * Keyon Jie + */ + +/** + * \file include/sof/lib/dai.h + * \brief DAI Drivers definition + * \author Liam Girdwood + * \author Keyon Jie + */ + +#ifndef __SOF_LIB_DAI_ZEPHYR_H__ +#define __SOF_LIB_DAI_ZEPHYR_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** \addtogroup sof_dai_drivers DAI Drivers + * DAI Drivers API specification. + * @{ + */ + +/** \brief If the device does not exist it will be created */ +#define DAI_CREAT BIT(0) + +struct dai { + uint32_t index; + uint32_t type; + uint32_t dma_caps; + uint32_t dma_dev; + const struct device *drv; + const struct dai_data *dd; +}; + +struct dai_info_table { + const char *name; /**< Name prototype*/ + struct dai dai_p; /**< DAI prototype */ +}; + +struct timestamp_cfg { + uint32_t walclk_rate; /* Rate in Hz, e.g. 19200000 */ + int type; /* SSP, DMIC, HDA, etc. */ + int direction; /* Playback, capture */ + int index; /* For SSPx to select correct timestamp register */ + int dma_id; /* GPDMA id*/ + int dma_chan_index; /* Used GPDMA channel */ + int dma_chan_count; /* Channels in single GPDMA */ +}; + +struct timestamp_data { + uint64_t walclk; /* Wall clock */ + uint64_t sample; /* Sample count */ + uint32_t walclk_rate; /* Rate in Hz, e.g. 19200000 */ +}; + +struct timestamp_ops { + int (*ts_config)(struct dai *dai, struct timestamp_cfg *cfg); + int (*ts_start)(struct dai *dai, struct timestamp_cfg *cfg); + int (*ts_stop)(struct dai *dai, struct timestamp_cfg *cfg); + int (*ts_get)(struct dai *dai, struct timestamp_cfg *cfg, + struct timestamp_data *tsd); +}; + +/** + * \brief DAI group information + */ +struct dai_group { + /** + * Group ID + */ + uint32_t group_id; + + /** + * Number of DAIs in this group + */ + uint32_t num_dais; + + /** + * Number of DAIs to receive a trigger before processing begins + */ + uint32_t trigger_counter; + + /** + * Trigger command to propagate + */ + int trigger_cmd; + + /** + * Last trigger error + */ + int trigger_ret; + + /** + * Group list + */ + struct list_item list; +}; + +/** + * \brief DAI runtime data + */ +struct dai_data { + /* local DMA config */ + struct dma_chan_data *chan; + uint32_t stream_id; + struct dma_sg_config config; + struct dma_config *z_config; + struct comp_dev *dai_dev; + struct comp_buffer *dma_buffer; + struct comp_buffer *local_buffer; + struct timestamp_cfg ts_config; + struct dai *dai; + struct dma *dma; + struct dai_group *group; /**< NULL if no group assigned */ + int xrun; /* true if we are doing xrun recovery */ + + pcm_converter_func process; /* processing function */ + + uint32_t dai_pos_blks; /* position in bytes (nearest block) */ + uint64_t start_position; /* position on start */ + uint32_t period_bytes; /**< number of bytes per one period */ + + /* host can read back this value without IPC */ + uint64_t *dai_pos; + + struct ipc_config_dai ipc_config; /* generic common config */ + void *dai_spec_config; /* dai specific config from the host */ + + uint64_t wallclock; /* wall clock at stream start */ + + /* + * flag indicating two-step stop/pause for DAI comp and DAI DMA. + * DAI stop occurs during STREAM_TRIG_STOP IPC and DMA stop during DAI_CONFIG IPC with + * the SOF_DAI_CONFIG_FLAGS_HW_FREE flag. + * DAI pause occurs during STREAM_TRIG_PAUSE IPC and DMA pause during DAI_CONFIG IPC with + * the SOF_DAI_CONFIG_FLAGS_PAUSE flag. + */ + bool delayed_dma_stop; +}; + +/* these 3 are here to satisfy clk.c and ssp.h interconnection, will be removed leter */ +static inline void dai_write(struct dai *dai, uint32_t reg, uint32_t value) +{ +} + +static inline uint32_t dai_read(struct dai *dai, uint32_t reg) +{ + return 0; +} + +static inline void dai_update_bits(struct dai *dai, uint32_t reg, + uint32_t mask, uint32_t value) +{ +} + +/** + * \brief API to request DAI group + * + * Returns a DAI group for the given ID and + * increments the counter of DAIs in the group. + * + * If a group for the given ID doesn't exist, + * it will either return NULL or allocate a new group structure + * if the CREATE flag is supplied. + * + * \param[in] group_id Group ID + * \param[in] flags Flags (CREATE) + */ +struct dai_group *dai_group_get(uint32_t group_id, uint32_t flags); + +/** + * \brief API to release DAI group + * + * Decrements the DAI counter inside the group. + * + * \param[in] group Group + */ +void dai_group_put(struct dai_group *group); + +/** + * \brief API to initialize a platform DAI. + * + * \param[in] sof Pointer to firmware main context. + */ +static inline int dai_init(struct sof *sof) +{ + return 0; +} + +/** + * \brief API to request a platform DAI. + * + * \param[in] type Type of requested DAI. + * \param[in] index Index of requested DAI. + * \param[in] flags Flags (CREATE) + */ +struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags); + +/** + * \brief API to release a platform DAI. + * + * @param[in] dai DAI to relese. + */ +void dai_put(struct dai *dai); + +/** + * \brief Digital Audio interface formatting + */ +int dai_set_config(struct dai *dai, struct ipc_config_dai *config, void *spec_config); + +/** + * \brief Get Digital Audio interface DMA Handshake + */ +int dai_get_handshake(struct dai *dai, int direction, int stream_id); + +/** + * \brief Get Digital Audio interface fifo depth + */ +int dai_get_fifo_depth(struct dai *dai, int direction); + +/** + * \brief Get DAI initial delay in milliseconds + */ +uint32_t dai_get_init_delay_ms(struct dai *dai); + +/** + * \brief Get DAI stream id + */ +int dai_get_stream_id(struct dai *dai, int direction); + +/** + * \brief Configure DMA channel for DAI + */ +int dai_config_dma_channel(struct comp_dev *dev, void *config); + +/** + * \brief Reset DAI DMA config + */ +void dai_dma_release(struct comp_dev *dev); + +/** + * \brief Configure DAI physical interface. + */ +int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_cfg, void *spec_cfg); + +/** + * \brief Assign DAI to a group for simultaneous triggering. + */ +int dai_assign_group(struct comp_dev *dev, uint32_t group_id); + +/** + * \brief dai position for host driver. + */ +int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn); + +/** + * \brief update dai dma position for host driver. + */ +void dai_dma_position_update(struct comp_dev *dev); +/** @}*/ + +#endif /* __SOF_LIB_DAI_ZEPHYR_H__ */ diff --git a/src/include/sof/lib/dai.h b/src/include/sof/lib/dai.h index 07d1d959fce5..150ac5f48fff 100644 --- a/src/include/sof/lib/dai.h +++ b/src/include/sof/lib/dai.h @@ -16,551 +16,10 @@ #ifndef __SOF_LIB_DAI_H__ #define __SOF_LIB_DAI_H__ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct dai; -struct sof_ipc_stream_params; - -/** \addtogroup sof_dai_drivers DAI Drivers - * DAI Drivers API specification. - * @{ - */ - -#define DAI_CLOCK_IN 0 -#define DAI_CLOCK_OUT 1 - -#define DAI_DIR_PLAYBACK 0 -#define DAI_DIR_CAPTURE 1 - -#define DAI_NUM_SLOT_MAPS 8 - -#define DAI_INFO_TYPE 0 -#define DAI_INFO_DMA_CAPS 1 -#define DAI_INFO_DMA_DEV 2 - -/* DAI flags */ - -/** \brief IRQ used for copy() timer */ -#define DAI_FLAGS_IRQ_CB BIT(0) - -/* DAI get() flags */ - -/** \brief If the device does not exist it will be created */ -#define DAI_CREAT BIT(0) - -/** - * \brief DAI operations - all optional - * - * DAI drivers may allocate private data, - * which can be set with 'dai_set_drvdata' and retrieved with 'dai_get_drvdata'. - * If a single DAI instance can have multiple DMA links and/or there is - * some other possibility of the same instance being used in multiple - * contexts at the same time, the private data should be allocated in the - * SOF_MEM_ZONE_SHARED. - */ -struct dai_ops { - int (*set_config)(struct dai *dai, struct ipc_config_dai *config, - void *spec_config); - int (*trigger)(struct dai *dai, int cmd, int direction); - int (*get_hw_params)(struct dai *dai, - struct sof_ipc_stream_params *params, int dir); - int (*hw_params)(struct dai *dai, struct sof_ipc_stream_params *params); - int (*get_handshake)(struct dai *dai, int direction, int stream_id); - int (*get_fifo)(struct dai *dai, int direction, int stream_id); - int (*probe)(struct dai *dai); - int (*remove)(struct dai *dai); - uint32_t (*get_init_delay_ms)(struct dai *dai); - int (*get_fifo_depth)(struct dai *dai, int direction); - void (*copy)(struct dai *dai); /* Can be used by DAIs to prepare for data copying */ -}; - -struct timestamp_cfg { - uint32_t walclk_rate; /* Rate in Hz, e.g. 19200000 */ - int type; /* SSP, DMIC, HDA, etc. */ - int direction; /* Playback, capture */ - int index; /* For SSPx to select correct timestamp register */ - int dma_id; /* GPDMA id*/ - int dma_chan_index; /* Used GPDMA channel */ - int dma_chan_count; /* Channels in single GPDMA */ -}; - -struct timestamp_data { - uint64_t walclk; /* Wall clock */ - uint64_t sample; /* Sample count */ - uint32_t walclk_rate; /* Rate in Hz, e.g. 19200000 */ -}; - -struct timestamp_ops { - int (*ts_config)(struct dai *dai, struct timestamp_cfg *cfg); - int (*ts_start)(struct dai *dai, struct timestamp_cfg *cfg); - int (*ts_stop)(struct dai *dai, struct timestamp_cfg *cfg); - int (*ts_get)(struct dai *dai, struct timestamp_cfg *cfg, - struct timestamp_data *tsd); -}; - -struct dai_driver { - uint32_t type; /**< type, one of SOF_DAI_... */ - const struct sof_uuid_entry *uid; - struct tr_ctx *tctx; - uint32_t dma_caps; - uint32_t dma_dev; - struct dai_ops ops; - struct timestamp_ops ts_ops; -}; - -/** - * \brief DAI slot map to audio channel - */ -struct dai_slot_map { - uint32_t channel; /**< channel ID - CHAN_ID_ */ - uint32_t slot; /**< physical slot index */ -}; - -struct dai_plat_fifo_data { - uint32_t offset; - uint32_t width; - uint32_t depth; - uint32_t watermark; - uint32_t handshake; -}; - -/** - * \brief DAI platform data - */ -struct dai_plat_data { - uint32_t base; - int irq; - const char *irq_name; - uint32_t flags; - struct dai_plat_fifo_data fifo[2]; -}; - -/** - * \brief DAI runtime data - */ -struct dai_data { - /* local DMA config */ - struct dma_chan_data *chan; - uint32_t stream_id; - struct dma_sg_config config; -#ifdef __ZEPHYR__ - struct dma_config *z_config; -#endif - struct comp_dev *dai_dev; - struct comp_buffer *dma_buffer; - struct comp_buffer *local_buffer; - struct timestamp_cfg ts_config; - struct dai *dai; - struct dma *dma; - struct dai_group *group; /**< NULL if no group assigned */ - int xrun; /* true if we are doing xrun recovery */ - - pcm_converter_func process; /* processing function */ - - uint32_t dai_pos_blks; /* position in bytes (nearest block) */ - uint64_t start_position; /* position on start */ - uint32_t period_bytes; /**< number of bytes per one period */ - - /* host can read back this value without IPC */ - uint64_t *dai_pos; - - struct ipc_config_dai ipc_config; /* generic common config */ - void *dai_spec_config; /* dai specific config from the host */ - - uint64_t wallclock; /* wall clock at stream start */ - - /* - * flag indicating two-step stop/pause for DAI comp and DAI DMA. - * DAI stop occurs during STREAM_TRIG_STOP IPC and DMA stop during DAI_CONFIG IPC with - * the SOF_DAI_CONFIG_FLAGS_HW_FREE flag. - * DAI pause occurs during STREAM_TRIG_PAUSE IPC and DMA pause during DAI_CONFIG IPC with - * the SOF_DAI_CONFIG_FLAGS_PAUSE flag. - */ - bool delayed_dma_stop; -}; - -struct dai { - uint32_t index; /**< index */ - struct k_spinlock lock; /**< locking mechanism */ - int sref; /**< simple ref counter, guarded by lock */ - struct dai_plat_data plat_data; - const struct dai_driver *drv; - const struct dai_data *dd; - void *priv_data; -}; - -/** - * \brief Array of DAIs grouped by type. - */ -struct dai_type_info { - uint32_t type; /**< Type */ - struct dai *dai_array; /**< Array of DAIs */ - size_t num_dais; /**< Number of elements in dai_array */ -}; - -/* dai tracing */ -#define trace_dai_drv_get_tr_ctx(drv_p) ((drv_p)->tctx) -#define trace_dai_drv_get_id(drv_p) (-1) -#define trace_dai_drv_get_subid(drv_p) (-1) - -#define trace_dai_get_tr_ctx(dai_p) ((dai_p)->drv->tctx) -#define trace_dai_get_id(dai_p) ((dai_p)->drv->type) -#define trace_dai_get_subid(dai_p) ((dai_p)->index) - -#if defined(__ZEPHYR__) && defined(CONFIG_ZEPHYR_LOG) -/* driver level tracing */ -#define dai_cl_err(drv_p, __e, ...) LOG_ERR(__e, ##__VA_ARGS__) - -#define dai_cl_warn(drv_p, __e, ...) LOG_WRN(__e, ##__VA_ARGS__) - -#define dai_cl_info(drv_p, __e, ...) LOG_INF(__e, ##__VA_ARGS__) - -#define dai_cl_dbg(drv_p, __e, ...) LOG_DBG(__e, ##__VA_ARGS__) - -/* device level tracing */ -#define dai_err(dai_p, __e, ...) LOG_ERR(__e, ##__VA_ARGS__) - -#define dai_warn(dai_p, __e, ...) LOG_WRN(__e, ##__VA_ARGS__) - -#define dai_info(dai_p, __e, ...) LOG_INF(__e, ##__VA_ARGS__) - -#define dai_dbg(dai_p, __e, ...) LOG_DBG(__e, ##__VA_ARGS__) - +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS +#include #else -/* class (driver) level (no device object) tracing */ - -#define dai_cl_err(drv_p, __e, ...) \ - trace_dev_err(trace_dai_dvr_get_tr_ctx, \ - trace_dai_drv_get_id, \ - trace_dai_drv_get_subid, \ - drv_p, __e, ##__VA_ARGS__) - -#define dai_cl_warn(drv_p, __e, ...) \ - trace_dev_warn(trace_dai_drv_get_tr_ctx,\ - trace_dai_drv_get_id, \ - trace_dai_drv_get_subid, \ - drv_p, __e, ##__VA_ARGS__) - -#define dai_cl_info(drv_p, __e, ...) \ - trace_dev_info(trace_dai_drv_get_tr_ctx,\ - trace_dai_drv_get_id, \ - trace_dai_drv_get_subid, \ - drv_p, __e, ##__VA_ARGS__) - -#define dai_cl_dbg(drv_p, __e, ...) \ - trace_dev_dbg(trace_dai_drv_get_tr_ctx, \ - trace_dai_drv_get_id, \ - trace_dai_drv_get_subid, \ - drv_p, __e, ##__VA_ARGS__) - -/* device tracing */ - -#define dai_err(dai_p, __e, ...) \ - trace_dev_err(trace_dai_get_tr_ctx, \ - trace_dai_get_id, \ - trace_dai_get_subid, dai_p, __e, ##__VA_ARGS__) - -#define dai_warn(dai_p, __e, ...) \ - trace_dev_warn(trace_dai_get_tr_ctx, \ - trace_dai_get_id, \ - trace_dai_get_subid, dai_p, __e, ##__VA_ARGS__) - -#define dai_info(dai_p, __e, ...) \ - trace_dev_info(trace_dai_get_tr_ctx, \ - trace_dai_get_id, \ - trace_dai_get_subid, dai_p, __e, ##__VA_ARGS__) - -#define dai_dbg(dai_p, __e, ...) \ - trace_dev_dbg(trace_dai_get_tr_ctx, \ - trace_dai_get_id, \ - trace_dai_get_subid, dai_p, __e, ##__VA_ARGS__) - -#endif /* #if defined(__ZEPHYR__) && defined(CONFIG_ZEPHYR_LOG) */ - -/** - * \brief API to request DAI group - * - * Returns a DAI group for the given ID and - * increments the counter of DAIs in the group. - * - * If a group for the given ID doesn't exist, - * it will either return NULL or allocate a new group structure - * if the CREATE flag is supplied. - * - * \param[in] group_id Group ID - * \param[in] flags Flags (CREATE) - */ -struct dai_group *dai_group_get(uint32_t group_id, uint32_t flags); - -/** - * \brief API to release DAI group - * - * Decrements the DAI counter inside the group. - * - * \param[in] group Group - */ -void dai_group_put(struct dai_group *group); - -/** - * \brief DAI group information - */ -struct dai_group { - /** - * Group ID - */ - uint32_t group_id; - - /** - * Number of DAIs in this group - */ - uint32_t num_dais; - - /** - * Number of DAIs to receive a trigger before processing begins - */ - uint32_t trigger_counter; - - /** - * Trigger command to propagate - */ - int trigger_cmd; - - /** - * Last trigger error - */ - int trigger_ret; - - /** - * Group list - */ - struct list_item list; -}; - -/** - * \brief Holds information about array of DAIs grouped by type. - */ -struct dai_info { - const struct dai_type_info *dai_type_array; - size_t num_dai_types; -}; - -/** - * \brief API to initialize a platform DAI. - * - * \param[in] sof Pointer to firmware main context. - */ -int dai_init(struct sof *sof); - -/** - * \brief API to request a platform DAI. - * - * \param[in] type Type of requested DAI. - * \param[in] index Index of requested DAI. - * \param[in] flags Flags (CREATE) - */ -struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags); - -/** - * \brief API to release a platform DAI. - * - * @param[in] dai DAI to relese. - */ -void dai_put(struct dai *dai); - -#define dai_set_drvdata(dai, data) \ - (dai->priv_data = data) -#define dai_get_drvdata(dai) \ - dai->priv_data -#define dai_base(dai) \ - dai->plat_data.base -#define dai_irq(dai) \ - dai->plat_data.irq -#define dai_fifo(dai, direction) \ - dai->plat_data.fifo[direction].offset - -/** - * \brief Digital Audio interface formatting - */ -static inline int dai_set_config(struct dai *dai, struct ipc_config_dai *config, - void *spec_config) -{ - return dai->drv->ops.set_config(dai, config, spec_config); -} - -/** - * \brief Digital Audio interface trigger - */ -static inline int dai_trigger(struct dai *dai, int cmd, int direction) -{ - return dai->drv->ops.trigger(dai, cmd, direction); -} - -/** - * \brief Get Digital Audio interface stream parameters - */ -static inline int dai_get_hw_params(struct dai *dai, - struct sof_ipc_stream_params *params, - int dir) -{ - return dai->drv->ops.get_hw_params(dai, params, dir); -} - -/** - * \brief Configure Digital Audio interface stream parameters - */ -static inline int dai_hw_params(struct dai *dai, - struct sof_ipc_stream_params *params) -{ - if (dai->drv->ops.hw_params) - return dai->drv->ops.hw_params(dai, params); - - return 0; -} - -/** - * \brief Get Digital Audio interface DMA Handshake - */ -static inline int dai_get_handshake(struct dai *dai, int direction, - int stream_id) -{ - return dai->drv->ops.get_handshake(dai, direction, stream_id); -} - -/** - * \brief Get Digital Audio interface FIFO address - */ -static inline int dai_get_fifo(struct dai *dai, int direction, - int stream_id) -{ - return dai->drv->ops.get_fifo(dai, direction, stream_id); -} - -/** - * \brief Digital Audio interface Probe - */ -static inline int dai_probe(struct dai *dai) -{ - return dai->drv->ops.probe(dai); -} - -/** - * \brief Digital Audio interface Remove - */ -static inline int dai_remove(struct dai *dai) -{ - return dai->drv->ops.remove(dai); -} - -/** - * \brief Get DAI initial delay in milliseconds - */ -static inline uint32_t dai_get_init_delay_ms(struct dai *dai) -{ - if (dai && dai->drv->ops.get_init_delay_ms) - return dai->drv->ops.get_init_delay_ms(dai); - - return 0; -} - -static inline int dai_get_fifo_depth(struct dai *dai, int direction) -{ - if (dai && dai->drv->ops.get_fifo_depth) - return dai->drv->ops.get_fifo_depth(dai, direction); - - return 0; -} - -/** - * \brief Get driver specific DAI information - */ -static inline int dai_get_info(struct dai *dai, int info) -{ - int ret; - - switch (info) { - case DAI_INFO_TYPE: - ret = dai->drv->type; - break; - case DAI_INFO_DMA_CAPS: - ret = dai->drv->dma_caps; - break; - case DAI_INFO_DMA_DEV: - ret = dai->drv->dma_dev; - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static inline void dai_write(struct dai *dai, uint32_t reg, uint32_t value) -{ - io_reg_write(dai_base(dai) + reg, value); -} - -static inline uint32_t dai_read(struct dai *dai, uint32_t reg) -{ - return io_reg_read(dai_base(dai) + reg); -} - -static inline void dai_update_bits(struct dai *dai, uint32_t reg, - uint32_t mask, uint32_t value) -{ - io_reg_update_bits(dai_base(dai) + reg, mask, value); -} - -static inline const struct dai_info *dai_info_get(void) -{ - return sof_get()->dai_info; -} - -/** - * \brief Configure DMA channel for DAI - */ -int dai_config_dma_channel(struct comp_dev *dev, void *config); - -/** - * \brief Reset DAI DMA config - */ -void dai_dma_release(struct comp_dev *dev); - -/** - * \brief Configure DAI physical interface. - */ -int dai_config(struct comp_dev *dev, struct ipc_config_dai *common_config, - void *spec_config); - -/** - * \brief Assign DAI to a group for simultaneous triggering. - */ -int dai_assign_group(struct comp_dev *dev, uint32_t group_id); - -/** - * \brief dai position for host driver. - */ -int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn); - -/** - * \brief update dai dma position for host driver. - */ -void dai_dma_position_update(struct comp_dev *dev); -/** @}*/ +#include +#endif #endif /* __SOF_LIB_DAI_H__ */ diff --git a/src/ipc/ipc4/dai.c b/src/ipc/ipc4/dai.c index 5da4102c2f0c..9bff9d814f33 100644 --- a/src/ipc/ipc4/dai.c +++ b/src/ipc/ipc4/dai.c @@ -70,7 +70,9 @@ int ipc_dai_data_config(struct comp_dev *dev) struct ipc_config_dai *dai = &dd->ipc_config; struct ipc4_copier_module_cfg *copier_cfg; struct dai *dai_p = dd->dai; +#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS struct alh_pdata *alh; +#endif if (!dai) { comp_err(dev, "dai_data_config(): no dai!\n"); @@ -102,7 +104,15 @@ int ipc_dai_data_config(struct comp_dev *dev) case SOF_DAI_INTEL_HDA: break; case SOF_DAI_INTEL_ALH: +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + dd->stream_id = dai_get_stream_id(dai_p, dai->direction); +#else alh = dai_get_drvdata(dai_p); + /* As with HDA, the DMA channel is assigned in runtime, + * not during topology parsing. + */ + dd->stream_id = alh->params.stream_id; +#endif /* SDW HW FIFO always requires 32bit MSB aligned sample data for * all formats, such as 8/16/24/32 bits. */ @@ -111,10 +121,6 @@ int ipc_dai_data_config(struct comp_dev *dev) dd->config.burst_elems = dai_get_fifo_depth(dd->dai, dai->direction); - /* As with HDA, the DMA channel is assigned in runtime, - * not during topology parsing. - */ - dd->stream_id = alh->params.stream_id; comp_dbg(dev, "dai_data_config() SOF_DAI_INTEL_ALH dd->dma_buffer->stream.frame_fmt %#x stream_id %d", dd->dma_buffer->stream.frame_fmt, dd->stream_id); diff --git a/src/lib/dai.c b/src/lib/dai.c index cb8db672e858..fa4a81bbb8bb 100644 --- a/src/lib/dai.c +++ b/src/lib/dai.c @@ -12,11 +12,16 @@ #include #include #include +#include #include #include #include #include +#if CONFIG_ZEPHYR_NATIVE_DRIVERS +#include +#include +#endif LOG_MODULE_REGISTER(dai, CONFIG_SOF_LOG_LEVEL); /* 06711c94-d37d-4a76-b302-bbf6944fdd2b */ @@ -123,6 +128,91 @@ void dai_group_put(struct dai_group *group) group->group_id = 0; } +#if CONFIG_ZEPHYR_NATIVE_DRIVERS + +struct dai_info_table dit[] = { + {"SSP_", { + .type = SOF_DAI_INTEL_SSP, + .dma_dev = DMA_DEV_SSP, + .dma_caps = DMA_CAP_GP_LP | DMA_CAP_GP_HP, + }, + }, + {"DMIC", { + .type = SOF_DAI_INTEL_DMIC, + .dma_dev = DMA_DEV_DMIC, + .dma_caps = DMA_CAP_GP_LP | DMA_CAP_GP_HP, + }, + }, + {"ALH_", { + .type = SOF_DAI_INTEL_ALH, + .dma_dev = DMA_DEV_ALH, + .dma_caps = DMA_CAP_GP_LP | DMA_CAP_GP_HP, + }, + }, +}; + +/* called from ipc/ipc3/handler.c and some platform.c files */ +struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags) +{ + const struct device *drv; + bool found = false; + char dai_name[32]; + struct dai *d; + uint32_t len; + int i; + + tr_info(&dai_tr, "get_dai_zephyr index: %u type: %u", index, type); + + for (i = 0; i < ARRAY_SIZE(dit); i++) { + if (dit[i].dai_p.type == type) { + strcpy(dai_name, dit[i].name); + len = strlen(dai_name); + dai_name[len] = 0x30 + index; + dai_name[len + 1] = 0; + found = true; + break; + } + } + + if (!found) + return NULL; + + drv = device_get_binding(dai_name); + if (!drv) + return NULL; + + d = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM, sizeof(struct dai)); + if (!d) + return NULL; + + d->type = type; + d->dma_dev = dit[i].dai_p.dma_dev; + d->dma_caps = dit[i].dai_p.dma_caps; + d->index = index; + d->drv = drv; + + if (dai_probe(d->drv)) { + rfree(d); + return NULL; + } + + return d; +} + +/* called from src/ipc/ipc3/handler.c */ +void dai_put(struct dai *dai) +{ + int ret; + + ret = dai_remove(dai->drv); + if (ret < 0) { + tr_err(&dai_tr, "dai_put_zephyr: index %d failed ret = %d", + dai->index, ret); + } + + rfree(dai); +} +#else static inline const struct dai_type_info *dai_find_type(uint32_t type) { const struct dai_info *info = dai_info_get(); @@ -190,3 +280,4 @@ void dai_put(struct dai *dai) dai->drv->type, dai->index, dai->sref); k_spin_unlock(&dai->lock, key); } +#endif diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 050e2376aeca..7f4b6492d7ee 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -145,11 +145,18 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V15) # Driver sources zephyr_library_sources( ${SOF_DRIVERS_PATH}/intel/cavs/ipc.c - ${SOF_DRIVERS_PATH}/intel/cavs/timestamp.c ) zephyr_library_sources_ifdef(CONFIG_INTEL_HDA ${SOF_DRIVERS_PATH}/intel/hda/hda-dma.c + ) + + if (NOT CONFIG_ZEPHYR_NATIVE_DRIVERS) + zephyr_library_sources( + ${SOF_DRIVERS_PATH}/intel/cavs/timestamp.c + ) + + zephyr_library_sources_ifdef(CONFIG_INTEL_HDA ${SOF_DRIVERS_PATH}/intel/hda/hda.c ) @@ -177,6 +184,11 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V15) ${SOF_DRIVERS_PATH}/intel/dmic/dmic_nhlt.c ) + zephyr_library_sources( + ${SOF_PLATFORM_PATH}/intel/cavs/lib/dai.c + ) + endif() + # Platform sources zephyr_library_sources( ${SOF_PLATFORM_PATH}/intel/cavs/platform.c @@ -184,7 +196,6 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V15) ${SOF_PLATFORM_PATH}/intel/cavs/lib/pm_runtime.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/pm_memory.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/clk.c - ${SOF_PLATFORM_PATH}/intel/cavs/lib/dai.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/dma.c ${SOF_PLATFORM_PATH}/apollolake/lib/power_down.S ${SOF_PLATFORM_PATH}/apollolake/lib/clk.c @@ -207,6 +218,14 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V18) # Driver sources zephyr_library_sources( ${SOF_DRIVERS_PATH}/intel/cavs/ipc.c + ) + + zephyr_library_sources_ifdef(CONFIG_INTEL_HDA + ${SOF_DRIVERS_PATH}/intel/hda/hda-dma.c + ) + + if (NOT CONFIG_ZEPHYR_NATIVE_DRIVERS) + zephyr_library_sources( ${SOF_DRIVERS_PATH}/intel/cavs/timestamp.c ) @@ -215,7 +234,6 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V18) #${SOF_DRIVERS_PATH}/intel/cavs/sue-iomux.c zephyr_library_sources_ifdef(CONFIG_INTEL_HDA - ${SOF_DRIVERS_PATH}/intel/hda/hda-dma.c ${SOF_DRIVERS_PATH}/intel/hda/hda.c ) @@ -243,6 +261,11 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V18) ${SOF_DRIVERS_PATH}/intel/dmic/dmic_nhlt.c ) + zephyr_library_sources( + ${SOF_PLATFORM_PATH}/intel/cavs/lib/dai.c + ) + endif() + # Platform sources zephyr_library_sources( ${SOF_PLATFORM_PATH}/intel/cavs/platform.c @@ -252,7 +275,6 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V18) ${SOF_PLATFORM_PATH}/intel/cavs/lib/pm_runtime.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/pm_memory.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/clk.c - ${SOF_PLATFORM_PATH}/intel/cavs/lib/dai.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/dma.c #${SOF_PLATFORM_PATH}/intel/cavs/lps_pic_restore_vector.S ) @@ -279,11 +301,18 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V20) # Driver sources zephyr_library_sources( ${SOF_DRIVERS_PATH}/intel/cavs/ipc.c - ${SOF_DRIVERS_PATH}/intel/cavs/timestamp.c ) zephyr_library_sources_ifdef(CONFIG_INTEL_HDA ${SOF_DRIVERS_PATH}/intel/hda/hda-dma.c + ) + + if (NOT CONFIG_ZEPHYR_NATIVE_DRIVERS) + zephyr_library_sources( + ${SOF_DRIVERS_PATH}/intel/cavs/timestamp.c + ) + + zephyr_library_sources_ifdef(CONFIG_INTEL_HDA ${SOF_DRIVERS_PATH}/intel/hda/hda.c ) @@ -311,6 +340,11 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V20) ${SOF_DRIVERS_PATH}/intel/dmic/dmic_nhlt.c ) + zephyr_library_sources( + ${SOF_PLATFORM_PATH}/intel/cavs/lib/dai.c + ) + endif() + # Platform sources zephyr_library_sources( ${SOF_PLATFORM_PATH}/intel/cavs/platform.c @@ -320,7 +354,6 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V20) ${SOF_PLATFORM_PATH}/intel/cavs/lib/pm_runtime.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/pm_memory.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/clk.c - ${SOF_PLATFORM_PATH}/intel/cavs/lib/dai.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/dma.c #${SOF_PLATFORM_PATH}/intel/cavs/lps_pic_restore_vector.S ) @@ -347,11 +380,18 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V25) # Driver sources zephyr_library_sources( ${SOF_DRIVERS_PATH}/intel/cavs/ipc.c - ${SOF_DRIVERS_PATH}/intel/cavs/timestamp.c ) zephyr_library_sources_ifdef(CONFIG_INTEL_HDA ${SOF_DRIVERS_PATH}/intel/hda/hda-dma.c + ) + + if (NOT CONFIG_ZEPHYR_NATIVE_DRIVERS) + zephyr_library_sources( + ${SOF_DRIVERS_PATH}/intel/cavs/timestamp.c + ) + + zephyr_library_sources_ifdef(CONFIG_INTEL_HDA ${SOF_DRIVERS_PATH}/intel/hda/hda.c ) @@ -379,6 +419,10 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V25) ${SOF_DRIVERS_PATH}/intel/dmic/dmic_nhlt.c ) + zephyr_library_sources( + ${SOF_PLATFORM_PATH}/intel/cavs/lib/dai.c + ) + endif() # Platform sources zephyr_library_sources( @@ -389,7 +433,6 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V25) ${SOF_PLATFORM_PATH}/intel/cavs/lib/pm_runtime.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/pm_memory.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/clk.c - ${SOF_PLATFORM_PATH}/intel/cavs/lib/dai.c ${SOF_PLATFORM_PATH}/intel/cavs/lib/dma.c #${SOF_PLATFORM_PATH}/intel/cavs/lps_pic_restore_vector.S )