// SPDX-License-Identifier: GPL-2.0 // // Copyright (C) 2018 MediaTek Inc. // Author: Chipeng Chang #include #include #include /* ipi message related*/ #include #include #include #include #include #include #include #include #include #ifdef CONFIG_MTK_AUDIODSP_SUPPORT #include #else #include #endif //#define DEBUG_VERBOSE static int adsp_standby_flag; static struct wait_queue_head waitq; /* don't use this directly if not necessary */ static struct mtk_base_dsp *local_base_dsp; static struct mtk_base_afe *local_dsp_afe; int audio_set_dsp_afe(struct mtk_base_afe *afe) { local_dsp_afe = afe; return 0; } struct mtk_base_afe *get_afe_base(void) { if (local_dsp_afe == NULL) pr_info("%s local_base_dsp == NULL", __func__); return local_dsp_afe; } int set_dsp_base(struct mtk_base_dsp *pdsp) { if (pdsp == NULL) pr_info("%s pdsp == NULL", __func__); local_base_dsp = pdsp; return 0; } void *get_dsp_base(void) { if (local_base_dsp == NULL) pr_warn("%s local_base_dsp == NULL", __func__); return local_base_dsp; } static void *ipi_recv_private; void *get_ipi_recv_private(void) { return ipi_recv_private; } void set_ipi_recv_private(void *priv) { pr_debug("%s\n", __func__); if (priv != NULL) ipi_recv_private = priv; else pr_debug("%s ipi_recv_private has been set\n", __func__); } int copy_ipi_payload(void *dst, void *src, unsigned int size) { if (size > MAX_PAYLOAD_SIZE) return -1; memcpy(dst, src, size); return 0; } /* * common function for IPI message */ int mtk_scp_ipi_send(int task_scene, int data_type, int ack_type, uint16_t msg_id, uint32_t param1, uint32_t param2, char *payload) { struct ipi_msg_t ipi_msg; int send_result = 0; memset((void *)&ipi_msg, 0, sizeof(struct ipi_msg_t)); if (!is_audio_task_dsp_ready(task_scene)) { pr_info("%s(), is_adsp_ready send false\n", __func__); send_result = -1; return send_result; } if (get_task_attr(get_dspdaiid_by_dspscene(task_scene), ADSP_TASK_ATTR_DEFAULT) == 0) { pr_info("%s() task_scene[%d] not enable\n", __func__, task_scene); send_result = -1; return send_result; } send_result = audio_send_ipi_msg( &ipi_msg, task_scene, AUDIO_IPI_LAYER_TO_DSP, data_type, ack_type, msg_id, param1, param2, (char *)payload); if (send_result) pr_info("%s(), scp_ipi send fail\n", __func__); return send_result; } /* dsp scene ==> od mapping */ int get_dspscene_by_dspdaiid(int id) { switch (id) { case AUDIO_TASK_VOIP_ID: return TASK_SCENE_VOIP; case AUDIO_TASK_PRIMARY_ID: return TASK_SCENE_PRIMARY; case AUDIO_TASK_OFFLOAD_ID: return TASK_SCENE_PLAYBACK_MP3; case AUDIO_TASK_DEEPBUFFER_ID: return TASK_SCENE_DEEPBUFFER; case AUDIO_TASK_PLAYBACK_ID: return TASK_SCENE_AUDPLAYBACK; case AUDIO_TASK_CAPTURE_UL1_ID: return TASK_SCENE_CAPTURE_UL1; case AUDIO_TASK_A2DP_ID: return TASK_SCENE_A2DP; case AUDIO_TASK_BLEDL_ID: return TASK_SCENE_BLEDL; case AUDIO_TASK_BLEUL_ID: return TASK_SCENE_BLEUL; case AUDIO_TASK_DATAPROVIDER_ID: return TASK_SCENE_DATAPROVIDER; case AUDIO_TASK_CALL_FINAL_ID: return TASK_SCENE_CALL_FINAL; case AUDIO_TASK_FAST_ID: return TASK_SCENE_FAST; case AUDIO_TASK_MUSIC_ID: return TASK_SCENE_MUSIC; case AUDIO_TASK_KTV_ID: return TASK_SCENE_KTV; case AUDIO_TASK_CAPTURE_RAW_ID: return TASK_SCENE_CAPTURE_RAW; case AUDIO_TASK_FM_ADSP_ID: return TASK_SCENE_FM_ADSP; case AUDIO_TASK_UL_PROCESS_ID: return TASK_SCENE_UL_PROCESS; case AUDIO_TASK_ECHO_REF_ID: return TASK_SCENE_ECHO_REF_UL; case AUDIO_TASK_ECHO_REF_DL_ID: return TASK_SCENE_ECHO_REF_DL; case AUDIO_TASK_USBDL_ID: return TASK_SCENE_USB_DL; case AUDIO_TASK_USBUL_ID: return TASK_SCENE_USB_UL; case AUDIO_TASK_MDDL_ID: return TASK_SCENE_MD_DL; case AUDIO_TASK_MDUL_ID: return TASK_SCENE_MD_UL; default: pr_warn("%s() err\n", __func__); return -1; } return 0; } int get_dspdaiid_by_dspscene(int dspscene) { switch (dspscene) { case TASK_SCENE_VOIP: return AUDIO_TASK_VOIP_ID; case TASK_SCENE_PRIMARY: return AUDIO_TASK_PRIMARY_ID; case TASK_SCENE_PLAYBACK_MP3: return AUDIO_TASK_OFFLOAD_ID; case TASK_SCENE_DEEPBUFFER: return AUDIO_TASK_DEEPBUFFER_ID; case TASK_SCENE_AUDPLAYBACK: return AUDIO_TASK_PLAYBACK_ID; case TASK_SCENE_CAPTURE_UL1: return AUDIO_TASK_CAPTURE_UL1_ID; case TASK_SCENE_A2DP: return AUDIO_TASK_A2DP_ID; case TASK_SCENE_BLEDL: return AUDIO_TASK_BLEDL_ID; case TASK_SCENE_BLEUL: return AUDIO_TASK_BLEUL_ID; case TASK_SCENE_DATAPROVIDER: return AUDIO_TASK_DATAPROVIDER_ID; case TASK_SCENE_FAST: return AUDIO_TASK_FAST_ID; case TASK_SCENE_MUSIC: return AUDIO_TASK_MUSIC_ID; case TASK_SCENE_CALL_FINAL: return AUDIO_TASK_CALL_FINAL_ID; case TASK_SCENE_KTV: return AUDIO_TASK_KTV_ID; case TASK_SCENE_CAPTURE_RAW: return AUDIO_TASK_CAPTURE_RAW_ID; case TASK_SCENE_FM_ADSP: return AUDIO_TASK_FM_ADSP_ID; case TASK_SCENE_UL_PROCESS: return AUDIO_TASK_UL_PROCESS_ID; case TASK_SCENE_ECHO_REF_UL: return AUDIO_TASK_ECHO_REF_ID; case TASK_SCENE_ECHO_REF_DL: return AUDIO_TASK_ECHO_REF_DL_ID; case TASK_SCENE_USB_DL: return AUDIO_TASK_USBDL_ID; case TASK_SCENE_USB_UL: return AUDIO_TASK_USBUL_ID; case TASK_SCENE_MD_DL: return AUDIO_TASK_MDDL_ID; case TASK_SCENE_MD_UL: return AUDIO_TASK_MDUL_ID; default: pr_info("%s() err dspscene=%d\n", __func__, dspscene); return -1; } return 0; } /* todo:: refine for check mechanism.*/ int get_audio_memery_type(struct snd_pcm_substream *substream) { if (substream->runtime->dma_addr < 0x20000000) return MEMORY_AUDIO_SRAM; else return MEMORY_AUDIO_DRAM; } int afe_get_pcmdir(int dir, struct audio_hw_buffer buf) { int ret = -1, i = 0, memif = 0; if (dir == SNDRV_PCM_STREAM_CAPTURE) ret = AUDIO_DSP_TASK_PCM_HWPARAM_UL; else if (dir == SNDRV_PCM_STREAM_PLAYBACK) ret = AUDIO_DSP_TASK_PCM_HWPARAM_DL; if (buf.hw_buffer == BUFFER_TYPE_SHARE_MEM) return ret; /* check if it is hardware buffer * if yes ==> check if it is ref buffer. */ memif = buf.audio_memiftype; for (i = 0; i < AUDIO_TASK_DAI_NUM; i++) { if (get_afememref_by_afe_taskid(i) == memif && buf.hw_buffer == BUFFER_TYPE_HW_MEM && get_task_attr(i, ADSP_TASK_ATTR_REF_RUNTIME) == 1) { ret = AUDIO_DSP_TASK_PCM_HWPARAM_REF; break; } } return ret; } int get_dsp_task_attr(int dsp_id, int task_attr) { return get_task_attr(dsp_id, task_attr); } int get_dsp_task_id_from_str(const char *task_name) { int ret = -1; if (strstr(task_name, "primary")) ret = AUDIO_TASK_PRIMARY_ID; else if (strstr(task_name, "deepbuffer")) ret = AUDIO_TASK_DEEPBUFFER_ID; else if (strstr(task_name, "voip")) ret = AUDIO_TASK_VOIP_ID; else if (strstr(task_name, "playback")) ret = AUDIO_TASK_PLAYBACK_ID; else if (strstr(task_name, "call_final")) ret = AUDIO_TASK_CALL_FINAL_ID; else if (strstr(task_name, "ktv")) ret = AUDIO_TASK_KTV_ID; else if (strstr(task_name, "fm")) ret = AUDIO_TASK_FM_ADSP_ID; else if (strstr(task_name, "offload")) ret = AUDIO_TASK_OFFLOAD_ID; else if (strstr(task_name, "capture")) ret = AUDIO_TASK_CAPTURE_UL1_ID; else if (strstr(task_name, "fast")) ret = AUDIO_TASK_FAST_ID; else if (strstr(task_name, "bledl")) ret = AUDIO_TASK_BLEDL_ID; else if (strstr(task_name, "bleul")) ret = AUDIO_TASK_BLEUL_ID; else pr_info("%s(), %s has no task id, ret %d", __func__, task_name, ret); return ret; } static int set_aud_buf_attr(struct audio_hw_buffer *audio_hwbuf, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, int irq_usage, struct snd_soc_dai *dai) { int ret = 0; ret = set_afe_audio_pcmbuf(audio_hwbuf, substream); if (ret < 0) { pr_info("set_afe_audio_pcmbuf fail\n"); return -1; } ret = set_audiobuffer_hw(audio_hwbuf, BUFFER_TYPE_HW_MEM); if (ret < 0) { pr_info("set_audiobuffer_hw fail\n"); return -1; } ret = set_audiobuffer_audio_irq_num(audio_hwbuf, irq_usage); if (ret < 0) { pr_info("set_audiobuffer_audio_irq_num fail\n"); return -1; } ret = set_audiobuffer_audio_memiftype(audio_hwbuf, dai->id); if (ret < 0) { pr_info("set_audiobuffer_audio_memiftype fail\n"); return -1; } ret = set_audiobuffer_memorytype( audio_hwbuf, get_audio_memery_type(substream)); if (ret < 0) { pr_info("set_audiobuffer_memorytype fail\n"); return -1; } ret = set_audiobuffer_attribute( audio_hwbuf, substream, params, afe_get_pcmdir(substream->stream, *audio_hwbuf)); if (ret < 0) { pr_info("set_audiobuffer_attribute fail\n"); return -1; } ret = set_audiobuffer_threshold(audio_hwbuf, substream); if (ret < 0) { pr_info("set_audiobuffer_threshold fail\n"); return -1; } pr_info("%s() memiftype: %d ch: %u fmt: %u rate: %u dir: %d, start_thres: %u stop_thres: %u period_size: %d period_cnt: %d\n", __func__, audio_hwbuf->audio_memiftype, audio_hwbuf->aud_buffer.buffer_attr.channel, audio_hwbuf->aud_buffer.buffer_attr.format, audio_hwbuf->aud_buffer.buffer_attr.rate, audio_hwbuf->aud_buffer.buffer_attr.direction, audio_hwbuf->aud_buffer.start_threshold, audio_hwbuf->aud_buffer.stop_threshold, audio_hwbuf->aud_buffer.period_size, audio_hwbuf->aud_buffer.period_count); return 0; } /* function warp playback buffer information send to dsp */ int afe_pcm_ipi_to_dsp(int command, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai, struct mtk_base_afe *afe) { int task_id = 0, ret = 0; struct mtk_base_dsp *dsp = (struct mtk_base_dsp *)local_base_dsp; void *ipi_audio_buf; /* dsp <-> audio data struct*/ struct mtk_base_dsp_mem *dsp_memif; struct mtk_base_afe_memif *memif = &afe->memif[dai->id]; task_id = get_taskid_by_afe_daiid(dai->id); if (task_id < 0 || task_id >= AUDIO_TASK_DAI_NUM) return -1; if (get_task_attr(task_id, ADSP_TASK_ATTR_RUNTIME) <= 0 || get_task_attr(task_id, ADSP_TASK_ATTR_DEFAULT) <= 0) return -1; pr_info("%s(), command 0x%x\n", __func__, command); dsp_memif = (struct mtk_base_dsp_mem *)&dsp->dsp_mem[task_id]; /* send msg by task , unsing common function*/ switch (command) { case AUDIO_DSP_TASK_PCM_HWPARAM: set_aud_buf_attr(&dsp_memif->audio_afepcm_buf, substream, params, memif->irq_usage, dai); /* send audio_afepcm_buf to SCP side*/ ipi_audio_buf = (void *) dsp_memif->msg_atod_share_buf.va_addr; memcpy((void *)ipi_audio_buf, (void *)&dsp_memif->audio_afepcm_buf, sizeof(struct audio_hw_buffer)); #ifdef DEBUG_VERBOSE dump_audio_hwbuffer(ipi_audio_buf); #endif /* send to task with hw_param information , * buffer and pcm attribute */ ret = mtk_scp_ipi_send(get_dspscene_by_dspdaiid(task_id), AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_PCM_HWPARAM, sizeof(dsp_memif->msg_atod_share_buf.phy_addr), 0, (char *) &dsp_memif->msg_atod_share_buf.phy_addr); break; case AUDIO_DSP_TASK_PCM_PREPARE: set_aud_buf_attr(&dsp_memif->audio_afepcm_buf, substream, params, memif->irq_usage, dai); /* send audio_afepcm_buf to SCP side*/ ipi_audio_buf = (void *)dsp_memif->msg_atod_share_buf.va_addr; memcpy((void *)ipi_audio_buf, (void *)&dsp_memif->audio_afepcm_buf, sizeof(struct audio_hw_buffer)); #ifdef DEBUG_VERBOSE dump_audio_hwbuffer(ipi_audio_buf); #endif /* send to task with prepare status*/ ret = mtk_scp_ipi_send(get_dspscene_by_dspdaiid(task_id), AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_PCM_PREPARE, sizeof(dsp_memif->msg_atod_share_buf.phy_addr), 0, (char *) &dsp_memif->msg_atod_share_buf.phy_addr); break; case AUDIO_DSP_TASK_PCM_HWFREE: set_aud_buf_attr(&dsp_memif->audio_afepcm_buf, substream, params, memif->irq_usage, dai); /* send to task with prepare status*/ ret = mtk_scp_ipi_send(get_dspscene_by_dspdaiid(task_id), AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_PCM_HWFREE, afe_get_pcmdir(substream->stream, dsp_memif->audio_afepcm_buf), 0, NULL); break; default: pr_warn("%s command = %d\n", __func__, command); return -1; } return ret; } void mtk_dsp_pcm_ipi_recv(struct ipi_msg_t *ipi_msg) { struct mtk_base_dsp *dsp = get_ipi_recv_private(); if (ipi_msg == NULL) { pr_info("%s ipi_msg == NULL\n", __func__); return; } if (!is_audio_task_dsp_ready(ipi_msg->task_scene)) { pr_info("%s(), is_adsp_ready send false\n", __func__); return; } if (dsp->dsp_ipi_ops.ipi_handler) dsp->dsp_ipi_ops.ipi_handler(dsp, ipi_msg); } #ifdef CONFIG_MTK_AUDIODSP_SUPPORT int mtk_dsp_register_feature(int id) { return adsp_register_feature(id); } #else int mtk_dsp_register_feature(int id) { int ret = 0; scp_register_feature(id); return ret; } #endif #ifdef CONFIG_MTK_AUDIODSP_SUPPORT int mtk_dsp_deregister_feature(int id) { return adsp_deregister_feature(id); } #else int mtk_dsp_deregister_feature(int id) { int ret = 0; scp_deregister_feature(id); return ret; } #endif #ifdef CONFIG_MTK_AUDIODSP_SUPPORT static int mtk_audio_dsp_event_receive( struct notifier_block *this, unsigned long event, void *ptr) { switch (event) { case ADSP_EVENT_STOP: adsp_standby_flag = 1; break; case ADSP_EVENT_READY: mtk_reinit_adsp(); adsp_standby_flag = 0; wake_up(&waitq); break; default: pr_info("event %lu err", event); } return 0; } static struct notifier_block mtk_audio_dsp_notifier = { .notifier_call = mtk_audio_dsp_event_receive, .priority = AUDIO_PLAYBACK_FEATURE_PRI, }; #endif int mtk_audio_register_notify(void) { #ifdef CONFIG_MTK_AUDIODSP_SUPPORT adsp_register_notify(&mtk_audio_dsp_notifier); #endif init_waitqueue_head(&waitq); return 0; } int wait_dsp_ready(void) { /* should not call this in atomic or interrupt level */ if (!wait_event_timeout(waitq, adsp_standby_flag == 0 && is_adsp_ready(ADSP_A_ID), msecs_to_jiffies(200))) return -EBUSY; return 0; }