12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079 |
- /*
- * Copyright (c) 2020 Actions Semiconductor Co., Ltd
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- /**
- * @file
- * @brief Audio IN(ADC/I2S-RX/SPDIF-RX) core management layer
- */
- #include <kernel.h>
- #include <device.h>
- #include <string.h>
- #include <errno.h>
- #include <soc.h>
- #include <board_cfg.h>
- #include <drivers/dma.h>
- #include <drivers/audio/audio_in.h>
- #include "phy_audio_common.h"
- #include <logging/log.h>
- LOG_MODULE_REGISTER(ain, CONFIG_LOG_DEFAULT_LEVEL);
- #define AIN_SESSION_MAGIC (0x11223344)
- #define AIN_SESSION_CHECK_MAGIC(x) ((x) == AIN_SESSION_MAGIC)
- #ifdef CONFIG_AUDIO_IN_ADC_SUPPORT
- #define AIN_ADC_SESSION_MAX (1) /* 1 ADC channels */
- #else
- #define AIN_ADC_SESSION_MAX (1)
- #endif
- #ifdef CONFIG_AUDIO_IN_I2SRX_SUPPORT
- #define AIN_I2SRX_SESSION_MAX (1) /* 1 I2SRX channel */
- #else
- #define AIN_I2SRX_SESSION_MAX (0)
- #endif
- #ifdef CONFIG_AUDIO_IN_SPDIFRX_SUPPORT
- #define AIN_SPDIFRX_SESSION_MAX (1) /* 1 SPDIFRX channel */
- #else
- #define AIN_SPDIFRX_SESSION_MAX (0)
- #endif
- #define AIN_SESSION_MAX (AIN_ADC_SESSION_MAX + AIN_I2SRX_SESSION_MAX + AIN_SPDIFRX_SESSION_MAX) /* totally max sessions */
- #define AIN_SESSION_OPEN (1 << 0) /* Session open flag */
- #define AIN_SESSION_CONFIG (1 << 1) /* Session config flag */
- #define AIN_SESSION_START (1 << 2) /* Session start flag */
- #define AIN_SESSION_BIND (1 << 3) /* Session start flag */
- #define DMA_IRQ_TC (0) /* DMA completion flag */
- #define DMA_IRQ_HF (1) /* DMA half-full flag */
- /**
- * struct ain_dynamic_debug_t
- * @brief audio in session dynamic debug structure
- */
- typedef struct {
- uint32_t timestamp; /* record the per-second timestamp */
- uint32_t per_second_size; /* record the per-second play size */
- uint8_t dump_len; /* dump the length of buffer per-second */
- uint8_t performance: 1; /* enable flag of showing the play performance */
- } ain_dynamic_debug_t;
- /*
- * struct ain_session_t
- * @brief audio in session structure
- */
- typedef struct {
- int magic; /* session magic value as AIN_SESSION_MAGIC */
- int dma_chan; /* DMA channel handle */
- uint16_t input_dev; /* input audio device */
- uint16_t channel_type; /* Indicates the channel type selection */
- uint8_t flags; /* session flags */
- uint8_t id; /* the session id */
- uint8_t dma_width; /* dma width */
- int (*callback)(void *cb_data, uint32_t reason); /* The callback function which called when #AIN_DMA_IRQ_HF or #AIN_DMA_IRQ_TC events happened */
- void *cb_data; /* callback user data */
- uint8_t *reload_addr; /* Reload buffer address to transfer */
- uint32_t reload_len; /* The length of the reload buffer */
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- ain_dynamic_debug_t debug; /* dynamic debug */
- #endif
- uint8_t dma_separated_en : 1; /* the flag of DMA interleaved mode enable or not */
- uint8_t dma_separated_mode : 2; /* the DMA interleaved mode which specifies the data format stored in memory */
- } ain_session_t;
- /*
- * struct aout_drv_data
- * @brief audio in driver data which are software related
- */
- struct ain_drv_data {
- struct k_sem lock;
- struct device *dma_dev;
- struct device *adc0_dev; /* ADC0 device handler */
- struct device *i2srx0_dev; /* I2SRX0 device handler */
- struct device *spdifrx0_dev; /* SPFIRX0 device handler */
- struct device *anc0_dev; /* ANC0 device handler */
- uint32_t adc_sess_stack[AIN_ADC_SESSION_MAX + 1]; /* stack to store sync wait sessions */
- uint8_t adc_sess_stack_cur; /* current curtor of adc session stack */
- uint8_t adc_ref_counter; /* ADC channels start reference counter */
- uint8_t anc_en_flag : 1; /* the flag of ANC enable */
- };
- /*
- * struct aout_config_data
- * @brief audio in config data which are the hardward related
- */
- struct ain_config_data {
- const char *adc0_name; /* physical ADC0 device name */
- const char *i2srx0_name; /* physical I2SRX0 device name */
- const char *spdifrx0_name; /* physical SPDIFRX0 device name */
- };
- static ain_session_t ain_sessions[AIN_SESSION_MAX];
- /*
- * @brief checkout if there is the same channel within audio in sessions
- */
- static bool audio_in_session_check(uint16_t type)
- {
- int i;
- ain_session_t *sess = ain_sessions;
- uint8_t max_sess, sess_opened = 0;
- if (AUDIO_CHANNEL_ADC & type) {
- max_sess = AIN_ADC_SESSION_MAX;
- } else if (AUDIO_CHANNEL_I2SRX & type) {
- max_sess = AIN_I2SRX_SESSION_MAX;
- } else if (AUDIO_CHANNEL_SPDIFRX & type) {
- max_sess = AIN_SPDIFRX_SESSION_MAX;
- } else {
- LOG_ERR("Invalid session type %d", type);
- return true;
- }
- for (i = 0; i < AIN_SESSION_MAX; i++, sess++) {
- if (AIN_SESSION_CHECK_MAGIC(sess->magic)
- && (sess->flags & AIN_SESSION_OPEN)
- && (sess->channel_type & type)) {
- sess_opened++;
- LOG_DBG("Found aout channel type %d in use", type);
- }
- }
- /* check if the number of opened sessions is bigger than the max limit */
- if (sess_opened >= max_sess) {
- LOG_ERR("channel type:%d session max %d and opened %d",
- type, max_sess, sess_opened);
- return true;
- }
- return false;
- }
- /*
- * @brief Get audio in session by specified channel
- */
- static ain_session_t *audio_in_session_get(uint16_t type)
- {
- ain_session_t *sess = ain_sessions;
- int i;
- if (audio_in_session_check(type))
- return NULL;
- for (i = 0; i < AIN_SESSION_MAX; i++, sess++) {
- if (!(sess->flags & AIN_SESSION_OPEN)) {
- memset(sess, 0, sizeof(ain_session_t));
- sess->magic = AIN_SESSION_MAGIC;
- sess->flags = AIN_SESSION_OPEN;
- sess->channel_type = type;
- sess->id = i;
- return sess;
- }
- }
- return NULL;
- }
- /*
- * @brief Put audio in session by given session address
- */
- static void audio_in_session_put(ain_session_t *s)
- {
- ain_session_t *sess = ain_sessions;
- int i;
- for (i = 0; i < AIN_SESSION_MAX; i++, sess++) {
- if ((uint32_t)s == (uint32_t)sess) {
- memset(s, 0, sizeof(ain_session_t));
- break;
- }
- }
- }
- /* @brief audio in session lock */
- static inline void audio_in_lock(struct device *dev)
- {
- struct ain_drv_data *data = dev->data;
- k_sem_take(&data->lock, K_FOREVER);
- }
- /* @brief audio in session unlock */
- static inline void audio_in_unlock(struct device *dev)
- {
- struct ain_drv_data *data = dev->data;
- k_sem_give(&data->lock);
- }
- /* @brief bind the audio in session to dma start at the same time */
- static int audio_in_session_bind(struct device *dev, ain_session_t *this, ain_session_t *bind)
- {
- struct ain_drv_data *data = dev->data;
- if ((!bind) || (!AIN_SESSION_CHECK_MAGIC(bind->magic))) {
- LOG_ERR("Invalid session:%p", bind);
- return -EINVAL;
- }
- /* check if the session does not config or already start */
- if (!(bind->flags & AIN_SESSION_CONFIG) || (bind->flags & AIN_SESSION_START)) {
- LOG_ERR("Invalid session status:0x%x", bind->flags);
- return -EPERM;
- }
- if (this == bind) {
- LOG_ERR("Bind the same session@%p", this);
- return -EPERM;
- }
- if (bind->flags & AIN_SESSION_BIND) {
- LOG_INF("Already bind session@%p", bind);
- return 0;
- }
- if (bind->channel_type != AUDIO_CHANNEL_ADC) {
- LOG_ERR("For now only support ADC to bind channels");
- return -EPERM;
- }
- bind->flags |= AIN_SESSION_BIND;
- ++data->adc_ref_counter;
- LOG_INF("session@%p binded", bind);
- return 0;
- }
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- /* @brief show the audio in performance */
- static void audio_in_debug_perf(ain_session_t *session, uint8_t *buffer, uint32_t len)
- {
- uint32_t delta;
- if (session->debug.performance) {
- delta = (uint32_t)k_cyc_to_ns_floor64(k_cycle_get_32() - session->debug.timestamp);
- session->debug.per_second_size += len;
- if (delta > 1000000000UL) {
- session->debug.timestamp = k_cycle_get_32();
- printk("** in session@%p - %dB/s **\n",
- session, session->debug.per_second_size);
- session->debug.per_second_size = 0;
- if (session->debug.dump_len) {
- AUDIO_DUMP_MEM(buffer, session->debug.dump_len);
- }
- }
- }
- }
- /* @brief control all sessions to enable or disable performance statistics */
- static int audio_in_debug_perf_all_sessions(uint8_t is_en)
- {
- int i;
- ain_session_t *sess = ain_sessions;
- for (i = 0; i < AIN_SESSION_MAX; i++, sess++) {
- if (AIN_SESSION_CHECK_MAGIC(sess->magic)
- && (sess->flags & AIN_SESSION_OPEN)) {
- LOG_INF("session@%p perf debug status %d", sess, is_en);
- sess->debug.performance = is_en;
- sess->debug.per_second_size = 0;
- sess->debug.timestamp = 0;
- sess->debug.dump_len = 0;
- }
- }
- return 0;
- }
- /* @brief control all sessions to set the dump length per second */
- static int audio_in_debug_dump_length(uint8_t length)
- {
- int i;
- ain_session_t *sess = ain_sessions;
- for (i = 0; i < AIN_SESSION_MAX; i++, sess++) {
- if (AIN_SESSION_CHECK_MAGIC(sess->magic)
- && (sess->flags & AIN_SESSION_OPEN)) {
- sess->debug.dump_len = length;
- }
- }
- return 0;
- }
- #endif
- /* @brief Enable the ADC channel */
- #ifdef CONFIG_AUDIO_IN_ADC_SUPPORT
- static int audio_in_enable_adc(struct device *dev, ain_param_t *param)
- {
- struct ain_drv_data *data = dev->data;
- struct device *adc = data->adc0_dev;
- if (!adc) {
- LOG_ERR("Physical ADC device is not esixted");
- return -EFAULT;
- }
- if ((param->channel_width != CHANNEL_WIDTH_16BITS)
- && (param->channel_width != CHANNEL_WIDTH_24BITS)) {
- LOG_ERR("Invalid channel width %d", param->channel_width);
- return -EINVAL;
- }
- return phy_audio_enable(adc, (void *)param);
- }
- #endif
- /* @brief Enable the I2SRX channel */
- #ifdef CONFIG_AUDIO_IN_I2SRX_SUPPORT
- static int audio_in_enable_i2srx(struct device *dev, ain_param_t *param)
- {
- struct ain_drv_data *data = dev->data;
- struct device *i2srx = data->i2srx0_dev;
- if (!i2srx) {
- LOG_ERR("Physical I2SRX device is not esixted");
- return -EFAULT;
- }
- if ((param->channel_width != CHANNEL_WIDTH_16BITS)
- && (param->channel_width != CHANNEL_WIDTH_20BITS)
- && (param->channel_width != CHANNEL_WIDTH_24BITS)) {
- LOG_ERR("Invalid channel width %d", param->channel_width);
- return -EINVAL;
- }
- return phy_audio_enable(i2srx, (void *)param);
- }
- #endif
- /* @brief Enable the SPDIFRX channel */
- #ifdef CONFIG_AUDIO_IN_SPDIFRX_SUPPORT
- static int audio_in_enable_spdifrx(struct device *dev, ain_param_t *param)
- {
- struct ain_drv_data *data = dev->data;
- struct device *spdifrx = data->spdifrx0_dev;
- if (!spdifrx) {
- LOG_ERR("Physical SPDIFRX device is not esixted");
- return -EFAULT;
- }
- if ((param->channel_width != CHANNEL_WIDTH_16BITS)
- && (param->channel_width != CHANNEL_WIDTH_24BITS)) {
- LOG_ERR("Invalid channel width %d", param->channel_width);
- return -EINVAL;
- }
- return phy_audio_enable(spdifrx, (void *)param);
- }
- #endif
- /* @brief DMA irq callback on reload method */
- static void audio_in_dma_reload(const struct device *dev, void *user_data,
- uint32_t channel, int status)
- {
- ain_session_t *session = (ain_session_t *)user_data;
- uint32_t ret_reson;
- uint32_t key, delta, len;
- uint16_t time = CONFIG_ADC_STARTUP_DISCARD_TIME;
- static uint32_t timestamp = 0;
- static bool already_discard = false;
- ARG_UNUSED(dev);
- ARG_UNUSED(channel);
- if(session ==NULL)
- {
- LOG_WRN("session is NULL");
- return;
- }
- len = session->reload_len / 2;
- if (!timestamp) {
- timestamp = k_cycle_get_32();
- LOG_DBG("discard data start time:%u ns",k_cyc_to_ns_floor32(timestamp));
- }
- if ((!already_discard) && time) {
- delta = (uint32_t)k_cyc_to_ns_floor64(k_cycle_get_32() - timestamp);
- if (delta > (time * 1000000UL)) {
- already_discard = true;
- }
- LOG_DBG("discard data end time:%u ns", k_cyc_to_ns_floor32(k_cycle_get_32()));
- }
- if (session && AIN_SESSION_CHECK_MAGIC(session->magic)) {
- if (session->callback) {
- key = irq_lock();
- if (status == DMA_IRQ_HF) {
- ret_reson = AIN_DMA_IRQ_HF;
- if (!already_discard && time)
- memset(session->reload_addr, 0, len);
- } else {
- ret_reson = AIN_DMA_IRQ_TC;
- if (!already_discard && time)
- memset(session->reload_addr + len, 0, len);
- }
- session->callback(session->cb_data, ret_reson);
- irq_unlock(key);
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- audio_in_debug_perf(session, session->reload_addr,
- session->reload_len / 2);
- #endif
- }
- }
- }
- /* @brief audio out dma enable separated mode */
- static int audio_in_dma_separated_enable(struct device *dev,
- ain_session_t *session, audio_interleave_mode_e *p_mode)
- {
- ARG_UNUSED(dev);
- if (!session || !p_mode) {
- LOG_ERR("Invalid parameter");
- return -EINVAL;
- }
- session->dma_separated_en = 1;
- session->dma_separated_mode = *p_mode;
- LOG_INF("Enable AIN DMA separated mode:%d", session->dma_separated_mode);
- return 0;
- }
- /* @brief prepare the DMA configuration for the audio in transfer */
- static int audio_in_dma_prepare(struct device *dev, ain_session_t *session)
- {
- struct ain_drv_data *data = dev->data;
- struct dma_config dma_cfg = {0};
- struct dma_block_config dma_block_cfg = {0};
- struct audio_in_dma_info info = {0};
- dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY;
- /* Only support the DMA reload mode */
- dma_block_cfg.dest_reload_en = 1;
- dma_cfg.head_block = &dma_block_cfg;
- dma_cfg.source_data_size = session->dma_width;
- if (AUDIO_CHANNEL_ADC == session->channel_type) {
- info.input_dev = session->input_dev;
- if (phy_audio_control(data->adc0_dev, PHY_CMD_GET_AIN_DMA_INFO, &info)) {
- LOG_ERR("Failed to get ADC DMA info");
- return -EFAULT;
- }
- uint32_t level;
- uint32_t fifo_cmd = PHY_FIFO_CMD(session->input_dev, 0);
- if (!phy_audio_control(data->adc0_dev,
- PHY_CMD_FIFO_DRQ_LEVEL_GET, &fifo_cmd)) {
- level = PHY_GET_FIFO_CMD_VAL(fifo_cmd);
- LOG_DBG("DRQ level %d", level);
- /* When DRQ level < 8 levels shall use DMA single mode */
- if (level < 8)
- dma_cfg.source_burst_length = 1;
- } else {
- LOG_ERR("Failed to get DRQ level");
- return -EFAULT;
- }
- } else if (AUDIO_CHANNEL_I2SRX == session->channel_type) {
- if (phy_audio_control(data->i2srx0_dev, PHY_CMD_GET_AIN_DMA_INFO, &info)) {
- LOG_ERR("Failed to get I2SRX DMA info");
- return -EFAULT;
- }
- } else if (AUDIO_CHANNEL_SPDIFRX == session->channel_type) {
- if (phy_audio_control(data->spdifrx0_dev, PHY_CMD_GET_AIN_DMA_INFO, &info)) {
- LOG_ERR("Failed to get SPDIFRX DMA info");
- return -EFAULT;
- }
- } else {
- LOG_ERR("invalid channel type 0x%x\n", session->channel_type);
- return -EINVAL;
- }
- if (!data->dma_dev) {
- data->dma_dev = (struct device *)device_get_binding(info.dma_info.dma_dev_name);
- if (!data->dma_dev) {
- LOG_ERR("Bind DMA device %s error", info.dma_info.dma_dev_name);
- return -ENOENT;
- }
- }
- /* request dma channel handle */
- session->dma_chan = dma_request(data->dma_dev, 0xFF);
- if (session->dma_chan < 0) {
- LOG_ERR("Failed to request dma channel");
- return -ENXIO;
- }
- dma_cfg.dma_slot = info.dma_info.dma_id;
- /* Use the DMA irq to notify the status of transfer */
- if (session->callback) {
- dma_cfg.dma_callback = audio_in_dma_reload;
- dma_cfg.user_data = (void *)session;
- dma_cfg.complete_callback_en = 1;
- }
- if (session->dma_separated_en) {
- dma_cfg.reserved = 1;
- }
- if (dma_config(data->dma_dev, session->dma_chan, &dma_cfg)) {
- LOG_ERR("DMA config error");
- return -EFAULT;
- }
- if (session->reload_addr) {
- LOG_INF("reload addr:0x%x, reload len:%d\n", (uint32_t)session->reload_addr, session->reload_len);
- if (session->dma_separated_en) {
- uint32_t dma_length = (session->reload_len << 1);
- if (session->dma_separated_mode == LEFT_MONO_RIGHT_MUTE_MODE) {
- dma_reload(data->dma_dev, session->dma_chan, AUDIO_IN_DMA_RESERVED_ADDRESS,
- (uint32_t)session->reload_addr, dma_length);
- } else if (session->dma_separated_mode == LEFT_RIGHT_SEPERATE){
- dma_reload(data->dma_dev, session->dma_chan,
- (uint32_t)(session->reload_addr + session->reload_len),
- (uint32_t)session->reload_addr, dma_length);
- } else if (session->dma_separated_mode == LEFT_MUTE_RIGHT_MONO_MODE){
- dma_reload(data->dma_dev, session->dma_chan, (uint32_t)session->reload_addr,
- AUDIO_IN_DMA_RESERVED_ADDRESS, dma_length);
- }
- } else {
- dma_reload(data->dma_dev, session->dma_chan,
- 0, (uint32_t)session->reload_addr, session->reload_len);
- }
- }
- return 0;
- }
- /* @brief audio out enable and return the session handler */
- static void *acts_audio_in_open(struct device *dev, ain_param_t *param)
- {
- ain_session_t * session = NULL;
- uint8_t channel_type;
- int ret;
- void *rtn_sess = NULL;
- if (!param) {
- LOG_ERR("NULL parameter");
- return NULL;
- }
- audio_in_lock(dev);
- channel_type = param->channel_type;
- session = audio_in_session_get(channel_type);
- if (!session) {
- LOG_ERR("Failed to get audio session (type:%d)", channel_type);
- goto out;
- }
- if (param->reload_setting.reload_addr && !param->reload_setting.reload_len) {
- LOG_ERR("Reload parameter error addr:0x%x, len:0x%x",
- (uint32_t)param->reload_setting.reload_addr, param->reload_setting.reload_len);
- audio_in_session_put(session);
- goto out;
- }
- session->callback = param->callback;
- session->cb_data = param->cb_data;
- session->reload_addr = param->reload_setting.reload_addr;
- session->reload_len = param->reload_setting.reload_len;
- session->dma_width = (CHANNEL_WIDTH_16BITS == param->channel_width) ? 2 : 4;
- if (channel_type == AUDIO_CHANNEL_ADC) {
- #ifdef CONFIG_AUDIO_IN_ADC_SUPPORT
- ret = audio_in_enable_adc(dev, param);
- if (!ret)
- session->input_dev = param->adc_setting->device;
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type == AUDIO_CHANNEL_I2SRX) {
- #ifdef CONFIG_AUDIO_IN_I2SRX_SUPPORT
- ret = audio_in_enable_i2srx(dev, param);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type == AUDIO_CHANNEL_SPDIFRX) {
- #ifdef CONFIG_AUDIO_IN_SPDIFRX_SUPPORT
- ret = audio_in_enable_spdifrx(dev, param);
- #else
- ret = -ENXIO;
- #endif
- } else {
- LOG_ERR("Invalid channel type %d", channel_type);
- audio_in_session_put(session);
- goto out;
- }
- if (ret) {
- LOG_ERR("Enable channel type %d error:%d", channel_type, ret);
- audio_in_session_put(session);
- goto out;
- }
- session->flags |= AIN_SESSION_CONFIG;
- /* The session address is the audio in handler */
- rtn_sess = (void *)session;
- LOG_INF("channel@%d sess:%p type:%d input_dev:0x%x opened",
- session->id, session, channel_type, session->input_dev);
- out:
- audio_in_unlock(dev);
- return rtn_sess;
- }
- /* @brief Disable the ADC channel */
- #ifdef CONFIG_AUDIO_IN_ADC_SUPPORT
- static int audio_in_disable_adc(struct device *dev, ain_session_t *session)
- {
- struct ain_drv_data *data = dev->data;
- struct device *adc = data->adc0_dev;
- uint16_t input_dev = session->input_dev;
- if (!adc) {
- LOG_ERR("Physical ADC device is not esixted");
- return -EFAULT;
- }
- if ((session->flags & AIN_SESSION_BIND)
- && !(session->flags & AIN_SESSION_START)) {
- /* the session already bind but not start yet */
- if (data->adc_ref_counter)
- --data->adc_ref_counter;
- }
- if (!data->adc_ref_counter)
- data->adc_sess_stack_cur = 0;
- return phy_audio_disable(adc, &input_dev);
- }
- #endif
- /* @brief Disable the I2SRX channel */
- #ifdef CONFIG_AUDIO_IN_I2SRX_SUPPORT
- static int audio_in_disable_i2srx(struct device *dev, ain_session_t *session)
- {
- struct ain_drv_data *data = dev->data;
- struct device *i2srx = data->i2srx0_dev;
- if (!i2srx) {
- LOG_ERR("Physical I2SRX device is not esixted");
- return -EFAULT;
- }
- return phy_audio_disable(i2srx, NULL);
- }
- #endif
- /* @brief Disable the SPDIFRX channel */
- #ifdef CONFIG_AUDIO_IN_SPDIFRX_SUPPORT
- static int audio_in_disable_spdifrx(struct device *dev, ain_session_t *session)
- {
- struct ain_drv_data *data = dev->data;
- struct device *spdifrx = data->spdifrx0_dev;
- if (!spdifrx) {
- LOG_ERR("Physical SPDIFRX device is not esixted");
- return -EFAULT;
- }
- return phy_audio_disable(spdifrx, NULL);
- }
- #endif
- /* @brief Disable audio in channel by specified session handler */
- static int acts_audio_in_close(struct device *dev, void *handle)
- {
- struct ain_drv_data *data = dev->data;
- uint8_t channel_type;
- int ret;
- ain_session_t *session = (ain_session_t *)handle;
- if (!handle) {
- LOG_ERR("Invalid handle");
- return -EINVAL;
- }
- audio_in_lock(dev);
- if (!AIN_SESSION_CHECK_MAGIC(session->magic)) {
- LOG_ERR("Session magic error:0x%x", session->magic);
- ret = -EFAULT;
- goto out;
- }
- channel_type = session->channel_type;
- if (channel_type == AUDIO_CHANNEL_ADC) {
- #ifdef CONFIG_AUDIO_IN_ADC_SUPPORT
- ret = audio_in_disable_adc(dev, session);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type == AUDIO_CHANNEL_I2SRX) {
- #ifdef CONFIG_AUDIO_IN_I2SRX_SUPPORT
- ret = audio_in_disable_i2srx(dev, session);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type == AUDIO_CHANNEL_SPDIFRX) {
- #ifdef CONFIG_AUDIO_IN_SPDIFRX_SUPPORT
- ret = audio_in_disable_spdifrx(dev, session);
- #else
- ret = -ENXIO;
- #endif
- } else {
- LOG_ERR("Invalid channel type %d", channel_type);
- ret = -EINVAL;
- goto out;
- }
- if (ret) {
- LOG_ERR("Disable channel type %d error:%d", channel_type, ret);
- goto out;
- }
- /* stop and free dma channel resource */
- if (data->dma_dev && session->dma_chan) {
- dma_stop(data->dma_dev, session->dma_chan);
- dma_free(data->dma_dev, session->dma_chan);
- }
- LOG_INF("session#%d@%p closed", session->id, session);
- audio_in_session_put(session);
- out:
- audio_in_unlock(dev);
- return ret;
- }
- static int acts_audio_in_control(struct device *dev, void *handle, int cmd, void *param)
- {
- struct ain_drv_data *data = dev->data;
- ain_session_t *session = (ain_session_t *)handle;
- uint8_t channel_type = session->channel_type;
- int ret;
- LOG_DBG("session#0x%x cmd 0x%x", (uint32_t)handle, cmd);
- if (cmd == AIN_CMD_ANC_CONTROL) {
- audio_in_lock(dev);
- ret = phy_audio_control(data->adc0_dev, AIN_CMD_ANC_CONTROL, param);
- audio_in_unlock(dev);
- return ret;
- }
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- if (AIN_CMD_DEBUG_PERFORMANCE_CTL_ALL == cmd)
- return audio_in_debug_perf_all_sessions(*(uint8_t *)param);
- if (AIN_CMD_DEBUG_DUMP_LENGTH_ALL == cmd)
- return audio_in_debug_dump_length(*(uint8_t *)param);
- #endif
- if (!AIN_SESSION_CHECK_MAGIC(session->magic)) {
- LOG_ERR("Session magic error:0x%x", session->magic);
- return -EFAULT;
- }
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- if (AIN_CMD_DEBUG_PERFORMANCE_CTL == cmd) {
- session->debug.performance = *(uint8_t *)param;
- session->debug.per_second_size = 0;
- session->debug.timestamp = 0;
- session->debug.dump_len = 0;
- return 0;
- }
- if (AIN_CMD_DEBUG_DUMP_LENGTH == cmd) {
- session->debug.dump_len = *(uint8_t *)param;
- return 0;
- }
- #endif
- if (cmd == AIN_CMD_BIND_CHANNEL)
- return audio_in_session_bind(dev, session, (ain_session_t *)param);
- if (cmd == AIN_CMD_SET_SEPARATED_MODE) {
- return audio_in_dma_separated_enable(dev, session,
- (audio_interleave_mode_e *)param);
- }
- if (channel_type == AUDIO_CHANNEL_ADC) {
- #ifdef CONFIG_AUDIO_IN_ADC_SUPPORT
- if (AIN_CMD_SET_ADC_GAIN == cmd) {
- adc_setting_t setting = {0};
- adc_gain *gain = (adc_gain *)param;
- setting.device = session->input_dev;
- memcpy(&setting.gain, gain, sizeof(adc_gain));
- ret = phy_audio_control(data->adc0_dev, PHY_CMD_ADC_GAIN_CONFIG, &setting);
- } else if ((AIN_CMD_GET_ADC_LEFT_GAIN_RANGE == cmd)
- || (AIN_CMD_GET_ADC_RIGHT_GAIN_RANGE == cmd)) {
- adc_gain_range *range = (adc_gain_range *)param;
- /* use the 'min' to send the input device info to phy-adc device */
- range->min = (int16_t)session->input_dev;
- ret = phy_audio_control(data->adc0_dev, cmd, param);
- } else if (AIN_CMD_GET_ADC_FIFO_DRQ_LEVEL == cmd) {
- uint32_t fifo_cmd = PHY_FIFO_CMD(session->input_dev, 0);
- ret = phy_audio_control(data->adc0_dev,
- PHY_CMD_FIFO_DRQ_LEVEL_GET, &fifo_cmd);
- if (!ret)
- *(uint8_t *)param = PHY_GET_FIFO_CMD_VAL(fifo_cmd);
- } else if (AIN_CMD_SET_ADC_FIFO_DRQ_LEVEL == cmd) {
- uint8_t level = *(uint8_t *)param;
- uint32_t fifo_cmd = PHY_FIFO_CMD(session->input_dev, level);
- ret = phy_audio_control(data->adc0_dev,
- PHY_CMD_FIFO_DRQ_LEVEL_SET, &fifo_cmd);
- } else {
- ret = phy_audio_control(data->adc0_dev, cmd, param);
- }
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type == AUDIO_CHANNEL_I2SRX) {
- #ifdef CONFIG_AUDIO_IN_I2SRX_SUPPORT
- ret = phy_audio_control(data->i2srx0_dev, cmd, param);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type == AUDIO_CHANNEL_SPDIFRX) {
- #ifdef CONFIG_AUDIO_IN_SPDIFRX_SUPPORT
- ret = phy_audio_control(data->spdifrx0_dev, cmd, param);
- #else
- ret = -ENXIO;
- #endif
- } else {
- LOG_ERR("Invalid channel type %d", channel_type);
- ret = -EINVAL;
- }
- return ret;
- }
- /* @brief Start the audio input transfering */
- static int acts_audio_in_start(struct device *dev, void *handle)
- {
- struct ain_drv_data *data = dev->data;
- ain_session_t *session = (ain_session_t *)handle;
- int ret;
- if (!handle) {
- LOG_ERR("Invalid handle");
- return -EINVAL;
- }
- if (!AIN_SESSION_CHECK_MAGIC(session->magic)) {
- LOG_ERR("Session magic error:0x%x", session->magic);
- return -EFAULT;
- }
- /* In DMA reload mode only start one time is enough */
- if (session->flags & AIN_SESSION_START) {
- LOG_DBG("session%p already started", session);
- return 0;
- }
- if (AUDIO_CHANNEL_ADC == session->channel_type) {
- if (data->adc_ref_counter) {
- if (data->adc_sess_stack_cur >= AIN_ADC_SESSION_MAX) {
- /* unlikely branch */
- LOG_ERR("FIXME: invlaid stack cur%d", data->adc_sess_stack_cur);
- return -EFAULT;
- }
- data->adc_sess_stack[data->adc_sess_stack_cur++] = (uint32_t)session;
- --data->adc_ref_counter;
- LOG_INF("session@%p save to wait stack[%d]", session, data->adc_ref_counter);
- return 0;
- }
- struct aduio_in_adc_en adc_en = {0};
- uint16_t input_dev[AIN_ADC_SESSION_MAX] = {0};
- adc_en.input_dev_array = input_dev;
- if (data->adc_sess_stack_cur > 0) {
- uint8_t i;
- ain_session_t *_sess = NULL;
- data->adc_sess_stack[data->adc_sess_stack_cur++] = (uint32_t)session;
- for (i = 0; i < data->adc_sess_stack_cur; i++) {
- _sess = (ain_session_t *)data->adc_sess_stack[i];
- if (!(_sess->flags & AIN_SESSION_CONFIG)
- || (_sess->flags & AIN_SESSION_START)
- || (_sess->channel_type != AUDIO_CHANNEL_ADC)) {
- /* unlikely branch */
- LOG_ERR("FIXME: invalid session@%p status:0x%x", _sess, _sess->flags);
- data->adc_sess_stack_cur = 0;
- return -ESRCH;
- }
- input_dev[i] = _sess->input_dev;
- }
- adc_en.input_dev_num = data->adc_sess_stack_cur;
- /* always set start flag regardless of the result of start */
- session->flags |= AIN_SESSION_START;
- ret = phy_audio_control(data->adc0_dev, PHY_CMD_ADC_DIGITAL_ENABLE, &adc_en);
- if (ret) {
- LOG_ERR("Failed to enable ADC err=%d", ret);
- data->adc_sess_stack_cur = 0;
- return ret;
- }
- for (i = 0; i < data->adc_sess_stack_cur; i++) {
- _sess = (ain_session_t *)data->adc_sess_stack[i];
- ret = audio_in_dma_prepare(dev, _sess);
- if (ret) {
- LOG_ERR("session@%p DMA prepare error=%d", session, ret);
- continue;
- }
- ret = dma_start(data->dma_dev, _sess->dma_chan);
- if (ret) {
- LOG_ERR("session@%p DMA start error:%d", _sess, ret);
- } else {
- LOG_INF("session@%p started", _sess);
- }
- }
- data->adc_sess_stack_cur = 0;
- } else {
- input_dev[0] = session->input_dev;
- adc_en.input_dev_num = 1;
- ret = phy_audio_control(data->adc0_dev, PHY_CMD_ADC_DIGITAL_ENABLE, &adc_en);
- if (ret) {
- LOG_ERR("Failed to enable ADC err=%d", ret);
- return ret;
- }
- ret = audio_in_dma_prepare(dev, session);
- if (ret) {
- LOG_ERR("session@%p DMA prepare error=%d", session, ret);
- return ret;
- }
- ret = dma_start(data->dma_dev, session->dma_chan);
- if (ret) {
- LOG_ERR("session@%p DMA start error:%d", session, ret);
- } else {
- LOG_INF("session@%p started", session);
- }
- }
- } else {
- session->flags |= AIN_SESSION_START;
- ret = audio_in_dma_prepare(dev, session);
- if (ret) {
- LOG_ERR("session@%p DMA prepare error=%d", session, ret);
- return ret;
- }
- ret = dma_start(data->dma_dev, session->dma_chan);
- if (ret) {
- LOG_ERR("session@%p DMA start error:%d", session, ret);
- } else {
- LOG_INF("session@%p started", session);
- }
- }
- return ret;
- }
- static int acts_audio_in_stop(struct device *dev, void *handle)
- {
- struct ain_drv_data *data = dev->data;
- ain_session_t *session = (ain_session_t *)handle;
- int ret = 0;
- if (session && AIN_SESSION_CHECK_MAGIC(session->magic)) {
- LOG_INF("session#%p, audio in stop", session);
- if (data->dma_dev && session->dma_chan)
- ret = dma_stop(data->dma_dev, session->dma_chan);
- }
- return ret;
- }
- const struct ain_driver_api ain_drv_api = {
- .ain_open = acts_audio_in_open,
- .ain_close = acts_audio_in_close,
- .ain_control = acts_audio_in_control,
- .ain_start = acts_audio_in_start,
- .ain_stop = acts_audio_in_stop
- };
- /* @brief audio-in channles initialization */
- static int audio_in_init(const struct device *dev)
- {
- const struct ain_config_data *cfg = dev->config;
- struct ain_drv_data *data = dev->data;
- memset(data, 0, sizeof(struct ain_drv_data));
- k_sem_init(&data->lock, 1, 1);
- #ifdef CONFIG_AUDIO_IN_ADC_SUPPORT
- data->adc0_dev = (struct device *)device_get_binding(cfg->adc0_name);
- if (!data->adc0_dev) {
- LOG_ERR("no ADC device(%s)", cfg->adc0_name);
- }
- #endif
- #ifdef CONFIG_AUDIO_IN_I2SRX_SUPPORT
- data->i2srx0_dev = (struct device *)device_get_binding(cfg->i2srx0_name);
- if (!data->i2srx0_dev) {
- LOG_ERR("no I2SRX device(%s)", cfg->i2srx0_name);
- }
- #endif
- #ifdef CONFIG_AUDIO_IN_SPDIFRX_SUPPORT
- data->spdifrx0_dev = (struct device *)device_get_binding(cfg->spdifrx0_name);
- if (!data->spdifrx0_dev) {
- LOG_ERR("no SPDIFRX device(%s)", cfg->spdifrx0_name);
- }
- #endif
- /* disable AVDD pulldown */
- sys_write32(sys_read32(AUDIOLDO_CTL) & ~(1 << 4), AUDIOLDO_CTL);
- printk("ACTS-AUDIO IN initialized\n");
- return 0;
- }
- static struct ain_drv_data audio_in_drv_data;
- static struct ain_config_data audio_in_config_data = {
- #ifdef CONFIG_AUDIO_IN_ADC_SUPPORT
- .adc0_name = CONFIG_AUDIO_ADC_0_NAME,
- #endif
- #ifdef CONFIG_AUDIO_IN_I2SRX_SUPPORT
- .i2srx0_name = CONFIG_AUDIO_I2SRX_0_NAME,
- #endif
- #ifdef CONFIG_AUDIO_IN_SPDIFRX_SUPPORT
- .spdifrx0_name = CONFIG_AUDIO_SPDIFRX_0_NAME,
- #endif
- };
- DEVICE_DEFINE(audio_in, CONFIG_AUDIO_IN_ACTS_DEV_NAME, audio_in_init,
- NULL,
- &audio_in_drv_data, &audio_in_config_data,
- POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &ain_drv_api);
|