12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784 |
- /*
- * Copyright (c) 2020 Actions Semiconductor Co., Ltd
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- /**
- * @file
- * @brief Audio OUTPUT Channels (DAC/I2S-TX/SPDIF-TX/PDM-TX) core management layer.
- */
- #include <kernel.h>
- #include <device.h>
- #include <ksched.h>
- #include <string.h>
- #include <errno.h>
- #include <soc.h>
- #include <board_cfg.h>
- #include <drivers/dma.h>
- #include <drivers/audio/audio_out.h>
- #include "phy_audio_common.h"
- #include <logging/log.h>
- LOG_MODULE_REGISTER(aout, LOG_LEVEL_DBG);
- #ifdef CONFIG_BOARD_EXTERNAL_PA_ENABLE
- #include <board.h>
- #endif
- /* Audio out session resource definitions */
- #define AOUT_SESSION_MAGIC (0x1a2b3c4d)
- #define AOUT_SESSION_CHECK_MAGIC(x) ((x) == AOUT_SESSION_MAGIC)
- /* Audio output channel sessions which depends on different chipsets */
- #ifdef CONFIG_SOC_SERIES_PEARLRIVER
- #define AOUT_DAC_SESSION_MAX (0) /* 0 DAC */
- #define AOUT_I2STX_SESSION_MAX (1) /* 1 I2STX */
- #define AOUT_SPDIFTX_SESSION_MAX (0) /* 0 SPDIFTX */
- #define AOUT_PDMTX_SESSION_MAX (1) /* 1 PDMTX */
- #else
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- #define AOUT_DAC_SESSION_MAX (2) /* 2 DAC */
- #else
- #define AOUT_DAC_SESSION_MAX (0)
- #endif
- #ifdef CONFIG_AUDIO_OUT_I2STX_SUPPORT
- #define AOUT_I2STX_SESSION_MAX (2) /* 1 I2STX */
- #else
- #define AOUT_I2STX_SESSION_MAX (0)
- #endif
- #ifdef CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT
- #define AOUT_SPDIFTX_SESSION_MAX (2) /* 1 SPDIFTX */
- #else
- #define AOUT_SPDIFTX_SESSION_MAX (0)
- #endif
- #define AOUT_PDMTX_SESSION_MAX (0) /* 0 PDMTX */
- #endif
- #define AOUT_SESSION_MAX (AOUT_DAC_SESSION_MAX + AOUT_I2STX_SESSION_MAX + \
- AOUT_SPDIFTX_SESSION_MAX + AOUT_PDMTX_SESSION_MAX) /* totally max sessions */
- #define AOUT_SESSION_OPEN (1 << 0) /* session open flag */
- #define AOUT_SESSION_CONFIG (1 << 1) /* session config flag */
- #define AOUT_SESSION_START (1 << 2) /* session start flag */
- #define AOUT_SESSION_WAIT_PA_TIMEOUT (2000) /* timeout of waitting pa session */
- #define DMA_IRQ_TC (0) /* DMA completion flag */
- #define DMA_IRQ_HF (1) /* DMA half-full flag */
- /* audio out io-commands following by the AUDIO FIFO usage */
- #define AOUT_IS_FIFO_CMD(x) ((x) & AOUT_FIFO_CMD_FLAG)
- /**
- * struct aout_dynamic_debug_t
- * @brief audio out 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; /* show the length of buffer per-second */
- uint8_t performance: 1; /* enable flag of showing the play performance */
- } aout_dynamic_debug_t;
- /**
- * struct aout_session_t
- * @brief audio out session structure
- */
- typedef struct {
- int magic; /* session magic value as AOUT_SESSION_MAGIC */
- int dma_chan; /* DMA channel handler */
- audio_outfifo_sel_e outfifo_type; /* indicator of the audio output fifo type */
- uint16_t channel_type; /* indicator of the channel type selection */
- uint16_t flags; /* session flags */
- int (*callback)(void *cb_data, uint32_t reason); /* callback function of the channel data link */
- 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 */
- uint8_t sample_rate; /* the channel sample rate setting */
- uint8_t id; /* the session id */
- uint8_t dma_width; /* dma width */
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- aout_dynamic_debug_t debug; /* dynamic debug */
- #endif
- uint8_t reload_en : 1; /* the flag of reload mode enable or not */
- uint8_t dma_separated_en : 1; /* the flag of DMA interleaved mode enable or not */
- uint8_t dsp_fifo_src : 1;
- } aout_session_t;
- /**
- * struct aout_drv_data
- * @brief The software related data that used by audio out driver.
- */
- struct aout_drv_data {
- struct k_sem lock; /* the semaphore to lock the channel resource */
- struct device *dma_dev; /* DMA device handler */
- struct device *dac0_dev; /* DAC0 device handler */
- struct device *i2stx0_dev; /* I2STX0 device handler */
- struct device *spdiftx0_dev; /* SPFITX0 device handler */
- struct device *pdmtx0_dev; /* PDMTX0 device handler */
- #ifdef CONFIG_AUDIO_PA_AW87390_SUPPORT
- struct device *pa_aw87390_dev;
- #endif
- uint8_t pa_session_indicator : 1; /* indicates the stage of the pa session operations */
- uint8_t dac_fifo_ref_counter : 2; /* DAC FIFO request by other's module reference counter */
- uint8_t external_pa_opened : 1; /* If 1 to indicate the external PA has already opened */
- };
- /**
- * struct aout_config_data
- * @brief The hardware related data that used by audio out driver.
- */
- struct aout_config_data {
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- const char *dac0_name; /* physical DAC0 device name */
- #endif
- #ifdef CONFIG_AUDIO_OUT_I2STX_SUPPORT
- const char *i2stx0_name; /* physical I2STX0 device name */
- #endif
- #ifdef CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT
- const char *spdiftx0_name; /* physical SPDIFTX device name */
- #endif
- #ifdef CONFIG_AUDIO_OUT_PDMTX_SUPPORT
- const char *pdmtx0_name; /* physical PDMTX device name */
- #endif
- #ifdef CONFIG_AUDIO_PA_AW87390_SUPPORT
- const char *pa_aw87390_name;
- #endif
- };
- /* audio out sessions */
- static aout_session_t aout_sessions[AOUT_SESSION_MAX];
- /*
- * @brief check if there is the same channel within audio out sessions.
- * Moreover check whether the session type is invalid or not.
- */
- static bool audio_out_session_check(uint16_t type)
- {
- int i;
- aout_session_t *sess = aout_sessions;
- uint8_t max_sess, sess_opened = 0;
- if (AUDIO_CHANNEL_DAC & type) {
- max_sess = AOUT_DAC_SESSION_MAX;
- } else if (AUDIO_CHANNEL_I2STX & type) {
- max_sess = AOUT_I2STX_SESSION_MAX;
- } else if (AUDIO_CHANNEL_SPDIFTX & type) {
- max_sess = AOUT_SPDIFTX_SESSION_MAX;
- } else if (AUDIO_CHANNEL_PDMTX & type) {
- max_sess = AOUT_PDMTX_SESSION_MAX;
- } else {
- LOG_ERR("Invalid session type %d", type);
- return true;
- }
- /* check if max session is 0 */
- if (!max_sess) {
- LOG_ERR("type#%d max session (%d) forbidden", type, max_sess);
- return true;
- }
- /* find the session that already opened */
- for (i = 0; i < AOUT_SESSION_MAX; i++, sess++) {
- if (AOUT_SESSION_CHECK_MAGIC(sess->magic)
- && (sess->flags & AOUT_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 out session by specified channel type.
- */
- static aout_session_t *audio_out_session_get(uint16_t type)
- {
- aout_session_t *sess = aout_sessions;
- int i;
- if (audio_out_session_check(type))
- return NULL;
- for (i = 0; i < AOUT_SESSION_MAX; i++, sess++) {
- if (!(sess->flags & AOUT_SESSION_OPEN)) {
- memset(sess, 0, sizeof(aout_session_t));
- sess->magic = AOUT_SESSION_MAGIC;
- sess->flags = AOUT_SESSION_OPEN;
- sess->channel_type = type;
- sess->id = i;
- sess->dma_chan = -1;
- sess->dsp_fifo_src = 0;
- return sess;
- }
- }
- return NULL;
- }
- /*
- * @brief Put audio out session by given session address
- */
- static void audio_out_session_put(aout_session_t *s)
- {
- aout_session_t *sess = aout_sessions;
- int i;
- for (i = 0; i < AOUT_SESSION_MAX; i++, sess++) {
- if ((uint32_t)s == (uint32_t)sess) {
- memset(s, 0, sizeof(aout_session_t));
- break;
- }
- }
- }
- /* @brief audio out session lock */
- static inline void audio_out_lock(struct device *dev)
- {
- struct aout_drv_data *data = dev->data;
- k_sem_take(&data->lock, K_FOREVER);
- }
- /* @brief audio out session unlock */
- static inline void audio_out_unlock(struct device *dev)
- {
- struct aout_drv_data *data = dev->data;
- k_sem_give(&data->lock);
- }
- #if defined(CONFIG_AUDIO_OUT_DAC_SUPPORT) || defined(CONFIG_AUDIO_POWERON_OPEN_I2STX)
- /* @brief the dummy callback for aout inner driver usage */
- static int acts_audio_dummy_callback(void *cb_data, uint32_t reason)
- {
- ARG_UNUSED(cb_data);
- ARG_UNUSED(reason);
- return 0;
- }
- #endif
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- /* @brief wait the pa operations finish */
- static int acts_audio_wait_pa_session(struct device *dev, uint32_t timeout_ms)
- {
- struct aout_drv_data *data = dev->data;
- uint32_t start_time, curr_time;
- start_time = k_cycle_get_32();
- while (data->pa_session_indicator) {
- curr_time = k_cycle_get_32();
- if (k_cyc_to_us_floor32(curr_time - start_time)
- >= (timeout_ms * 1000)) {
- LOG_ERR("wait pa session timeout");
- return -ETIMEDOUT;
- }
- /* PM works in IDLE thread and not allow to sleep */
- if (!z_is_idle_thread_object(_current))
- k_sleep(K_MSEC(2));
- }
- return 0;
- }
- /* @brief open a pa session to power charge the DAC digital and analog part */
- static void *acts_audio_open_pa_session(struct device *dev)
- {
- aout_param_t aout_setting = {0};
- dac_setting_t dac_setting = {0};
- void *handle;
- aout_setting.sample_rate = SAMPLE_RATE_48KHZ;
- aout_setting.channel_type = AUDIO_CHANNEL_DAC;
- aout_setting.channel_width = CHANNEL_WIDTH_16BITS;
- aout_setting.callback = acts_audio_dummy_callback;
- aout_setting.cb_data = (void *)dev;
- dac_setting.channel_mode = MONO_MODE;
- aout_setting.dac_setting = &dac_setting;
- handle = audio_out_open(dev, &aout_setting);
- #ifdef CONFIG_BOARD_EXTERNAL_PA_ENABLE
- struct aout_drv_data *data = dev->data;
- if (handle)
- data->pa_session_indicator = 1;
- #endif
- return handle;
- }
- /* @brief close the pa session */
- static void acts_audio_close_pa_session(struct device *dev, void *hdl)
- {
- audio_out_close(dev, hdl);
- #ifdef CONFIG_BOARD_EXTERNAL_PA_ENABLE
- struct aout_drv_data *data = dev->data;
- data->pa_session_indicator = 0;
- #endif
- }
- #ifdef CONFIG_BOARD_EXTERNAL_PA_ENABLE
- /* @brief open external PA */
- static void acts_audio_open_external_pa(struct device *dev, bool force)
- {
- struct aout_drv_data *data = dev->data;
- if (((!data->external_pa_opened) && (!data->pa_session_indicator)) || (force)) {
- board_extern_pa_ctl(CONFIG_EXTERN_PA_CLASS, true);
- data->external_pa_opened = 1;
- }
- }
- /* @brief close external PA */
- static void acts_audio_close_external_pa(struct device *dev)
- {
- struct aout_drv_data *data = dev->data;
- if (data->external_pa_opened) {
- board_extern_pa_ctl(CONFIG_EXTERN_PA_CLASS, false);
- data->external_pa_opened = 0;
- }
- }
- #endif
- /* @brief open the DAC internal and external PA */
- static int acts_audio_open_pa(struct device *dev, bool open_exteranl_pa)
- {
- void *pa_session = NULL;
- uint32_t pcm_data = 0;
- uint8_t i = 0;
- struct aout_drv_data *data = dev->data;
- phy_audio_control(data->dac0_dev, AOUT_CMD_OPEN_PA, NULL);
- pa_session = acts_audio_open_pa_session(dev);
- if (!pa_session) {
- LOG_ERR("Failed to open pa session");
- return -ENXIO;
- }
- /* FIXME: it is necessary to send some samples(zero data) when startup
- * to avoid the noise with probability.
- */
- for (i = 0; i < 16; i++) // TODO check this issue!!
- audio_out_write(dev, pa_session, (uint8_t *)&pcm_data, 4);
- #ifdef CONFIG_BOARD_EXTERNAL_PA_ENABLE
- if (open_exteranl_pa)
- acts_audio_open_external_pa(dev, true);
- #endif
- acts_audio_close_pa_session(dev, pa_session);
- LOG_INF("Open PA successfully");
- return 0;
- }
- /* @brief close DAC internal and external PA */
- static int acts_audio_close_pa(struct device *dev)
- {
- void *pa_session = NULL;
- struct aout_drv_data *data = dev->data;
- pa_session = acts_audio_open_pa_session(dev);
- if (!pa_session) {
- LOG_ERR("Failed to open pa session");
- return -ENXIO;
- }
- /* close external pa */
- #ifdef CONFIG_BOARD_EXTERNAL_PA_ENABLE
- acts_audio_close_external_pa(dev);
- #endif
- acts_audio_close_pa_session(dev, pa_session);
- /* close internal pa */
- phy_audio_control(data->dac0_dev, AOUT_CMD_CLOSE_PA, NULL);
- LOG_INF("Close PA successfully");
- return 0;
- }
- static int acts_audio_pa_class_select(struct device *dev, uint8_t pa_class)
- {
- ARG_UNUSED(dev);
- #ifdef CONFIG_BOARD_EXTERNAL_PA_ENABLE
- return board_extern_pa_ctl(pa_class, true);
- #else
- return -1;
- #endif
- }
- #endif
- #ifdef CONFIG_AUDIO_POWERON_OPEN_I2STX
- static int acts_audio_open_i2stx_dev(struct device *dev)
- {
- aout_param_t aout_setting = {0};
- i2stx_setting_t i2stx_setting = {0};
- void *handle;
- aout_setting.sample_rate = SAMPLE_RATE_48KHZ;
- aout_setting.channel_type = AUDIO_CHANNEL_I2STX;
- aout_setting.channel_width = CHANNEL_WIDTH_16BITS;
- aout_setting.callback = acts_audio_dummy_callback;
- aout_setting.cb_data = (void *)dev;
- i2stx_setting.mode = I2S_MASTER_MODE;
- aout_setting.i2stx_setting = &i2stx_setting;
- handle = audio_out_open(dev, &aout_setting);
- /* close interface will not disable i2stx truly*/
- audio_out_close(dev, handle);
- return 0;
- }
- static int acts_audio_close_i2stx_dev(struct device *dev)
- {
- struct aout_drv_data *data = dev->data;
- phy_audio_control(data->i2stx, AOUT_CMD_CLOSE_I2STX_DEVICE, NULL);
- return 0;
- }
- #endif
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- /* @brief show the audio out performance */
- static void audio_out_debug_perf(aout_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();
- LOG_INF("** out 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_out_debug_perf_all_sessions(uint8_t is_en)
- {
- int i;
- aout_session_t *sess = aout_sessions;
- for (i = 0; i < AOUT_SESSION_MAX; i++, sess++) {
- if (AOUT_SESSION_CHECK_MAGIC(sess->magic)
- && (sess->flags & AOUT_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_out_debug_dump_length(uint8_t length)
- {
- int i;
- aout_session_t *sess = aout_sessions;
- for (i = 0; i < AOUT_SESSION_MAX; i++, sess++) {
- if (AOUT_SESSION_CHECK_MAGIC(sess->magic)
- && (sess->flags & AOUT_SESSION_OPEN)) {
- sess->debug.dump_len = length;
- }
- }
- return 0;
- }
- #endif
- /* @brief Enable the DAC channel */
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- static int audio_out_enable_dac(struct device *dev, aout_session_t *session, aout_param_t *param)
- {
- uint8_t channel_type = param->channel_type;
- struct aout_drv_data *data = dev->data;
- struct device *dac = data->dac0_dev;
- dac_setting_t *dac_setting = param->dac_setting;
- if (!dac) {
- LOG_ERR("no dac device");
- return -ENXIO;
- }
- if (!dac_setting) {
- LOG_ERR("DAC setting is NULL");
- return -EINVAL;
- }
- /* Check the DAC FIFO usage */
- if ((param->outfifo_type != AOUT_FIFO_DAC0)
- && (param->outfifo_type != AOUT_FIFO_DAC1)) {
- LOG_ERR("Channel fifo type invalid %d", param->outfifo_type);
- return -EINVAL;
- }
- if ((param->channel_width != CHANNEL_WIDTH_16BITS)
- && (param->channel_width != CHANNEL_WIDTH_24BITS)) {
- LOG_ERR("Invalid channel width %d", param->channel_width);
- return -EINVAL;
- }
- /* Linkage with I2STX */
- if (channel_type & AUDIO_CHANNEL_I2STX) {
- if (!data->i2stx0_dev) {
- LOG_ERR("no i2stx device");
- return -ENXIO;
- }
- if (phy_audio_enable(data->i2stx0_dev, (void *)param)) {
- LOG_ERR("Failed to enable I2STX");
- return -EFAULT;
- }
- else
- return 0;
- }
- /* Linkage with SPDIFTX */
- if (channel_type & AUDIO_CHANNEL_SPDIFTX) {
- if (!data->spdiftx0_dev) {
- LOG_ERR("no spdiftx device");
- return -ENXIO;
- }
- if (!phy_audio_enable(data->spdiftx0_dev, (void *)param))
- phy_audio_control(dac, PHY_CMD_CLAIM_WITH_128FS, NULL);
- }
- return phy_audio_enable(dac, (void *)param);
- }
- #endif
- /* @brief Claim the use of the DAC/I2STX/PDMTX FIFO */
- #if defined(CONFIG_AUDIO_OUT_I2STX_SUPPORT) \
- || defined(CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT)
- static int audio_out_fifo_request(struct device *dev, aout_param_t *param)
- {
- if (!dev)
- return -EINVAL;
- if (phy_audio_control(dev, PHY_CMD_FIFO_GET, param)) {
- LOG_ERR("Request FIFO failed");
- return -EBUSY;
- }
- return 0;
- }
- /* @brief Release the use of the DAC/I2STX FIFO */
- static int audio_out_fifo_release(struct device *dev, uint8_t idx)
- {
- if (!dev)
- return -EINVAL;
- if (phy_audio_control(dev, PHY_CMD_FIFO_PUT, &idx)) {
- LOG_ERR("Release DAC FIFO failed");
- return -EFAULT;
- }
- return 0;
- }
- #endif
- /* @brief Enable the I2STX channel */
- #ifdef CONFIG_AUDIO_OUT_I2STX_SUPPORT
- static int audio_out_enable_i2stx(struct device *dev, aout_session_t *session, aout_param_t *param)
- {
- uint8_t channel_type = param->channel_type;
- struct aout_drv_data *data = dev->data;
- struct device *i2stx = data->i2stx0_dev;
- i2stx_setting_t *i2stx_setting = param->i2stx_setting;
- int ret;
- if (!i2stx) {
- LOG_ERR("no i2s-tx device");
- return -ENXIO;
- }
- if (!i2stx_setting) {
- LOG_ERR("I2STX setting is NULL");
- return -EINVAL;
- }
- /* Check the DAC FIFO usage */
- if ((param->outfifo_type != AOUT_FIFO_DAC0)
- && (param->outfifo_type != AOUT_FIFO_DAC1)
- && (param->outfifo_type != AOUT_FIFO_I2STX0)) {
- LOG_ERR("Channel fifo type invalid %d", param->outfifo_type);
- return -EINVAL;
- }
- if ((param->channel_width != CHANNEL_WIDTH_16BITS)
- && (param->channel_width != CHANNEL_WIDTH_24BITS)
- && (param->channel_width != CHANNEL_WIDTH_20BITS)) {
- LOG_ERR("Invalid channel width %d", param->channel_width);
- return -EINVAL;
- }
- /* Linkage with SPDIFTX */
- if (channel_type & AUDIO_CHANNEL_SPDIFTX) {
- if (!data->spdiftx0_dev) {
- LOG_ERR("no spdiftx device");
- return -ENXIO;
- }
- if (!phy_audio_enable(data->spdiftx0_dev, param))
- phy_audio_control(data->dac0_dev, PHY_CMD_CLAIM_WITH_128FS, NULL);
- }
- if ((AOUT_FIFO_DAC0 == param->outfifo_type)
- || (AOUT_FIFO_DAC1 == param->outfifo_type)) {
- if (audio_out_fifo_request(data->dac0_dev, param)) {
- LOG_ERR("Failed to take DAC FIFO");
- return -ENXIO;
- }
- ++data->dac_fifo_ref_counter;
- }
- ret = phy_audio_enable(i2stx, (void *)param);
- if (ret) {
- if ((AOUT_FIFO_DAC0 == param->outfifo_type)
- || (AOUT_FIFO_DAC1 == param->outfifo_type)) {
- audio_out_fifo_release(data->dac0_dev, param->outfifo_type);
- --data->dac_fifo_ref_counter;
- }
- }
- return ret;
- }
- #endif
- /* @brief Enable the SPDIFTX channel */
- #ifdef CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT
- static int audio_out_enable_spdiftx(struct device *dev, aout_session_t *session, aout_param_t *param)
- {
- struct aout_drv_data *data = dev->data;
- struct device *spdiftx = data->spdiftx0_dev;
- spdiftx_setting_t *spdiftx_setting = param->spdiftx_setting;
- audio_dma_width_e width;
- int ret;
- if ((param->channel_width != CHANNEL_WIDTH_16BITS)
- && (param->channel_width != CHANNEL_WIDTH_24BITS)) {
- LOG_ERR("Invalid channel width %d", param->channel_width);
- return -EINVAL;
- }
- if (param->channel_width == CHANNEL_WIDTH_16BITS)
- width = DMA_WIDTH_16BITS;
- else
- width = DMA_WIDTH_32BITS;
- if (!spdiftx) {
- LOG_ERR("Physical SPDIFTX device is not esixted");
- return -EFAULT;
- }
- if (!spdiftx_setting) {
- LOG_ERR("SPDIFTX setting is NULL");
- return -EINVAL;
- }
- /* Check the DAC FIFO usage */
- if ((param->outfifo_type != AOUT_FIFO_DAC0)
- && (param->outfifo_type != AOUT_FIFO_DAC1)
- && (param->outfifo_type != AOUT_FIFO_I2STX0)
- && (param->outfifo_type != AOUT_FIFO_DAC1_ONLY_SPDIF)) {
- LOG_ERR("Channel fifo type invalid %d", param->outfifo_type);
- return -EINVAL;
- }
- if ((AOUT_FIFO_DAC0 == param->outfifo_type)
- || (AOUT_FIFO_DAC1 == param->outfifo_type)
- || (AOUT_FIFO_DAC1_ONLY_SPDIF == param->outfifo_type)) {
- if (!data->dac0_dev) {
- LOG_ERR("no dac device");
- return -ENXIO;
- }
- if (audio_out_fifo_request(data->dac0_dev, param)) {
- LOG_ERR("Failed to take DAC FIFO");
- return -ENXIO;
- }
- ++data->dac_fifo_ref_counter;
- } else if (AOUT_FIFO_I2STX0 == param->outfifo_type) {
- if (!data->i2stx0_dev) {
- LOG_ERR("no i2stx device");
- return -ENXIO;
- }
- if (audio_out_fifo_request(data->i2stx0_dev, param)) {
- LOG_ERR("Failed to take I2STX FIFO");
- return -ENXIO;
- }
- }
- phy_audio_control(data->dac0_dev, PHY_CMD_CLAIM_WITH_128FS, NULL);
- ret = phy_audio_enable(spdiftx, (void *)param);
- if (ret) {
- if ((AOUT_FIFO_DAC0 == param->outfifo_type)
- || (AOUT_FIFO_DAC1 == param->outfifo_type)
- || (AOUT_FIFO_DAC1_ONLY_SPDIF == param->outfifo_type)) {
- audio_out_fifo_release(data->dac0_dev, param->outfifo_type);
- --data->dac_fifo_ref_counter;
- }
- else if (AOUT_FIFO_I2STX0 == param->outfifo_type)
- audio_out_fifo_release(data->i2stx0_dev, param->outfifo_type);
- }
- return ret;
- }
- #endif
- /* @brief Enable the PDMTX channel */
- #ifdef CONFIG_AUDIO_OUT_PDMTX_SUPPORT
- static int audio_out_enable_pdmtx(struct device *dev, aout_session_t *session, aout_param_t *param)
- {
- uint8_t channel_type = param->channel_type;
- struct aout_drv_data *data = dev->data;
- struct device *pdmtx = data->pdmtx0_dev;
- int ret;
- if (!pdmtx) {
- LOG_ERR("no pdm-tx device");
- return -ENXIO;
- }
- if (channel_type != AUDIO_CHANNEL_PDMTX) {
- LOG_ERR("invalid channel type:0x%x", channel_type);
- return -EINVAL;
- }
- /* Check the DAC FIFO usage */
- if (param->outfifo_type != AOUT_FIFO_I2STX0) {
- LOG_ERR("Channel fifo type invalid %d", param->outfifo_type);
- return -EINVAL;
- }
- if (param->channel_width != CHANNEL_WIDTH_16BITS) {
- LOG_ERR("Invalid channel width %d", param->channel_width);
- return -EINVAL;
- }
- ret = phy_audio_enable(pdmtx, (void *)param);
- if (ret)
- LOG_ERR("physical pdmtx enable error:%d", ret);
- return ret;
- }
- #endif
- /* @brief DMA irq callback on direct method */
- static void audio_out_dma_direct(const struct device *dev, void *user_data,
- uint32_t channel, int status)
- {
- aout_session_t *session = (aout_session_t *)user_data;
- uint32_t key;
- ARG_UNUSED(dev);
- ARG_UNUSED(channel);
- if (session && AOUT_SESSION_CHECK_MAGIC(session->magic)) {
- /* Only notify when DMA transfer completly */
- if ((status == DMA_IRQ_TC) && session->callback) {
- key = irq_lock();
- session->callback(session->cb_data, AOUT_DMA_IRQ_TC);
- irq_unlock(key);
- }
- }
- }
- /* @brief DMA irq callback on reload method */
- static void audio_out_dma_reload(const struct device *dev, void *user_data,
- uint32_t channel, int status)
- {
- aout_session_t *session = (aout_session_t *)user_data;
- uint32_t ret_reason;
- int ret;
- ARG_UNUSED(dev);
- ARG_UNUSED(channel);
- if (session && AOUT_SESSION_CHECK_MAGIC(session->magic)) {
- if (session->callback) {
- if (status == DMA_IRQ_HF) {
- ret_reason = AOUT_DMA_IRQ_HF;
- } else if (status == DMA_IRQ_TC){
- ret_reason = AOUT_DMA_IRQ_TC;
- } else {
- LOG_ERR("Unknown DMA reson %d", status);
- return ;
- }
- ret = session->callback(session->cb_data, ret_reason);
- if (ret < 0)
- LOG_DBG("reload callback error:%d", ret);
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- audio_out_debug_perf(session, session->reload_addr,
- session->reload_len / 2);
- #endif
- }
- }
- }
- /* @brief audio out dma enable interleaved mode */
- static void audio_out_dma_separated_enable(struct device *dev, aout_session_t *session)
- {
- ARG_UNUSED(dev);
- if (session) {
- LOG_INF("Enable AOUT DMA separated mode");
- session->dma_separated_en = 1;
- }
- }
- /* @brief prepare the DMA configuration for the audio out transfer */
- static int audio_out_dma_prepare(struct device *dev, aout_session_t *session)
- {
- struct aout_drv_data *data = dev->data;
- struct dma_config dma_cfg = {0};
- struct dma_block_config dma_block_cfg = {0};
- struct audio_out_dma_info info = {0};
- /* check already configured */
- if (session->flags & AOUT_SESSION_CONFIG)
- return 0;
- dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL;
- dma_cfg.source_burst_length = 8;
- dma_cfg.dest_burst_length = 8;
- dma_block_cfg.source_reload_en = session->reload_en;
- dma_cfg.head_block = &dma_block_cfg;
- dma_cfg.source_data_size = session->dma_width;
- info.fifo_type = session->outfifo_type;
- if ((AOUT_FIFO_DAC0 == session->outfifo_type)
- || (AOUT_FIFO_DAC1 == session->outfifo_type)
- || (AOUT_FIFO_DAC1_ONLY_SPDIF == session->outfifo_type)) {
- if (phy_audio_control(data->dac0_dev, PHY_CMD_GET_AOUT_DMA_INFO, &info)) {
- LOG_ERR("Failed to get DAC DMA info");
- return -EFAULT;
- }
- uint8_t level, fifo_type = session->outfifo_type;
- if (fifo_type == AOUT_FIFO_DAC1_ONLY_SPDIF)
- fifo_type = AOUT_FIFO_DAC1;
- uint32_t fifo_cmd = PHY_FIFO_CMD(fifo_type, 0);
- if (!phy_audio_control(data->dac0_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 < 3)
- dma_cfg.dest_burst_length = 1;
- } else {
- LOG_ERR("Failed to get DRQ level");
- return -EFAULT;
- }
- } else if (AOUT_FIFO_I2STX0 == session->outfifo_type) {
- if (phy_audio_control(data->i2stx0_dev, PHY_CMD_GET_AOUT_DMA_INFO, &info)) {
- LOG_ERR("Failed to get I2STX DMA info");
- return -EFAULT;
- }
- }
- 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;
- #ifdef CONFIG_AUDIO_OUT_DAC_PCMBUF_SUPPORT
- if ((AOUT_FIFO_DAC0 != session->outfifo_type)
- && (AOUT_FIFO_DAC1 != session->outfifo_type))
- #endif
- {
- if (session->callback) {
- if (session->reload_en)
- dma_cfg.dma_callback = audio_out_dma_reload;
- else
- dma_cfg.dma_callback = audio_out_dma_direct;
- dma_cfg.user_data = 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_en) {
- if (session->dma_separated_en) {
- dma_reload(data->dma_dev, session->dma_chan,
- (uint32_t)session->reload_addr,
- (uint32_t)session->reload_addr, session->reload_len * 2);
- } else {
- dma_reload(data->dma_dev, session->dma_chan,
- (uint32_t)session->reload_addr, 0, session->reload_len);
- }
- }
- LOG_DBG("request DMA channel:0x%x, DMA slot:%d", session->dma_chan, dma_cfg.dma_slot);
- session->flags |= AOUT_SESSION_CONFIG;
- return 0;
- }
- /* @brief audio out open and return the session handler */
- static void *acts_audio_out_open(struct device *dev, aout_param_t *param)
- {
- aout_session_t *session = NULL;
- struct aout_drv_data *data = dev->data;
- struct device *dac = data->dac0_dev;
- dac_fifosrc_setting_t def_dac_fifosrc;
- uint8_t channel_type;
- int ret;
- if (!param) {
- LOG_ERR("NULL parameter");
- return NULL;
- }
- #ifdef CONFIG_AUDIO_PA_AW87390_SUPPORT
- phy_audio_enable(data->pa_aw87390_dev,NULL);
- #endif
- /* There is a 120ms yield scheduer under extern pa operation and need to wait until the pa session close */
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- if (acts_audio_wait_pa_session(dev, AOUT_SESSION_WAIT_PA_TIMEOUT))
- return NULL;
- #endif
- audio_out_lock(dev);
- channel_type = param->channel_type;
- session = audio_out_session_get(channel_type);
- if (!session) {
- LOG_ERR("Failed to get audio session (type:%d)", channel_type);
- audio_out_unlock(dev);
- return NULL;
- }
- if (!param->callback) {
- LOG_ERR("Channel callback is NULL");
- goto err;
- }
- session->outfifo_type = param->outfifo_type;
- session->sample_rate = param->sample_rate;
- session->channel_type = channel_type;
- session->callback = param->callback;
- session->cb_data = param->cb_data;
- def_dac_fifosrc.fifo_idx = session->id;
- def_dac_fifosrc.fifo_from_dsp = session->dsp_fifo_src;
- def_dac_fifosrc.dsp_audio_set_param = NULL;
- session->dma_width = (CHANNEL_WIDTH_16BITS == param->channel_width) ? 2 : 4;
- if (param->reload_setting) {
- if ((!param->reload_setting->reload_addr)
- || (!param->reload_setting->reload_len)) {
- LOG_ERR("Invalid reload parameter addr:0x%x, len:0x%x",
- (uint32_t)param->reload_setting->reload_addr,
- param->reload_setting->reload_len);
- goto err;
- }
- session->reload_en = 1;
- session->reload_addr = param->reload_setting->reload_addr;
- session->reload_len = param->reload_setting->reload_len;
- LOG_INF("In reload mode [0x%08x %d]",
- (uint32_t)session->reload_addr, session->reload_len);
- } else {
- session->reload_en = 0;
- }
- if (channel_type & AUDIO_CHANNEL_DAC) {
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- ret = audio_out_enable_dac(dev, session, param);
- phy_audio_control(dac, AOUT_CMD_SET_FIFO_SRC, &def_dac_fifosrc);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type & AUDIO_CHANNEL_I2STX) {
- #ifdef CONFIG_AUDIO_OUT_I2STX_SUPPORT
- ret = audio_out_enable_i2stx(dev, session, param);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type & AUDIO_CHANNEL_SPDIFTX) {
- #ifdef CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT
- ret = audio_out_enable_spdiftx(dev, session, param);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type & AUDIO_CHANNEL_PDMTX) {
- #ifdef CONFIG_AUDIO_OUT_PDMTX_SUPPORT
- ret = audio_out_enable_pdmtx(dev, session, param);
- #else
- ret = -ENXIO;
- #endif
- } else {
- LOG_ERR("Invalid channel type %d", channel_type);
- goto err;
- }
- if (ret) {
- LOG_ERR("Enable channel type %d error:%d", channel_type, ret);
- goto err;
- }
- LOG_INF("channel@%d sess:%p type:%d fifo:%d sr:%d opened",
- session->id, session, channel_type,
- param->outfifo_type, param->sample_rate);
- audio_out_unlock(dev);
- return (void *)session;
- err:
- audio_out_session_put(session);
- audio_out_unlock(dev);
- return NULL;
- }
- /* @brief Disable the DAC channel */
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- static int audio_out_disable_dac(struct device *dev, aout_session_t *session)
- {
- struct aout_drv_data *data = dev->data;
- struct device *dac = data->dac0_dev;
- uint8_t fifo_idx = session->outfifo_type;
- if (!dac) {
- LOG_ERR("no dac device");
- return -EFAULT;
- }
- if (session->channel_type & AUDIO_CHANNEL_SPDIFTX)
- phy_audio_control(dac, PHY_CMD_CLAIM_WITHOUT_128FS, NULL);
- #if defined(CONFIG_BOARD_EXTERNAL_PA_ENABLE) && (CONFIG_AUDIO_DAC_POWER_PREFERRED == 1)
- acts_audio_close_external_pa(dev);
- #endif
- return phy_audio_disable(dac, &fifo_idx);
- }
- #endif
- /* @brief Disable the I2STX channel */
- #ifdef CONFIG_AUDIO_OUT_I2STX_SUPPORT
- static int audio_out_disable_i2stx(struct device *dev, aout_session_t *session)
- {
- struct aout_drv_data *data = dev->data;
- struct device *i2stx = data->i2stx0_dev;
- uint8_t fifo_idx = session->outfifo_type;
- if (!i2stx) {
- LOG_ERR("Physical I2STX device is not esixted");
- return -EFAULT;
- }
- if ((AOUT_FIFO_DAC0 == session->outfifo_type)
- || (AOUT_FIFO_DAC1 == session->outfifo_type)) {
- audio_out_fifo_release(data->dac0_dev, fifo_idx);
- --data->dac_fifo_ref_counter;
- }
- if (data->dac_fifo_ref_counter) {
- LOG_INF("Still remain %d DAC FIFO now is using", data->dac_fifo_ref_counter);
- return 0;
- }
- if (session->channel_type & AUDIO_CHANNEL_SPDIFTX)
- phy_audio_control(data->dac0_dev, PHY_CMD_CLAIM_WITHOUT_128FS, NULL);
- return phy_audio_disable(i2stx, NULL);
- }
- #endif
- /* @brief Disable the SPDIFTX channel */
- #ifdef CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT
- static int audio_out_disable_spdiftx(struct device *dev, aout_session_t *session)
- {
- struct aout_drv_data *data = dev->data;
- struct device *spdiftx = data->spdiftx0_dev;
- uint8_t fifo_idx = session->outfifo_type;
- if (!spdiftx) {
- LOG_ERR("Physical SPDIFTX device is not esixted");
- return -EFAULT;
- }
- if ((AOUT_FIFO_DAC0 == session->outfifo_type)
- || (AOUT_FIFO_DAC1 == session->outfifo_type)
- || (AOUT_FIFO_DAC1_ONLY_SPDIF == session->outfifo_type)) {
- audio_out_fifo_release(data->dac0_dev, fifo_idx);
- --data->dac_fifo_ref_counter;
- } else if (AOUT_FIFO_I2STX0 == session->outfifo_type) {
- audio_out_fifo_release(data->i2stx0_dev, fifo_idx);
- }
- if (data->dac_fifo_ref_counter) {
- LOG_INF("Still remain %d DAC FIFO now is using", data->dac_fifo_ref_counter);
- return 0;
- }
- phy_audio_control(data->dac0_dev, PHY_CMD_CLAIM_WITHOUT_128FS, NULL);
- return phy_audio_disable(spdiftx, NULL);
- }
- #endif
- #ifdef CONFIG_AUDIO_OUT_PDMTX_SUPPORT
- static int audio_out_disable_pdmtx(struct device *dev, aout_session_t *session)
- {
- struct aout_drv_data *data = dev->data;
- struct device *pdmtx = data->pdmtx0_dev;
- if (!pdmtx) {
- LOG_ERR("Physical PDMTX device is not esixted");
- return -EFAULT;
- }
- return phy_audio_disable(pdmtx, NULL);
- }
- #endif
- /* @brief Close audio out channel by specified session handler */
- static int acts_audio_out_close(struct device *dev, void *handle)
- {
- struct aout_drv_data *data = dev->data;
- uint8_t channel_type;
- int ret;
- aout_session_t *session = (aout_session_t *)handle;
- if (!handle) {
- LOG_ERR("Invalid handle");
- return -EINVAL;
- }
- audio_out_lock(dev);
- if (!AOUT_SESSION_CHECK_MAGIC(session->magic)) {
- LOG_ERR("Session magic error:0x%x", session->magic);
- ret = -EFAULT;
- goto out;
- }
- channel_type = session->channel_type;
- #ifdef CONFIG_AUDIO_PA_AW87390_SUPPORT
- phy_audio_disable(data->pa_aw87390_dev,NULL);
- #endif
- if (channel_type & AUDIO_CHANNEL_DAC) {
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- ret = audio_out_disable_dac(dev, session);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type & AUDIO_CHANNEL_I2STX) {
- #ifdef CONFIG_AUDIO_OUT_I2STX_SUPPORT
- ret = audio_out_disable_i2stx(dev, session);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type & AUDIO_CHANNEL_SPDIFTX) {
- #ifdef CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT
- ret = audio_out_disable_spdiftx(dev, session);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type & AUDIO_CHANNEL_PDMTX) {
- #ifdef CONFIG_AUDIO_OUT_PDMTX_SUPPORT
- ret = audio_out_disable_pdmtx(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 >= 0)) {
- 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_out_session_put(session);
- out:
- audio_out_unlock(dev);
- return ret;
- }
- static int acts_audio_out_control(struct device *dev, void *handle, int cmd, void *param)
- {
- #if defined(CONFIG_AUDIO_OUT_DAC_SUPPORT) \
- || defined(CONFIG_AUDIO_OUT_I2STX_SUPPORT) \
- || defined(CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT)
- struct aout_drv_data *data = dev->data;
- #endif
- aout_session_t *session = (aout_session_t *)handle;
- uint8_t channel_type = session->channel_type;
- int ret;
- LOG_DBG("session#0x%x cmd 0x%x", (uint32_t)handle, cmd);
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- if (AOUT_CMD_OPEN_PA == cmd)
- return acts_audio_open_pa(dev, true);
- if (AOUT_CMD_CLOSE_PA == cmd)
- return acts_audio_close_pa(dev);
- if (AOUT_CMD_PA_CLASS_SEL == cmd)
- return acts_audio_pa_class_select(dev, *(uint8_t *)param);
- #endif
- #ifdef CONFIG_AUDIO_POWERON_OPEN_I2STX
- if (AOUT_CMD_OPEN_I2STX_DEVICE == cmd)
- return acts_audio_open_i2stx_dev(dev);
- if (AOUT_CMD_CLOSE_I2STX_DEVICE == cmd)
- return acts_audio_close_i2stx_dev(dev);
- #endif
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- if (AOUT_CMD_DEBUG_PERFORMANCE_CTL_ALL == cmd)
- return audio_out_debug_perf_all_sessions(*(uint8_t *)param);
- if (AOUT_CMD_DEBUG_DUMP_LENGTH_ALL == cmd)
- return audio_out_debug_dump_length(*(uint8_t *)param);
- #endif
- if (cmd == AOUT_CMD_ANC_CONTROL) {
- audio_out_lock(dev);
- ret = phy_audio_control(data->dac0_dev, AOUT_CMD_ANC_CONTROL, param);
- audio_out_unlock(dev);
- return ret;
- }
- if (!AOUT_SESSION_CHECK_MAGIC(session->magic)) {
- LOG_ERR("Session magic error:0x%x", session->magic);
- return -EFAULT;
- }
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- if (AOUT_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 (AOUT_CMD_DEBUG_DUMP_LENGTH == cmd) {
- session->debug.dump_len = *(uint8_t *)param;
- return 0;
- }
- #endif
- if (AOUT_CMD_SET_SEPARATED_MODE == cmd) {
- audio_out_dma_separated_enable(dev, session);
- return 0;
- }
- if (AOUT_CMD_SET_FIFO_SRC == cmd) {
- dac_fifosrc_setting_t *dac_fifosrc = (dac_fifosrc_setting_t*)param;
- session->dsp_fifo_src = dac_fifosrc->fifo_from_dsp ? 1:0;
- }
- /* In the case of the commands according to the FIFO attribute */
- if (AOUT_IS_FIFO_CMD(cmd)) {
- if ((AOUT_FIFO_DAC0 == session->outfifo_type)
- || (AOUT_FIFO_DAC1 == session->outfifo_type)
- || (AOUT_FIFO_DAC1_ONLY_SPDIF == session->outfifo_type)) {
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- uint32_t val = session->outfifo_type;
- if (val == AOUT_FIFO_DAC1_ONLY_SPDIF)
- val = AOUT_FIFO_DAC1;
- if (AOUT_CMD_GET_SAMPLE_CNT == cmd) {
- ret = phy_audio_control(data->dac0_dev,
- PHY_CMD_DAC_FIFO_GET_SAMPLE_CNT, &val);
- if (!ret)
- *(uint32_t *)param = val;
- else
- *(uint32_t *)param = 0;
- } else if (AOUT_CMD_RESET_SAMPLE_CNT == cmd) {
- ret = phy_audio_control(data->dac0_dev,
- PHY_CMD_DAC_FIFO_RESET_SAMPLE_CNT, &val);
- } else if (AOUT_CMD_ENABLE_SAMPLE_CNT == cmd) {
- ret = phy_audio_control(data->dac0_dev,
- PHY_CMD_DAC_FIFO_ENABLE_SAMPLE_CNT, &val);
- } else if (AOUT_CMD_DISABLE_SAMPLE_CNT == cmd) {
- ret = phy_audio_control(data->dac0_dev,
- PHY_CMD_DAC_FIFO_DISABLE_SAMPLE_CNT, &val);
- } else if (AOUT_CMD_GET_CHANNEL_STATUS == cmd) {
- ret = phy_audio_control(data->dac0_dev,
- AOUT_CMD_GET_CHANNEL_STATUS, &val);
- if (!ret)
- *(uint8_t *)param = val;
- } else if (PHY_CMD_FIFO_PUT == cmd) {
- ret = phy_audio_control(data->dac0_dev,
- PHY_CMD_FIFO_PUT, &val);
- } else if (AOUT_CMD_GET_DAC_FIFO_DRQ_LEVEL == cmd) {
- uint32_t fifo_cmd = PHY_FIFO_CMD(val, 0);
- ret = phy_audio_control(data->dac0_dev,
- PHY_CMD_FIFO_DRQ_LEVEL_GET, &fifo_cmd);
- if (!ret)
- *(uint8_t *)param = PHY_GET_FIFO_CMD_VAL(fifo_cmd);
- } else if (AOUT_CMD_SET_DAC_FIFO_DRQ_LEVEL == cmd) {
- uint8_t level = *(uint8_t *)param;
- uint32_t fifo_cmd = PHY_FIFO_CMD(val, level);
- ret = phy_audio_control(data->dac0_dev,
- PHY_CMD_FIFO_DRQ_LEVEL_SET, &fifo_cmd);
- } else if (AOUT_CMD_GET_DAC_FIFO_VOLUME == cmd) {
- uint32_t fifo_cmd = PHY_FIFO_CMD(val, 0);
- ret = phy_audio_control(data->dac0_dev,
- PHY_CMD_DAC_FIFO_VOLUME_GET, &fifo_cmd);
- if (!ret)
- *(uint8_t *)param = PHY_GET_FIFO_CMD_VAL(fifo_cmd);
- } else if (AOUT_CMD_SET_DAC_FIFO_VOLUME == cmd) {
- uint8_t volume = *(uint8_t *)param;
- uint32_t fifo_cmd = PHY_FIFO_CMD(val, volume);
- ret = phy_audio_control(data->dac0_dev,
- PHY_CMD_DAC_FIFO_VOLUME_SET, &fifo_cmd);
- } else {
- ret = phy_audio_control(data->dac0_dev, cmd, param);
- }
- #else
- ret = -ENXIO;
- #endif
- } else if (AOUT_FIFO_I2STX0 == session->outfifo_type) {
- #ifdef CONFIG_AUDIO_OUT_I2STX_SUPPORT
- ret = phy_audio_control(data->i2stx0_dev, cmd, param);
- LOG_INF("i2stx0_dev+++cmd %d", cmd);
- #else
- ret = -ENXIO;
- #endif
- } else {
- ret = -ENXIO;
- }
- goto out;
- }
- if (channel_type & AUDIO_CHANNEL_DAC) {
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- ret = phy_audio_control(data->dac0_dev, cmd, param);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type & AUDIO_CHANNEL_I2STX) {
- #ifdef CONFIG_AUDIO_OUT_I2STX_SUPPORT
- ret = phy_audio_control(data->i2stx0_dev, cmd, param);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type & AUDIO_CHANNEL_SPDIFTX) {
- #ifdef CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT
- ret = phy_audio_control(data->spdiftx0_dev, cmd, param);
- #else
- ret = -ENXIO;
- #endif
- } else if (channel_type & AUDIO_CHANNEL_PDMTX) {
- #ifdef CONFIG_AUDIO_OUT_PDMTX_SUPPORT
- ret = phy_audio_control(data->pdmtx0_dev, cmd, param);
- #else
- ret = -ENXIO;
- #endif
- } else {
- LOG_ERR("Invalid channel type %d", channel_type);
- ret = -EINVAL;
- }
- out:
- return ret;
- }
- static int acts_audio_out_start(struct device *dev, void *handle)
- {
- struct aout_drv_data *data = dev->data;
- aout_session_t *session = (aout_session_t *)handle;
- if (!handle) {
- LOG_ERR("Invalid handle");
- return -EINVAL;
- }
- if (!AOUT_SESSION_CHECK_MAGIC(session->magic)) {
- LOG_ERR("Session magic error:0x%x", session->magic);
- return -EFAULT;
- }
- if(!session->dsp_fifo_src) {
- if (audio_out_dma_prepare(dev, session)) {
- LOG_ERR("prepare session dma error");
- return -ENXIO;
- }
- }
- #ifdef CONFIG_BOARD_EXTERNAL_PA_ENABLE
- acts_audio_open_external_pa(dev, false);
- #endif
- /* In DMA reload mode only start one time is enough */
- if (session->reload_en
- && (session->flags & AOUT_SESSION_START)) {
- return 0;
- }
- if (!(session->flags & AOUT_SESSION_START))
- session->flags |= AOUT_SESSION_START;
- if(session->dsp_fifo_src) {
- return 0;
- }
- return dma_start(data->dma_dev, session->dma_chan);
- }
- #if defined(CONFIG_AUDIO_OUT_DAC_PCMBUF_SUPPORT) && (CONFIG_AUDIO_DAC_WAIT_WRITE_PCMBUF_FINISH == 1)
- static bool __acts_audio_check_dma_busy(struct device *dma_dev, uint32_t dma_chan)
- {
- int ret;
- struct dma_status stat = {0};
- ret = dma_get_status(dma_dev, dma_chan, &stat);
- if (ret) {
- LOG_ERR("get dma(%d) status error %d\n", dma_chan, ret);
- return -EFAULT;
- }
- /* DMA transfer finish */
- if (!stat.pending_length)
- return false;
- return true;
- }
- static int acts_audio_dma_wait_timeout(struct device *dma_dev, uint32_t dma_chan, uint32_t timeout)
- {
- uint32_t start_time, curr_time;
- LOG_DBG("audio wait write dma_chan:%d", dma_chan);
- start_time = k_cycle_get_32();
- while (1) {
- if (!__acts_audio_check_dma_busy(dma_dev, dma_chan))
- break;
- curr_time = k_cycle_get_32();
- if (k_cyc_to_us_floor32(curr_time - start_time) >= timeout) {
- LOG_ERR("wait dma(%d) finish timeout", dma_chan);
- return -ETIMEDOUT;
- }
- if ((!k_is_in_isr()) && (CONFIG_AUDIO_DAC_WAIT_WRITE_PCMBUF_SLEEP_MS)
- && (!z_is_idle_thread_object(_current)))
- k_sleep(K_MSEC(CONFIG_AUDIO_DAC_WAIT_WRITE_PCMBUF_SLEEP_MS));
- }
- return 0;
- }
- /* @brief wait dac FIFO empty */
- static int wait_dacfifo_empty(struct device *dev, aout_session_t *session)
- {
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- struct aout_drv_data *data = dev->data;
- int ret;
- uint8_t fifo_idx = session->outfifo_type;
- ret = phy_audio_control(data->dac0_dev, PHY_CMD_DAC_WAIT_EMPTY, &fifo_idx);
- if (ret) {
- LOG_ERR("wait empty error");
- }
- return ret;
- #else
- return -ENXIO;
- #endif
- }
- /* @brief Wait writing data to audio DAC channel finishly */
- static int acts_audio_out_wait_write_finish(struct device *dev, aout_session_t *session)
- {
- struct aout_drv_data *data = dev->data;
- int ret;
- if ((AOUT_FIFO_DAC0 == session->outfifo_type)
- || (AOUT_FIFO_DAC1 == session->outfifo_type)) {
- #if (CONFIG_AUDIO_DAC_WAIT_WRITE_PCMBUF_NEXT_TIME == 1)
- if (session->flags & AOUT_SESSION_START)
- #endif
- {
- ret = acts_audio_dma_wait_timeout(data->dma_dev,
- session->dma_chan, CONFIG_AUDIO_DAC_WAIT_WRITE_PCMBUF_TIMEOUT_US);
- if (ret) {
- LOG_ERR("wait dma error %d", ret);
- return -EIO;
- }
- wait_dacfifo_empty(dev, session);
- }
- }
- return 0;
- }
- #endif
- /* @brief Write the data to the audio output channel */
- static int acts_audio_out_write(struct device *dev, void *handle, uint8_t *buffer, uint32_t length)
- {
- struct aout_drv_data *data = dev->data;
- aout_session_t *session = (aout_session_t *)handle;
- int ret;
- if (!handle || !buffer || !length) {
- LOG_ERR("Invalid parameters (%p, %p, %d)", handle, buffer, length);
- return -EINVAL;
- }
- if (!AOUT_SESSION_CHECK_MAGIC(session->magic)) {
- LOG_ERR("Session magic error:0x%x", session->magic);
- return -EFAULT;
- }
- if (session->reload_en) {
- LOG_INF("Reload mode can start directly");
- return 0;
- }
- if (!(session->flags & AOUT_SESSION_OPEN)) {
- LOG_ERR("session not opened");
- return -EPERM;
- }
- if(!session->dsp_fifo_src){
- #if defined(CONFIG_AUDIO_OUT_DAC_PCMBUF_SUPPORT) \
- && (CONFIG_AUDIO_DAC_WAIT_WRITE_PCMBUF_FINISH == 1) \
- && (CONFIG_AUDIO_DAC_WAIT_WRITE_PCMBUF_NEXT_TIME == 1)
- ret = acts_audio_out_wait_write_finish(dev, session);
- if (ret)
- return ret;
- #endif
- if (audio_out_dma_prepare(dev, session)) {
- LOG_ERR("prepare session dma error");
- return -ENXIO;
- }
- LOG_DBG("DMA channel:0x%x, buffer:%p len:%d", session->dma_chan, buffer, length);
- if (session->dma_separated_en) {
- ret = dma_reload(data->dma_dev, session->dma_chan, (uint32_t)buffer,
- (uint32_t)buffer, length * 2);
- } else {
- ret = dma_reload(data->dma_dev, session->dma_chan, (uint32_t)buffer, 0, length);
- }
- if (ret) {
- LOG_ERR("dma reload error %d", ret);
- return ret;
- }
- }
- ret = audio_out_start(dev, handle);
- if (ret)
- LOG_ERR("dma start error %d", ret);
- if(!session->dsp_fifo_src){
- #if defined(CONFIG_AUDIO_OUT_DAC_PCMBUF_SUPPORT) \
- && (CONFIG_AUDIO_DAC_WAIT_WRITE_PCMBUF_FINISH == 1) \
- && (CONFIG_AUDIO_DAC_WAIT_WRITE_PCMBUF_NEXT_TIME == 0)
- ret = acts_audio_out_wait_write_finish(dev, session);
- if (ret)
- return ret;
- #endif
- }
- #ifdef CONFIG_AUDIO_DYNAMIC_DEBUG
- if (!session->reload_en)
- audio_out_debug_perf(session, buffer, length);
- #endif
- return ret;
- }
- static int acts_audio_out_stop(struct device *dev, void *handle)
- {
- struct aout_drv_data *data = dev->data;
- aout_session_t *session = (aout_session_t *)handle;
- int ret = 0;
- if (session && AOUT_SESSION_CHECK_MAGIC(session->magic)) {
- LOG_INF("session#%p, audio out stop", session);
- if (data->dma_dev && session->dma_chan) {
- ret = dma_stop(data->dma_dev, session->dma_chan);
- if (!ret)
- session->flags &= ~AOUT_SESSION_START;
- }
- }
- return ret;
- }
- const struct aout_driver_api aout_drv_api = {
- .aout_open = acts_audio_out_open,
- .aout_close = acts_audio_out_close,
- .aout_control = acts_audio_out_control,
- .aout_start = acts_audio_out_start,
- .aout_write = acts_audio_out_write,
- .aout_stop = acts_audio_out_stop
- };
- /* @brief audio-out channles initialization */
- static int audio_out_init(const struct device *dev)
- {
- const struct aout_config_data *cfg = dev->config;
- struct aout_drv_data *data = dev->data;
- memset(data, 0, sizeof(struct aout_drv_data));
- k_sem_init(&data->lock, 1, 1);
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- data->dac0_dev = (struct device *)device_get_binding(cfg->dac0_name);
- if (!data->dac0_dev) {
- LOG_ERR("no DAC device(%s)", cfg->dac0_name);
- }
- #endif
- #ifdef CONFIG_AUDIO_OUT_I2STX_SUPPORT
- data->i2stx0_dev = (struct device *)device_get_binding(cfg->i2stx0_name);
- if (!data->i2stx0_dev) {
- LOG_ERR("no I2STX device(%s)", cfg->i2stx0_name);
- }
- #endif
- #ifdef CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT
- data->spdiftx0_dev = (struct device *)device_get_binding(cfg->spdiftx0_name);
- if (!data->spdiftx0_dev) {
- LOG_ERR("no SPDIFTX device(%s)", cfg->spdiftx0_name);
- }
- #endif
- #ifdef CONFIG_AUDIO_OUT_PDMTX_SUPPORT
- data->pdmtx0_dev = (struct device *)device_get_binding(cfg->pdmtx0_name);
- if (!data->pdmtx0_dev) {
- LOG_ERR("no PDMTX device(%s)", cfg->pdmtx0_name);
- }
- #endif
- #ifdef CONFIG_AUDIO_PA_AW87390_SUPPORT
- data->pa_aw87390_dev = (struct device *)device_get_binding(cfg->pa_aw87390_name);
- if (!data->pa_aw87390_dev) {
- LOG_ERR("no pa_aw87390_dev device(%s)", cfg->pa_aw87390_name);
- }
- #endif
- /* clear pa session indicator */
- data->pa_session_indicator = 0;
- /* clear DAC fifo reference counter */
- data->dac_fifo_ref_counter = 0;
- /* disable AVDD pulldown */
- sys_write32(sys_read32(AUDIOLDO_CTL) & ~(1 << 4), AUDIOLDO_CTL);
- /* open internal and external pa */
- #ifdef CONFIG_AUDIO_POWERON_OPEN_PA
- #ifdef CONFIG_AUDIO_PA_AW87390_SUPPORT
- phy_audio_disable(data->pa_aw87390_dev,NULL);
- #endif
- acts_audio_open_pa((struct device *)dev, CONFIG_POWERON_OPEN_EXTERNAL_PA);
- #ifdef CONFIG_AUDIO_PA_AW87390_SUPPORT
- phy_audio_enable(data->pa_aw87390_dev,NULL);
- #endif
- #endif
- #ifdef CONFIG_AUDIO_POWERON_OPEN_I2STX
- acts_audio_open_i2stx_dev(dev);
- #endif
- printk("ACTS-AUDIO OUT initialized\n");
- return 0;
- }
- static struct aout_drv_data audio_out_drv_data;
- static const struct aout_config_data audio_out_config_data = {
- #ifdef CONFIG_AUDIO_OUT_DAC_SUPPORT
- .dac0_name = CONFIG_AUDIO_DAC_0_NAME,
- #endif
- #ifdef CONFIG_AUDIO_OUT_I2STX_SUPPORT
- .i2stx0_name = CONFIG_AUDIO_I2STX_0_NAME,
- #endif
- #ifdef CONFIG_AUDIO_OUT_SPDIFTX_SUPPORT
- .spdiftx0_name = CONFIG_AUDIO_SPDIFTX_0_NAME,
- #endif
- #ifdef CONFIG_AUDIO_OUT_PDMTX_SUPPORT
- .pdmtx0_name = CONFIG_AUDIO_PDMTX_0_NAME,
- #endif
- #ifdef CONFIG_AUDIO_PA_AW87390_SUPPORT
- .pa_aw87390_name = "aw87390_out",
- #endif
- };
- DEVICE_DEFINE(audio_out, CONFIG_AUDIO_OUT_ACTS_DEV_NAME, audio_out_init,
- NULL,
- &audio_out_drv_data, &audio_out_config_data,
- POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &aout_drv_api);
|