// SPDX-License-Identifier: GPL-2.0 // // Copyright (c) 2018 MediaTek Inc. #include "mtk-scp-audio-pcm.h" #include "scp_audio_ipi.h" #include "scp.h" #include "mtk-scp-audio-mem-control.h" #include static int rv_standby_flag;//TODO static struct mtk_base_afe *audio_afe; static struct mbox_msg *mbox_msg_temp; static int mscpSpkProcessEnable; //#define DEBUG_VERBOSE_IRQ //#define DEBUG_VERBOSE static struct snd_soc_dai_driver mtk_scp_audio_dai_driver[] = { { .name = "audio_task_spk_process", .id = SCP_AUD_TASK_SPK_PROCESS_ID, .playback = { .stream_name = "SCP_SPK_Process", .channels_min = 1, .channels_max = 2, .rates = MTK_PCM_RATES, .formats = MTK_PCM_FORMATS, }, }, }; static int scp_audio_task_scence[SCP_AUD_TASK_DAI_NUM] = { [SCP_AUD_TASK_SPK_PROCESS_ID] = TASK_SCENE_RV_SPK_PROCESS, }; static int scp_audio_feature[SCP_AUD_TASK_DAI_NUM] = { [SCP_AUD_TASK_SPK_PROCESS_ID] = RVSPKPROCESS_FEATURE_ID, }; static char *scp_task_name[SCP_AUD_TASK_DAI_NUM] = { [SCP_AUD_TASK_SPK_PROCESS_ID] = "spkProcess", }; int scp_audio_dai_register(struct platform_device *pdev, struct mtk_scp_audio_base *scp_audio) { scp_audio->dai_drivers = mtk_scp_audio_dai_driver; scp_audio->num_dai_drivers = ARRAY_SIZE(mtk_scp_audio_dai_driver); return 0; }; int scp_set_audio_afe(struct mtk_base_afe *afe) { audio_afe = afe; return 0; } EXPORT_SYMBOL_GPL(scp_set_audio_afe); struct mtk_base_afe *scp_get_audio_afe(void) { if (audio_afe == NULL) pr_info("%s audio_afe == NULL", __func__); return audio_afe; } /* scene <--> dai mapping */ int get_scene_by_daiid(int id) { if (id < 0 || id >= SCP_AUD_TASK_DAI_NUM) { pr_info("%s(), id err: %d\n", __func__, id); return -1; } return scp_audio_task_scence[id]; } EXPORT_SYMBOL(get_scene_by_daiid); int get_daiid_by_scene(int scene) { int id; int ret = -1; if (scene < 0) { pr_info("%s() scene err: %d\n", __func__, scene); return -1; } for (id = 0; id < SCP_AUD_TASK_DAI_NUM; id++) { if (scp_audio_task_scence[id] == scene) { ret = id; break; } } if (ret < 0) pr_info("%s() scene is not in scp_audio_task_scence\n", __func__); return ret; } EXPORT_SYMBOL(get_daiid_by_scene); /* feature <--> dai mapping */ int get_feature_by_daiid(int id) { if (id < 0 || id >= SCP_AUD_TASK_DAI_NUM) { pr_info("%s(), id err: %d\n", __func__, id); return -1; } return scp_audio_feature[id]; } int get_daiid_by_afeid(int id) { struct mtk_scp_audio_base *scp_audio = get_scp_audio_base(); if (scp_audio == NULL) return -1; //TODO if (id < 0 || id >= SCP_AUD_TASK_DAI_NUM) { // pr_info("%s(), id err: %d\n", __func__, id); // return -1; //} if (id == scp_audio->ul_memif || id == scp_audio->dl_memif || id == scp_audio->ref_memif) return SCP_AUD_TASK_SPK_PROCESS_ID; else return -1; } EXPORT_SYMBOL(get_daiid_by_afeid); const char *get_taskname_by_daiid(const int daiid) { if (daiid < 0 || daiid >= SCP_AUD_TASK_DAI_NUM) { pr_info("%s(), daiid err: %d\n", __func__, daiid); return NULL; } return scp_task_name[daiid]; } EXPORT_SYMBOL_GPL(get_taskname_by_daiid); int get_daiid_by_taskname(const char *task_name) { int ret = -1; int id; for (id = 0; id < SCP_AUD_TASK_DAI_NUM; id++) { if (strstr(task_name, scp_task_name[id])) ret = id; } if (ret < 0) pr_info("%s(), %s has no task id, ret %d", __func__, task_name, ret); return ret; } EXPORT_SYMBOL_GPL(get_daiid_by_taskname); struct scp_aud_task_base *get_taskbase_by_daiid(const int daiid) { struct mtk_scp_audio_base *scp_audio = get_scp_audio_base(); if (daiid < 0 || daiid >= SCP_AUD_TASK_DAI_NUM) { pr_info("%s(), daiid err: %d\n", __func__, daiid); return NULL; } return &scp_audio->task_base[daiid]; } /* * common function for IPI message */ int mtk_scp_audio_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_scp_ready send false\n", __func__); return -1; } 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; } EXPORT_SYMBOL(mtk_scp_audio_ipi_send); /* get scene for ipi set/get buf API */ int mtk_get_ipi_buf_scene_rv(void) { int task_scene = -1; if (rv_standby_flag) return task_scene; //TODO if (get_task_attr(AUDIO_TASK_CALL_FINAL_ID, // ADSP_TASK_ATTR_RUNTIME) > 0) task_scene = TASK_SCENE_RV_SPK_PROCESS; //else { // pr_info("%s(), spk process not enabled\n", __func__); // return result; //} return task_scene; } EXPORT_SYMBOL(mtk_get_ipi_buf_scene_rv); int send_task_sharemem_to_scp(struct mtk_scp_audio_base *scp_audio, int daiid) { int ret = 0; struct scp_aud_task_base *task_base; struct ipi_msg_t ipi_msg; pr_info("%s(+)\n", __func__); if (scp_audio == NULL) { pr_info("%s scp_audio == NULL\n", __func__); return -1; } if (daiid < 0 || daiid >= SCP_AUD_TASK_DAI_NUM) { pr_info("%s daiid = %d\n", __func__, daiid); return -1; } task_base = get_taskbase_by_daiid(daiid); /* send share message to scp side */ memcpy((void *)task_base->ipi_payload_buf, (void *)&task_base->msg_atod_share_buf, sizeof(struct audio_dsp_dram)); ret = audio_send_ipi_msg( &ipi_msg, get_scene_by_daiid(daiid), AUDIO_IPI_LAYER_TO_DSP, AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_BYPASS_ACK, AUDIO_DSP_TASK_MSGA2DSHAREMEM, sizeof(struct audio_dsp_dram), 0, (char *)task_base->ipi_payload_buf); if (ret) pr_info("%s(), dai [%d]send a2d ipi fail\n", __func__, daiid); /* send share message to SCP side */ memcpy((void *)task_base->ipi_payload_buf, (void *)&task_base->msg_dtoa_share_buf, sizeof(struct audio_dsp_dram)); ret = audio_send_ipi_msg( &ipi_msg, get_scene_by_daiid(daiid), AUDIO_IPI_LAYER_TO_DSP, AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_BYPASS_ACK, AUDIO_DSP_TASK_MSGD2ASHAREMEM, sizeof(struct audio_dsp_dram), 0, (char *)task_base->ipi_payload_buf); if (ret) pr_info("%s(), dai [%d]send d2a ipi fail\n", __func__, daiid); pr_info("%s(-)\n", __func__); return 0; } int get_audio_mem_type(struct snd_pcm_substream *substream) { if (substream->runtime->dma_addr < 0x20000000) return MEMORY_SRAM; else return MEMORY_DRAM; } int scp_audio_get_pcmdir(int dir, struct audio_hw_buffer buf) { int ret = -1; struct mtk_scp_audio_base *scp_audio = get_scp_audio_base(); if (dir == SNDRV_PCM_STREAM_CAPTURE) ret = SCP_AUDIO_TASK_PCM_HWPARAM_UL; else if (dir == SNDRV_PCM_STREAM_PLAYBACK) ret = SCP_AUDIO_TASK_PCM_HWPARAM_DL; if (scp_audio->ref_memif == buf.audio_memiftype) ret = SCP_AUDIO_TASK_PCM_HWPARAM_REF; 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; if (audio_hwbuf == NULL) { pr_info("%s audio_hwbuf == NULL", __func__); return -1; } ret = set_afe_audio_pcmbuf(audio_hwbuf, substream); if (ret < 0) { pr_info("set_afe_audio_pcmbuf fail\n"); return -1; } audio_hwbuf->hw_buffer = SCP_BUFFER_HW_MEM; //TODO BUFFER_TYPE_HW_MEM; audio_hwbuf->irq_num = irq_usage; audio_hwbuf->audio_memiftype = dai->id; audio_hwbuf->memory_type = get_audio_mem_type(substream); ret = set_audiobuffer_threshold(audio_hwbuf, substream); if (ret < 0) { pr_info("set_audiobuffer_threshold fail\n"); return -1; } ret = set_audiobuffer_attribute( audio_hwbuf, substream, params, scp_audio_get_pcmdir(substream->stream, *audio_hwbuf)); if (ret < 0) { pr_info("set_audiobuffer_attribute 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 scp */ int afe_pcm_ipi_to_scp(int command, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai, struct mtk_base_afe *afe) { int ret = 0, daiid; struct audio_hw_buffer *audio_hwbuf; struct mtk_base_afe_memif *memif = &afe->memif[dai->id]; struct scp_aud_task_base *task_base; const char *task_name; unsigned long vaddr; dma_addr_t paddr; daiid = get_daiid_by_afeid(dai->id); if (daiid < 0 || daiid >= SCP_AUD_TASK_DAI_NUM) return -1; task_name = get_taskname_by_daiid(daiid); task_base = get_taskbase_by_daiid(daiid); pr_info("%s(), %s send cmd 0x%x\n", __func__, task_name, command); if (task_base->msg_atod_share_buf.phy_addr <= 0 || !task_base->msg_atod_share_buf.va_addr) { pr_warn("%s msg_atod_share_buf error!\n", __func__); return -1; } vaddr = task_base->msg_atod_share_buf.va_addr; paddr = task_base->msg_atod_share_buf.phy_addr; audio_hwbuf = (struct audio_hw_buffer *)vaddr; /* send msg by task by unsing common function */ switch (command) { case AUDIO_DSP_TASK_PCM_HWPARAM: case AUDIO_DSP_TASK_PCM_PREPARE: /* send to task with hw_param information, buffer and pcm attribute * or send to task with prepare status */ set_aud_buf_attr(audio_hwbuf, substream, params, memif->irq_usage, dai); ret = mtk_scp_audio_ipi_send(get_scene_by_daiid(daiid), AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, command, sizeof(task_base->msg_atod_share_buf.phy_addr), 0, (char *) &paddr); break; case AUDIO_DSP_TASK_PCM_HWFREE: set_aud_buf_attr(audio_hwbuf, substream, params, memif->irq_usage, dai); /* send to task with prepare status */ ret = mtk_scp_audio_ipi_send(get_scene_by_daiid(daiid), AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_PCM_HWFREE, scp_audio_get_pcmdir(substream->stream, *audio_hwbuf), 0, NULL); break; default: pr_warn("%s error command: %d\n", __func__, command); return -1; } return ret; } EXPORT_SYMBOL_GPL(afe_pcm_ipi_to_scp); void mtk_reset_memif_sharemem(int memif_num) { struct mtk_base_afe_memif *memif; if (audio_afe == NULL) return; if (memif_num >= 0) { memif = &audio_afe->memif[memif_num]; if (memif && memif->use_scp_share_mem) memif->use_scp_share_mem = 0; } } void mtk_reset_scp_audio_sharemem(void) { struct mtk_scp_audio_base *scp_audio = get_scp_audio_base(); if (scp_audio == NULL) return; mtk_reset_memif_sharemem(scp_audio->dl_memif); mtk_reset_memif_sharemem(scp_audio->ul_memif); mtk_reset_memif_sharemem(scp_audio->ref_memif); } int mtk_reinit_scp_audio(void) { int ret = 0, scene = 0, daiid = 0; for (daiid = 0; daiid < SCP_AUD_TASK_DAI_NUM; daiid++) { send_task_sharemem_to_scp(get_scp_audio_base(), daiid); scene = get_scene_by_daiid(daiid); ret = mtk_scp_audio_ipi_send(scene, AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_BYPASS_ACK, AUDIO_DSP_TASK_RESET, 0, 0, NULL); pr_info("%s scene = %d\n", __func__, scene); } return 0; } int scp_audio_pcm_recover_event(struct notifier_block *this, unsigned long event, void *ptr) { switch (event) { case SCP_EVENT_READY: { pr_info("%s(), SCP_EVENT_READY\n", __func__); mtk_reinit_scp_audio(); rv_standby_flag = 0; break; } case SCP_EVENT_STOP: rv_standby_flag = 1; mtk_reset_scp_audio_sharemem(); //Release memif ctrl back to AP pr_info("%s(), SCP_EVENT_STOP\n", __func__); break; } return NOTIFY_DONE; } void init_scp_spk_process_enable(int enable_flag) { pr_info("%s enable_flag: %d\n", __func__, enable_flag); mscpSpkProcessEnable = enable_flag; } static int scp_spk_process_enable_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { pr_info("%s enable_flag: %d\n", __func__, mscpSpkProcessEnable); mscpSpkProcessEnable = ucontrol->value.integer.value[0]; return 0; } static int scp_spk_process_enable_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { ucontrol->value.integer.value[0] = mscpSpkProcessEnable; return 0; } static int is_scp_ready_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { ucontrol->value.integer.value[0] = is_scp_audio_ready(); return 0; } static int scp_dl_sharemem_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct mtk_scp_audio_base *scp_audio = get_scp_audio_base(); struct mtk_base_afe_memif *memif; int memif_num = -1; if (scp_audio == NULL) return -1; memif_num = scp_audio->dl_memif; if (memif_num < 0) return 0; memif = &audio_afe->memif[memif_num]; ucontrol->value.integer.value[0] = memif->use_scp_share_mem; return 0; } static int scp_dl_sharemem_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct mtk_scp_audio_base *scp_audio = get_scp_audio_base(); struct mtk_base_afe_memif *memif; int memif_num = -1; if (scp_audio == NULL) return -1; memif_num = scp_audio->dl_memif; if (memif_num >= 0) { memif = &audio_afe->memif[memif_num]; memif->use_scp_share_mem = ucontrol->value.integer.value[0]; } return 0; } static int scp_ul_sharemem_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct mtk_scp_audio_base *scp_audio = get_scp_audio_base(); struct mtk_base_afe_memif *memif; int memif_num = -1; if (scp_audio == NULL) return -1; memif_num = scp_audio->ul_memif; if (memif_num < 0) return 0; memif = &audio_afe->memif[memif_num]; ucontrol->value.integer.value[0] = memif->use_scp_share_mem; return 0; } static int scp_ul_sharemem_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct mtk_scp_audio_base *scp_audio = get_scp_audio_base(); struct mtk_base_afe_memif *memif; int memif_num = -1; if (scp_audio == NULL) return -1; memif_num = scp_audio->ul_memif; if (memif_num >= 0) { memif = &audio_afe->memif[memif_num]; memif->use_scp_share_mem = ucontrol->value.integer.value[0]; } return 0; } static int scp_ref_sharemem_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct mtk_scp_audio_base *scp_audio = get_scp_audio_base(); struct mtk_base_afe_memif *memif; int memif_num = -1; if (scp_audio == NULL) return -1; memif_num = scp_audio->ref_memif; if (memif_num < 0) return 0; memif = &audio_afe->memif[memif_num]; ucontrol->value.integer.value[0] = memif->use_scp_share_mem; return 0; } static int scp_ref_sharemem_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct mtk_scp_audio_base *scp_audio = get_scp_audio_base(); struct mtk_base_afe_memif *memif; int memif_num = -1; if (scp_audio == NULL) return -1; memif_num = scp_audio->ref_memif; if (memif_num >= 0) { memif = &audio_afe->memif[memif_num]; memif->use_scp_share_mem = ucontrol->value.integer.value[0]; } return 0; } static const struct snd_kcontrol_new scp_audio_platform_kcontrols[] = { SOC_SINGLE_EXT("scp_spk_process_enable", SND_SOC_NOPM, 0, 0xffff, 0, scp_spk_process_enable_get, scp_spk_process_enable_set), SOC_SINGLE_EXT("is_scp_ready", SND_SOC_NOPM, 0, 0xffff, 0, is_scp_ready_get, NULL), SOC_SINGLE_EXT("mtk_scp_spk_dl_scenario", SND_SOC_NOPM, 0, 0x1, 0, scp_dl_sharemem_get, scp_dl_sharemem_set), SOC_SINGLE_EXT("mtk_scp_spk_ul_scenario", SND_SOC_NOPM, 0, 0x1, 0, scp_ul_sharemem_get, scp_ul_sharemem_set), SOC_SINGLE_EXT("mtk_scp_spk_iv_scenario", SND_SOC_NOPM, 0, 0x1, 0, scp_ref_sharemem_get, scp_ref_sharemem_set), }; static int mtk_scp_audio_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct mtk_scp_audio_base *scp_aud = snd_soc_component_get_drvdata(component); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); int feature_id = get_feature_by_daiid(id); const char *task_name = get_taskname_by_daiid(id); pr_info("%s(), %s\n", __func__, task_name); if (!scp_aud || !task_base) { pr_info("%s(), NULL! %p,%p\n", __func__, scp_aud, task_base); return -1; } memcpy((void *)(&(runtime->hw)), (void *)scp_aud->scp_audio_hardware, sizeof(struct snd_pcm_hardware)); scp_register_feature(feature_id); /* send to task with open information */ ret = mtk_scp_audio_ipi_send(get_scene_by_daiid(id), AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_OPEN, 0, 0, NULL); if (ret < 0) { pr_info("%s() ret[%d]\n", __func__, ret); scp_deregister_feature(feature_id); goto error; } /* set the wait_for_avail to 2 sec*/ substream->wait_time = msecs_to_jiffies(2 * 1000); task_base->substream = substream; task_base->underflowed = 0; error: return ret; } static int mtk_scp_audio_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); int feature_id = get_feature_by_daiid(id); const char *task_name = get_taskname_by_daiid(id); pr_info("%s(), %s\n", __func__, task_name); if (!task_base) { pr_info("%s(), task base NULL!\n", __func__); return -1; } /* send to task with close information */ ret = mtk_scp_audio_ipi_send(get_scene_by_daiid(id), AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_CLOSE, 0, 0, NULL); if (ret) pr_info("%s ret[%d]\n", __func__, ret); scp_deregister_feature(feature_id); task_base->substream = NULL; return ret; } static int mtk_scp_audio_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_scp_audio_base *scp_audio = snd_soc_component_get_drvdata(component); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); void *ipi_audio_buf; /* dsp <-> audio data struct*/ int ret = 0; const char *task_name = get_taskname_by_daiid(id); pr_info("%s(), %s\n", __func__, task_name); if (!scp_audio || !task_base) { pr_info("%s(), NULL! %p,%p\n", __func__, scp_audio, task_base); return -1; } reset_audiobuffer_hw(&task_base->share_hw_buf); reset_audiobuffer_hw(&task_base->temp_work_buf); RingBuf_Reset(&task_base->ring_buf); scp_audio->request_dram_resource(scp_audio->dev); /* if already allocate , free it.*/ if (substream->dma_buffer.area) { ret = scp_audio_free_sharemem_ring(task_base, scp_audio->genpool); if (!ret) release_snd_dmabuffer(&substream->dma_buffer); } if (ret < 0) { pr_warn("%s err\n", __func__); return -1; } /* allocate ring buffer wioth share memory */ ret = scp_audio_allocate_sharemem_ring(task_base, params_buffer_bytes(params), scp_audio->genpool); if (ret < 0) { pr_warn("%s err\n", __func__); return -1; } #ifdef DEBUG_VERBOSE //dump_audio_dram(&task_base->msg_atod_share_buf); //dump_audio_dram(&task_base->msg_dtoa_share_buf); //dump_audio_dram(&task_base->ring_share_buf); #endif ret = dram_to_snd_dmabuffer(&task_base->ring_share_buf, &substream->dma_buffer); if (ret < 0) goto error; ret = set_audiobuffer_hw(&task_base->share_hw_buf, SCP_BUFFER_SHARE_MEM); if (ret < 0) goto error; ret = set_audiobuffer_memorytype(&task_base->share_hw_buf, MEMORY_DRAM); if (ret < 0) goto error; ret = set_audiobuffer_attribute(&task_base->share_hw_buf, substream, params, scp_audio_get_pcmdir(substream->stream, task_base->share_hw_buf)); if (ret < 0) goto error; memcpy(&task_base->temp_work_buf, &task_base->share_hw_buf, sizeof(struct audio_hw_buffer)); /* send audio_hw_buffer to SCP side */ ipi_audio_buf = (void *)task_base->msg_atod_share_buf.va_addr; memcpy((void *)ipi_audio_buf, (void *)&task_base->share_hw_buf, sizeof(struct audio_hw_buffer)); #ifdef DEBUG_VERBOSE dump_rbuf_s(__func__, &task_base->ring_buf); #endif /* send to task with hw_param information , buffer and pcm attribute */ mtk_scp_audio_ipi_send(get_scene_by_daiid(id), AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_HWPARAM, sizeof(task_base->msg_atod_share_buf.phy_addr), 0, (char *)&task_base->msg_atod_share_buf.phy_addr); return ret; error: pr_err("%s err\n", __func__); return -1; } static int mtk_scp_audio_pcm_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_scp_audio_base *scp_audio = snd_soc_component_get_drvdata(component); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); const char *task_name = get_taskname_by_daiid(id); pr_info("%s(), %s\n", __func__, task_name); if (!scp_audio || !task_base) { pr_info("%s(), NULL! %p,%p\n", __func__, scp_audio, task_base); return -1; } /* send to task with free status */ ret = mtk_scp_audio_ipi_send(get_scene_by_daiid(id), AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_HWFREE, 1, 0, NULL); if (ret) pr_info("%s ret[%d]\n", __func__, ret); if (scp_audio->genpool != NULL && substream->dma_buffer.area) { ret = scp_audio_free_sharemem_ring(task_base, scp_audio->genpool); if (!ret) release_snd_dmabuffer(&substream->dma_buffer); } /* reset buf setting */ ret = reset_audiobuffer_hw(&task_base->share_hw_buf); scp_audio->release_dram_resource(scp_audio->dev); return ret; } static int mtk_scp_audio_pcm_hw_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); void *ipi_audio_buf; /* dsp <-> audio data struct */ struct audio_hw_buffer *hw_buf = &task_base->share_hw_buf; const char *task_name = get_taskname_by_daiid(id); pr_info("%s(), %s\n", __func__, task_name); if (!task_base) { pr_info("%s(), task base NULL!\n", __func__); return -1; } /* The data type of stop_threshold in userspace is unsigned int. * However its data type in kernel space is unsigned long. * It needs to convert to ULONG_MAX in kernel space */ if (substream->runtime->stop_threshold == ~(0U)) substream->runtime->stop_threshold = ULONG_MAX; clear_audiobuffer_hw(hw_buf); RingBuf_Reset(&task_base->ring_buf); RingBuf_Bridge_Reset(&hw_buf->aud_buffer.buf_bridge); RingBuf_Bridge_Reset( &task_base->temp_work_buf.aud_buffer.buf_bridge); ret = set_audiobuffer_threshold(hw_buf, substream); if (ret < 0) pr_warn("%s set_audiobuffer_attribute err\n", __func__); pr_info("%s(), %s start_threshold: %u stop_threshold: %u period_size: %d period_count: %d\n", __func__, task_name, hw_buf->aud_buffer.start_threshold, hw_buf->aud_buffer.stop_threshold, hw_buf->aud_buffer.period_size, hw_buf->aud_buffer.period_count); /* send audio_hw_buffer to SCP side */ ipi_audio_buf = (void *)task_base->msg_atod_share_buf.va_addr; memcpy((void *)ipi_audio_buf, (void *)hw_buf, sizeof(struct audio_hw_buffer)); /* send to task with prepare status */ mtk_scp_audio_ipi_send(get_scene_by_daiid(id), AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_PREPARE, sizeof(task_base->msg_atod_share_buf.phy_addr), 0, (char *)&task_base->msg_atod_share_buf.phy_addr); return ret; } static int mtk_scp_audio_start(struct snd_pcm_substream *substream, struct mtk_scp_audio_base *scp_audio) { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); const char *task_name = get_taskname_by_daiid(id); pr_info("%s(), %s\n", __func__, task_name); if (!task_base) { pr_info("%s(), task base NULL!\n", __func__); return -1; } task_base->underflowed = 0; ret = mtk_scp_audio_ipi_send(get_scene_by_daiid(id), AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_DIRECT_SEND, AUDIO_DSP_TASK_START, 1, 0, NULL); return ret; } static int mtk_scp_audio_stop(struct snd_pcm_substream *substream, struct mtk_scp_audio_base *scp_audio) { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; /* Avoid print log in alsa stop. If underflow happens, * log will be printed in ISR. */ const char *task_name = get_taskname_by_daiid(id); pr_info("%s(), %s\n", __func__, task_name); ret = mtk_scp_audio_ipi_send(get_scene_by_daiid(id), AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_DIRECT_SEND, AUDIO_DSP_TASK_STOP, 1, 0, NULL); return ret; } static int mtk_scp_audio_pcm_hw_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { struct mtk_scp_audio_base *scp_audio = snd_soc_component_get_drvdata(component); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: return mtk_scp_audio_start(substream, scp_audio); case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: return mtk_scp_audio_stop(substream, scp_audio); } return -EINVAL; } static int mtk_scp_audio_pcm_copy_dl(struct snd_pcm_substream *substream, int copy_size, struct scp_aud_task_base *task_base, void __user *buf) { int ret = 0, availsize = 0; int ack_type; void *ipi_audio_buf; /* dsp <-> audio data struct */ struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct RingBuf *ringbuf = &task_base->ring_buf; struct ringbuf_bridge *buf_bridge = &(task_base->share_hw_buf.aud_buffer.buf_bridge); unsigned long flags = 0; spinlock_t *ringbuf_lock = &task_base->ringbuf_lock; const char *task_name = get_taskname_by_daiid(id); #ifdef DEBUG_VERBOSE dump_rbuf_s(__func__, &task_base->ring_buf); dump_rbuf_bridge_s(__func__, &task_base->share_hw_buf.aud_buffer.buf_bridge); #endif if (substream->runtime->status->state == SNDRV_PCM_STATE_XRUN) { pr_info_ratelimited("%s() %s state[%d]\n", __func__, task_name, substream->runtime->status->state); return -1; } Ringbuf_Check(ringbuf); Ringbuf_Bridge_Check( &task_base->share_hw_buf.aud_buffer.buf_bridge); spin_lock_irqsave(ringbuf_lock, flags); availsize = RingBuf_getFreeSpace(ringbuf); spin_unlock_irqrestore(ringbuf_lock, flags); if (availsize < copy_size) { pr_info("%s, id = %d, fail copy_size = %d availsize = %d\n", __func__, id, copy_size, RingBuf_getFreeSpace(ringbuf)); dump_rbuf_s("check dlcopy", &task_base->ring_buf); dump_rbuf_bridge_s("check dlcopy", &task_base->share_hw_buf.aud_buffer.buf_bridge); return -1; } RingBuf_copyFromUserLinear(ringbuf, buf, copy_size); RingBuf_Bridge_update_writeptr(buf_bridge, copy_size); /* send audio_hw_buffer to SCP side*/ ipi_audio_buf = (void *)task_base->msg_atod_share_buf.va_addr; memcpy((void *)ipi_audio_buf, (void *)&task_base->share_hw_buf, sizeof(struct audio_hw_buffer)); if (substream->runtime->status->state != SNDRV_PCM_STATE_RUNNING) ack_type = AUDIO_IPI_MSG_NEED_ACK; else ack_type = AUDIO_IPI_MSG_BYPASS_ACK; ret = mtk_scp_audio_ipi_send(get_scene_by_daiid(id), AUDIO_IPI_PAYLOAD, ack_type, AUDIO_DSP_TASK_DLCOPY, sizeof(task_base->msg_atod_share_buf.phy_addr), 0, (char *)&task_base->msg_atod_share_buf.phy_addr); #ifdef DEBUG_VERBOSE dump_rbuf_s(__func__, ringbuf); #endif return ret; } static int mtk_scp_audio_pcm_copy_ul(struct snd_pcm_substream *substream, int copy_size, struct scp_aud_task_base *task_base, void __user *buf) { int ret = 0, availsize = 0; void *ipi_audio_buf; /* dsp <-> audio data struct */ struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct RingBuf *ringbuf = &(task_base->ring_buf); unsigned long flags = 0; spinlock_t *ringbuf_lock = &task_base->ringbuf_lock; #ifdef DEBUG_VERBOSE dump_rbuf_s(__func__, &task_base->ring_buf); dump_rbuf_bridge_s(__func__, &task_base->share_hw_buf.aud_buffer.buf_bridge); #endif Ringbuf_Check(&task_base->ring_buf); Ringbuf_Bridge_Check( &task_base->share_hw_buf.aud_buffer.buf_bridge); spin_lock_irqsave(ringbuf_lock, flags); availsize = RingBuf_getDataCount(ringbuf); spin_unlock_irqrestore(ringbuf_lock, flags); if (availsize < copy_size) { pr_info("%s fail copy_size = %d availsize = %d\n", __func__, copy_size, RingBuf_getFreeSpace(ringbuf)); return -1; } /* get audio_buffer from ring buffer */ ringbuf_copyto_user_linear(buf, &task_base->ring_buf, copy_size); spin_lock_irqsave(ringbuf_lock, flags); sync_bridge_ringbuf_readidx(&task_base->share_hw_buf.aud_buffer.buf_bridge, &task_base->ring_buf); spin_unlock_irqrestore(ringbuf_lock, flags); ipi_audio_buf = (void *)task_base->msg_atod_share_buf.va_addr; memcpy((void *)ipi_audio_buf, (void *)&task_base->share_hw_buf, sizeof(struct audio_hw_buffer)); ret = mtk_scp_audio_ipi_send(get_scene_by_daiid(id), AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_ULCOPY, sizeof(task_base->msg_atod_share_buf.phy_addr), 0, (char *)&task_base->msg_atod_share_buf.phy_addr); #ifdef DEBUG_VERBOSE dump_rbuf_bridge_s("1 mtk_scp_audio_ul_handler", &task_base->share_hw_buf.aud_buffer.buf_bridge); dump_rbuf_s("1 mtk_scp_audio_ul_handler", &task_base->ring_buf); #endif return ret; } static int mtk_scp_audio_pcm_copy(struct snd_soc_component *component, struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, void __user *buf, unsigned long bytes) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); int ret = 0; if (!task_base) { pr_info("%s(), task base NULL!\n", __func__); return -1; } if (bytes <= 0) { pr_info( "error %s channel = %d pos = %lu count = %lu bytes = %d\n", __func__, channel, pos, bytes, bytes); return -1; } if (is_audio_task_dsp_ready(TASK_SCENE_RV_SPK_PROCESS) == false) { //TODO not fix pr_info("%s(), scp not ready", __func__); return -1; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = mtk_scp_audio_pcm_copy_dl(substream, bytes, task_base, buf); else ret = mtk_scp_audio_pcm_copy_ul(substream, bytes, task_base, buf); return ret; } static bool mtk_scp_aud_check_exception(struct mtk_scp_audio_base *scp_aud, struct ipi_msg_t *ipi_msg, int id) { struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); const char *task_name = get_taskname_by_daiid(id); if (!task_base) { pr_info("%s(), task base NULL!\n", __func__); return -1; } if (!task_base->substream) { pr_info_ratelimited("%s() %s substream NULL\n", __func__, task_name); return false; } if (!snd_pcm_running(task_base->substream)) { pr_info_ratelimited("%s() %s state[%d]\n", __func__, task_name, task_base->substream->runtime->status->state); return false; } /* reset message */ if (ipi_msg && ipi_msg->param2 == SCP_DL_CONSUME_RESET) { pr_info("%s() %s scp audio reset\n", __func__, task_name); RingBuf_Reset(&task_base->ring_buf); snd_pcm_period_elapsed(task_base->substream); return true; } /* underflow message */ if (ipi_msg && ipi_msg->param2 == SCP_DL_CONSUME_UNDERFLOW) { pr_info("%s() %s scp audio underflow\n", __func__, task_name); task_base->underflowed = true; snd_pcm_period_elapsed(task_base->substream); return true; } return false; } static void mtk_scp_aud_dl_consume_handler(struct mtk_scp_audio_base *scp_aud, struct ipi_msg_t *ipi_msg, int id) { struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); spinlock_t *ringbuf_lock = &task_base->ringbuf_lock; struct snd_pcm_substream *substream; unsigned long flags = 0; if (!task_base) { pr_info("%s(), task base NULL!\n", __func__); return; } if (!task_base->substream) { pr_info("%s(), substream NULL!\n", __func__); return; } if (!snd_pcm_running(task_base->substream)) { pr_info("%s(), substream not running!\n", __func__); return; } substream = task_base->substream; // handle for no restart pcm, copy audio_hw_buffer from msg payload, others from share mem if ((substream->runtime->stop_threshold > substream->runtime->start_threshold) && ipi_msg) { memcpy((void *)&task_base->temp_work_buf, ipi_msg->payload, sizeof(struct audio_hw_buffer)); } else { unsigned long long read, base, end; read = mbox_msg_temp->pRead; base = task_base->temp_work_buf.aud_buffer.buf_bridge.pBufBase; end = task_base->temp_work_buf.aud_buffer.buf_bridge.pBufEnd; if (read < base || read > end) { pr_info("%s(), wrong readidx 0x%llx from SCP!\n", __func__, read); return; } task_base->temp_work_buf.aud_buffer.buf_bridge.pRead = mbox_msg_temp->pRead; } spin_lock_irqsave(ringbuf_lock, flags); task_base->share_hw_buf.aud_buffer.buf_bridge.pRead = task_base->temp_work_buf.aud_buffer.buf_bridge.pRead; #ifdef DEBUG_VERBOSE_IRQ dump_rbuf_s("dl_consume before sync", &task_base->ring_buf); dump_rbuf_bridge_s("dl_consume before sync", &task_base->share_hw_buf.aud_buffer.buf_bridge); #endif sync_ringbuf_readidx( &task_base->ring_buf, &task_base->share_hw_buf.aud_buffer.buf_bridge); spin_unlock_irqrestore(ringbuf_lock, flags); #ifdef DEBUG_VERBOSE_IRQ dump_rbuf_s("dl_consume after sync", &task_base->ring_buf); #endif /* notify subsream */ snd_pcm_period_elapsed(task_base->substream); } static void mtk_scp_aud_dl_handler(struct mtk_scp_audio_base *scp_aud, struct ipi_msg_t *ipi_msg, int id) { struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); if (!task_base) { pr_info("%s(), task base NULL!\n", __func__); return; } if (task_base->substream == NULL) { pr_info("%s = substream == NULL\n", __func__); goto SCP_IRQ_HANDLER_ERR; } if (!snd_pcm_running(task_base->substream)) { pr_info("%s = state[%d]\n", __func__, task_base->substream->runtime->status->state); goto SCP_IRQ_HANDLER_ERR; } /* notify subsream */ snd_pcm_period_elapsed(task_base->substream); SCP_IRQ_HANDLER_ERR: return; } static void mtk_scp_aud_ul_handler(struct mtk_scp_audio_base *scp_aud, struct ipi_msg_t *ipi_msg, int id) { struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); void *ipi_audio_buf; unsigned long flags; spinlock_t *ringbuf_lock = &task_base->ringbuf_lock; if (!task_base) { pr_info("%s(), task base NULL!\n", __func__); return; } if (!task_base->substream) { pr_info("%s substream NULL\n", __func__); return; } if (!snd_pcm_running(task_base->substream)) { pr_info("%s = state[%d]\n", __func__, task_base->substream->runtime->status->state); goto SCP_IRQ_HANDLER_ERR; } if (task_base->substream->runtime->status->state != SNDRV_PCM_STATE_RUNNING) { pr_info("%s = state[%d]\n", __func__, task_base->substream->runtime->status->state); goto SCP_IRQ_HANDLER_ERR; } if (ipi_msg && ipi_msg->param2 == SCP_UL_READ_RESET) { spin_lock_irqsave(ringbuf_lock, flags); RingBuf_Reset(&task_base->ring_buf); /* set buf size full to trigger pcm_read */ task_base->ring_buf.datacount = task_base->ring_buf.bufLen; spin_unlock_irqrestore(ringbuf_lock, flags); pr_info("%s reset UL\n", __func__); snd_pcm_period_elapsed(task_base->substream); goto SCP_IRQ_HANDLER_ERR; } /* upadte for write index*/ ipi_audio_buf = (void *)task_base->msg_dtoa_share_buf.va_addr; memcpy((void *)&task_base->temp_work_buf, (void *)ipi_audio_buf, sizeof(struct audio_hw_buffer)); task_base->share_hw_buf.aud_buffer.buf_bridge.pWrite = (task_base->temp_work_buf.aud_buffer.buf_bridge.pWrite); #ifdef DEBUG_VERBOSE dump_rbuf_bridge_s(__func__, &task_base->temp_work_buf.aud_buffer.buf_bridge); dump_rbuf_bridge_s(__func__, &task_base->share_hw_buf.aud_buffer.buf_bridge); #endif spin_lock_irqsave(ringbuf_lock, flags); sync_ringbuf_writeidx(&task_base->ring_buf, &task_base->share_hw_buf.aud_buffer.buf_bridge); spin_unlock_irqrestore(ringbuf_lock, flags); #ifdef DEBUG_VERBOSE dump_rbuf_s(__func__, &task_base->ring_buf); #endif /* notify subsream */ snd_pcm_period_elapsed(task_base->substream); SCP_IRQ_HANDLER_ERR: return; } void scp_aud_ipi_handler(struct mtk_scp_audio_base *scp_aud, struct ipi_msg_t *ipi_msg) { int id = 0; if (!scp_aud) { pr_info("%s scp_aud NULL", __func__); return; } if (ipi_msg == NULL) { pr_info("%s ipi_msg == NULL\n", __func__); return; } id = get_daiid_by_scene(ipi_msg->task_scene); if (id < 0) return; if (!is_audio_task_dsp_ready(ipi_msg->task_scene)) { pr_info("%s(), is_scp_ready send false\n", __func__); return; } switch (ipi_msg->msg_id) { case AUDIO_DSP_TASK_IRQDL: mtk_scp_aud_dl_handler(scp_aud, ipi_msg, id); break; case AUDIO_DSP_TASK_IRQUL: mtk_scp_aud_ul_handler(scp_aud, ipi_msg, id); break; case AUDIO_DSP_TASK_DL_CONSUME_DATA: /* check exceptions in consume message */ if (mtk_scp_aud_check_exception(scp_aud, ipi_msg, id)) break; /* Handle consume message for the platforms * which not support audio IRQ. */ mtk_scp_aud_dl_consume_handler(scp_aud, ipi_msg, id); break; default: break; } } void scp_audio_pcm_ipi_recv(struct ipi_msg_t *ipi_msg) { struct mtk_scp_audio_base *scp_aud = get_scp_audio_base(); 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_scp_ready send false\n", __func__); return; } if (scp_aud->ipi_ops.ipi_handler) scp_aud->ipi_ops.ipi_handler(scp_aud, ipi_msg); } static snd_pcm_uframes_t mtk_scp_audio_pcm_pointer_ul (struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); int ptr_bytes; if (!task_base) { pr_info("%s(), task base NULL!\n", __func__); return SNDRV_PCM_POS_XRUN; } #ifdef DEBUG_VERBOSE dump_rbuf_s(__func__, &task_base->ring_buf); #endif ptr_bytes = task_base->ring_buf.pWrite - task_base->ring_buf.pBufBase; return bytes_to_frames(substream->runtime, ptr_bytes); } static snd_pcm_uframes_t mtk_scp_audio_pcm_pointer_dl (struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int id = cpu_dai->id; struct scp_aud_task_base *task_base = get_taskbase_by_daiid(id); int pcm_ptr_bytes, pcm_remap_ptr_bytes; spinlock_t *ringbuf_lock = &task_base->ringbuf_lock; unsigned long flags = 0; if (!task_base) { pr_info("%s(), task base NULL!\n", __func__); return SNDRV_PCM_POS_XRUN; } #ifdef DEBUG_VERBOSE dump_rbuf_s("-mtk_scphw_pcm_pointer_dl", &task_base->ring_buf); #endif /* handle for underflow */ if (task_base->underflowed) return -1; spin_lock_irqsave(ringbuf_lock, flags); pcm_ptr_bytes = (int)(task_base->ring_buf.pRead - task_base->ring_buf.pBufBase); spin_unlock_irqrestore(ringbuf_lock, flags); pcm_remap_ptr_bytes = bytes_to_frames(substream->runtime, pcm_ptr_bytes); return pcm_remap_ptr_bytes; } static snd_pcm_uframes_t mtk_scp_audiohw_pcm_pointer (struct snd_soc_component *component, struct snd_pcm_substream *substream) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return mtk_scp_audio_pcm_pointer_dl(substream); else return mtk_scp_audio_pcm_pointer_ul(substream); } static int scp_audio_mbox_recv_handler(unsigned int id, void *prdata, void *data, unsigned int len) { int daiid; memcpy((void *)mbox_msg_temp, data, sizeof(struct mbox_msg)); daiid = get_daiid_by_scene(mbox_msg_temp->scene_id); mtk_scp_aud_dl_consume_handler(get_scp_audio_base(), NULL, daiid); return 0; } static void scp_audio_tasklet_hdl(struct tasklet_struct *t) { int daiid = get_daiid_by_scene(mbox_msg_temp->scene_id); struct scp_aud_task_base *task_base = get_taskbase_by_daiid(daiid); pr_debug("%s(), scene: %x, msgid: %x, read: %llx, work read: %llx", __func__, mbox_msg_temp->scene_id, mbox_msg_temp->msg_id, mbox_msg_temp->pRead, task_base->temp_work_buf.aud_buffer.buf_bridge.pRead); mtk_scp_aud_dl_consume_handler(get_scp_audio_base(), NULL, daiid); } static int mtk_scp_audio_probe(struct snd_soc_component *component) { int ret = 0, id = 0; struct mtk_scp_audio_base *scp_audio = snd_soc_component_get_drvdata(component); struct scp_aud_task_base *task_base; pr_info("%s scp_audio = %p\n", __func__, scp_audio); //scp_audio_wakelock = aud_wake_lock_init(NULL, "scp_audio_wakelock"); //if (scp_audio_wakelock == NULL) //pr_info("%s init scp_audio_wakelock error\n", __func__); ret = snd_soc_add_component_controls(component, scp_audio_platform_kcontrols, ARRAY_SIZE(scp_audio_platform_kcontrols)); if (ret) pr_info("%s add controls err ret = %d\n", __func__, ret); for (id = 0; id < SCP_AUD_TASK_DAI_NUM; id++) { task_base = get_taskbase_by_daiid(id); spin_lock_init(&task_base->ringbuf_lock); ret = audio_task_register_callback(get_scene_by_daiid(id), scp_audio_pcm_ipi_recv); if (ret < 0) return ret; } mbox_msg_temp = devm_kzalloc(component->dev, sizeof(struct mbox_msg), GFP_KERNEL); if (!mbox_msg_temp) return -ENOMEM; mtk_ipi_register(&scp_ipidev, IPI_IN_RV_SPK_PROCESS, (void *)scp_audio_mbox_recv_handler, NULL, mbox_msg_temp); tasklet_setup(&scp_audio->tasklet, scp_audio_tasklet_hdl); if (ret) pr_info("%s add_component err ret = %d\n", __func__, ret); return ret; } const struct snd_soc_component_driver mtk_scp_audio_pcm_platform = { .name = AFE_SCP_AUDIO_NAME, .probe = mtk_scp_audio_probe, .open = mtk_scp_audio_pcm_open, .close = mtk_scp_audio_pcm_close, .hw_params = mtk_scp_audio_pcm_hw_params, .hw_free = mtk_scp_audio_pcm_hw_free, .prepare = mtk_scp_audio_pcm_hw_prepare, .trigger = mtk_scp_audio_pcm_hw_trigger, .pointer = mtk_scp_audiohw_pcm_pointer, .copy_user = mtk_scp_audio_pcm_copy, }; EXPORT_SYMBOL_GPL(mtk_scp_audio_pcm_platform); MODULE_DESCRIPTION("Mediatek scp audio pcm platform driver"); MODULE_AUTHOR("Zhixiong Wang"); MODULE_LICENSE("GPL v2");