/* SPDX-License-Identifier: GPL-2.0 */ /* * mtk-scp-spk-platform.c -- Mediatek scp spk platform * * Copyright (c) 2018 MediaTek Inc. * Author: Shane Chien */ #include #include #include #include #include #include "audio_task_manager.h" #include "scp_helper.h" #include "audio_spkprotect_msg_id.h" #include "audio_ipi_client_spkprotect.h" #include "audio_task_manager.h" #include "mtk-scp-spk-mem-control.h" #include "mtk-scp-spk-platform-mem-control.h" #include "mtk-scp-spk-platform-driver.h" #include "mtk-base-scp-spk.h" #include "mtk-scp-spk-common.h" #include "mtk-base-afe.h" #include "audio_buf.h" static DEFINE_SPINLOCK(scp_spk_ringbuf_lock); #define GET_SYSTEM_TIMER_CYCLE(void) \ ({ \ unsigned long long __ret = arch_counter_get_cntvct(); \ __ret; \ }) #define SPK_IPIMSG_TIMEOUT (50) #define SPK_WAITCHECK_INTERVAL_MS (2) static bool spk_ipi_wait; static const char *const mtk_scp_spk_dump_str[] = {"off", "normal_dump", "split_dump"}; static const struct soc_enum mtk_scp_spk_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mtk_scp_spk_dump_str), mtk_scp_spk_dump_str), }; static int mtk_scp_spk_scp_dump_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_base_scp_spk *scp_spk = snd_soc_component_get_drvdata(cmpnt); struct mtk_base_scp_spk_dump *spk_dump = &scp_spk->spk_dump; dev_dbg(scp_spk->dev, "%s()\n", __func__); if (ucontrol->value.enumerated.item[0] > ARRAY_SIZE(mtk_scp_spk_dump_str)) { dev_dbg(scp_spk->dev, "return -EINVAL\n"); return -EINVAL; } ucontrol->value.integer.value[0] = spk_dump->dump_flag; return 0; } static int mtk_scp_spk_scp_dump_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_base_scp_spk *scp_spk = snd_soc_component_get_drvdata(cmpnt); struct mtk_base_scp_spk_dump *spk_dump = &scp_spk->spk_dump; struct mtk_base_scp_spk_mem *spk_mem = &scp_spk->spk_mem; struct mtk_base_afe *afe = get_afe_base(); static int ctrl_val; int ret, timeout = 0; unsigned int payload_len = 0; dev_dbg(scp_spk->dev, "%s(), value = %ld, dump_flag = %d\n", __func__, ucontrol->value.integer.value[0], spk_dump->dump_flag); if (spk_dump->dump_flag == false && ucontrol->value.integer.value[0] > 0) { ctrl_val = ucontrol->value.integer.value[0]; spk_dump->dump_flag = true; /* scp spk dump buffer use dram */ if (afe->request_dram_resource) afe->request_dram_resource(afe->dev); if (ctrl_val == 1) ret = spkprotect_open_dump_file(); else if (ctrl_val == 2) spk_pcm_dump_split_task_enable(); else { dev_dbg(scp_spk->dev, "%s(), value not support, return\n", __func__); return -1; } if (ret < 0) { dev_dbg(scp_spk->dev, "%s(), open dump file fail, return\n", __func__); return -1; } payload_len = mtk_scp_spk_pack_payload( SPK_PROTTCT_PCMDUMP_ON, spk_dump->dump_resv_mem.size, spk_dump->dump_resv_mem.phy_addr, NULL, NULL); mtk_scp_spk_ipi_send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_BYPASS_ACK, SPK_PROTTCT_PCMDUMP_ON, payload_len, spk_dump->dump_flag, (char *)spk_mem->ipi_payload_buf); spk_ipi_wait = true; } else if (spk_dump->dump_flag == true && ucontrol->value.integer.value[0] == 0) { spk_dump->dump_flag = false; while (spk_ipi_wait) { msleep(SPK_WAITCHECK_INTERVAL_MS); if (timeout++ >= SPK_IPIMSG_TIMEOUT) spk_ipi_wait = false; } if (ctrl_val == 1) spkprotect_close_dump_file(); else if (ctrl_val == 2) spk_pcm_dump_split_task_disable(); else { dev_dbg(scp_spk->dev, "%s(), value not support, return\n", __func__); return -1; } mtk_scp_spk_ipi_send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_BYPASS_ACK, SPK_PROTTCT_PCMDUMP_OFF, 1, 0, NULL); /* scp spk dump buffer use dram */ if (afe->release_dram_resource) afe->release_dram_resource(afe->dev); ctrl_val = ucontrol->value.integer.value[0]; } return 0; } static int mtk_scp_spk_dl_scenario_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { const int scp_spk_memif_id = get_scp_spk_memif_id(SCP_SPK_DL_DAI_ID); struct mtk_base_afe *afe = get_afe_base(); ucontrol->value.integer.value[0] = afe->memif[scp_spk_memif_id].scp_spk_enable; return 0; } static int mtk_scp_spk_dl_scenario_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_base_scp_spk *scp_spk = snd_soc_component_get_drvdata(cmpnt); struct mtk_base_afe *afe = get_afe_base(); const int scp_spk_memif_id = get_scp_spk_memif_id(SCP_SPK_DL_DAI_ID); dev_info(scp_spk->dev, "%s(), %d\n", __func__, ucontrol->value.integer.value[0]); if (ucontrol->value.integer.value[0] > 0) afe->memif[scp_spk_memif_id].scp_spk_enable = true; else afe->memif[scp_spk_memif_id].scp_spk_enable = false; return 0; } static int mtk_scp_spk_iv_scenario_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { const int scp_spk_memif_id = get_scp_spk_memif_id(SCP_SPK_IV_DAI_ID); struct mtk_base_afe *afe = get_afe_base(); ucontrol->value.integer.value[0] = afe->memif[scp_spk_memif_id].scp_spk_enable; return 0; } static int mtk_scp_spk_iv_scenario_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_base_scp_spk *scp_spk = snd_soc_component_get_drvdata(cmpnt); struct mtk_base_afe *afe = get_afe_base(); const int scp_spk_memif_id = get_scp_spk_memif_id(SCP_SPK_IV_DAI_ID); dev_info(scp_spk->dev, "%s(), %d\n", __func__, ucontrol->value.integer.value[0]); if (ucontrol->value.integer.value[0] > 0) afe->memif[scp_spk_memif_id].scp_spk_enable = true; else afe->memif[scp_spk_memif_id].scp_spk_enable = false; return 0; } static int mtk_scp_spk_mdul_scenario_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { const int scp_spk_memif_id = get_scp_spk_memif_id(SCP_SPK_MDUL_DAI_ID); struct mtk_base_afe *afe = get_afe_base(); ucontrol->value.integer.value[0] = afe->memif[scp_spk_memif_id].scp_spk_enable; return 0; } static int mtk_scp_spk_mdul_scenario_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_base_scp_spk *scp_spk = snd_soc_component_get_drvdata(cmpnt); struct mtk_base_afe *afe = get_afe_base(); const int scp_spk_memif_id = get_scp_spk_memif_id(SCP_SPK_MDUL_DAI_ID); dev_info(scp_spk->dev, "%s(), %d\n", __func__, ucontrol->value.integer.value[0]); if (ucontrol->value.integer.value[0] > 0) afe->memif[scp_spk_memif_id].scp_spk_enable = true; else afe->memif[scp_spk_memif_id].scp_spk_enable = false; return 0; } static int mtk_scp_spk_set_iv_tcm_buf_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_base_scp_spk *scp_spk = snd_soc_component_get_drvdata(cmpnt); struct mtk_base_scp_spk_mem *spk_mem = &scp_spk->spk_mem; ucontrol->value.integer.value[0] = spk_mem->is_iv_buf_in_tcm; return 0; } static int mtk_scp_spk_set_iv_tcm_buf_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int ret; struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct mtk_base_scp_spk *scp_spk = snd_soc_component_get_drvdata(cmpnt); dev_info(scp_spk->dev, "%s(), %d\n", __func__, ucontrol->value.integer.value[0]); if (ucontrol->value.integer.value[0] > 0) ret = mtk_scp_spk_allocate_tcm_iv_buf(); return ret; } static const struct snd_kcontrol_new scp_spk_platform_kcontrols[] = { SOC_SINGLE_EXT("mtk_scp_spk_set_iv_tcm_buf", SND_SOC_NOPM, 0, 0x1, 0, mtk_scp_spk_set_iv_tcm_buf_get, mtk_scp_spk_set_iv_tcm_buf_set), SOC_ENUM_EXT("mtk_scp_spk_pcm_dump", mtk_scp_spk_enum[0], mtk_scp_spk_scp_dump_get, mtk_scp_spk_scp_dump_set), SOC_SINGLE_EXT("mtk_scp_spk_dl_scenario", SND_SOC_NOPM, 0, 0x1, 0, mtk_scp_spk_dl_scenario_get, mtk_scp_spk_dl_scenario_set), SOC_SINGLE_EXT("mtk_scp_spk_iv_scenario", SND_SOC_NOPM, 0, 0x1, 0, mtk_scp_spk_iv_scenario_get, mtk_scp_spk_iv_scenario_set), SOC_SINGLE_EXT("mtk_scp_spk_mdul_scenario", SND_SOC_NOPM, 0, 0x1, 0, mtk_scp_spk_mdul_scenario_get, mtk_scp_spk_mdul_scenario_set), }; static unsigned int scp_spk_word_size_align(unsigned int in_size) { unsigned int align_size; align_size = in_size & 0xFFFFFFE0; return align_size; } static snd_pcm_uframes_t mtk_scp_spk_pcm_pointer (struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_scp_spk *scp_spk = snd_soc_platform_get_drvdata(rtd->platform); struct mtk_base_scp_spk_mem *spk_mem = &scp_spk->spk_mem; struct RingBuf *ring_buf = &spk_mem->platform_ringbuf; struct mtk_base_afe *afe = get_afe_base(); struct mtk_base_afe_memif *memif = &afe->memif[spk_mem->spk_dl_memif_id]; const struct mtk_base_memif_data *memif_data = memif->data; int reg_ofs_base, reg_ofs_cur; int ret, pcm_consume_bytes, pcm_remap_consume_bytes; unsigned int hw_ptr = 0, hw_base = 0, ring_buf_ptr, pcm_hw_ptr; unsigned long flags; bool underflow = false; reg_ofs_base = memif_data->reg_ofs_base; reg_ofs_cur = memif_data->reg_ofs_cur; ret = regmap_read(afe->regmap, reg_ofs_base, &hw_base); if (ret || hw_base == 0) { dev_err(scp_spk->dev, "1 %s hw_base err: %d\n", __func__, hw_base); pcm_consume_bytes = 0; pcm_remap_consume_bytes = 0; goto POINTER_RETURN_FRAMES; } ret = regmap_read(afe->regmap, reg_ofs_cur, &hw_ptr); if (ret || hw_ptr == 0) { dev_err(scp_spk->dev, "2 %s hw_ptr err: %d\n", __func__, hw_ptr); pcm_consume_bytes = 0; pcm_remap_consume_bytes = 0; goto POINTER_RETURN_FRAMES; } spin_lock_irqsave(&scp_spk_ringbuf_lock, flags); pcm_hw_ptr = hw_ptr - hw_base; ring_buf_ptr = (unsigned int)(ring_buf->pRead - ring_buf->pBufBase); if (pcm_hw_ptr >= ring_buf_ptr) pcm_consume_bytes = pcm_hw_ptr - ring_buf_ptr; else pcm_consume_bytes = pcm_hw_ptr - ring_buf_ptr + ring_buf->bufLen; pcm_remap_consume_bytes = scp_spk_word_size_align(pcm_consume_bytes); scp_spk_debug("%s(), consume_bytes:0x%x(%d), datacount:%d\n", __func__, pcm_remap_consume_bytes, pcm_remap_consume_bytes, ring_buf->datacount); if (pcm_remap_consume_bytes > ring_buf->datacount) { dev_err(scp_spk->dev, "%s(), underflow\n", __func__); underflow = true; } RingBuf_update_readptr(ring_buf, pcm_remap_consume_bytes); ring_buf_ptr = (unsigned int)(ring_buf->pRead - ring_buf->pBufBase); spin_unlock_irqrestore(&scp_spk_ringbuf_lock, flags); POINTER_RETURN_FRAMES: if (underflow) return -1; return bytes_to_frames(substream->runtime, ring_buf_ptr); } void mtk_scp_spk_ipi_recv(struct ipi_msg_t *ipi_msg) { struct mtk_base_scp_spk *scp_spk = (struct mtk_base_scp_spk *)get_ipi_recv_private(); struct mtk_base_scp_spk_dump *spk_dump = &scp_spk->spk_dump; if (ipi_msg == NULL) { dev_warn(scp_spk->dev, "%s ipi_msg == NULL\n", __func__); return; } switch (ipi_msg->msg_id) { case SPK_PROTECT_IRQDL: if (scp_spk->spk_mem.substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING) { /* notify subsream */ snd_pcm_period_elapsed(scp_spk->spk_mem.substream); } else { dev_warn(scp_spk->dev, "%s() state error", __func__); } break; case SPK_PROTECT_PCMDUMP_OK: if (spk_dump->dump_ops->spk_dump_callback != NULL) spk_dump->dump_ops->spk_dump_callback(ipi_msg); break; default: break; } } static int mtk_scp_spk_pcm_open(struct snd_pcm_substream *substream) { int msg_id; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct mtk_base_scp_spk *scp_spk = snd_soc_platform_get_drvdata(rtd->platform); struct mtk_base_afe *afe = get_afe_base(); struct mtk_base_afe_memif *memif; struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage]; dev_info(scp_spk->dev, "%s() spk_dl_memif_id: %d, system cycle:%llu\n", __func__, scp_spk->spk_mem.spk_dl_memif_id, GET_SYSTEM_TIMER_CYCLE()); memcpy((void *)(&(runtime->hw)), (void *)scp_spk->mtk_dsp_hardware, sizeof(struct snd_pcm_hardware)); if (scp_spk->spk_mem.spk_dl_memif_id < 0) { dev_info(scp_spk->dev, "%s() spk_dl_memif_id < 0, return\n", __func__); return 0; } memif = &afe->memif[scp_spk->spk_mem.spk_dl_memif_id]; scp_register_feature(SPEAKER_PROTECT_FEATURE_ID); set_afe_irq_target(memif->irq_usage, true); scp_spk->spk_mem.substream = substream; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) msg_id = SPK_PROTECT_OPEN; else msg_id = SPK_PROTECT_SPEECH_OPEN; /* send to task with open information */ mtk_scp_spk_ipi_send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_NEED_ACK, msg_id, memif->irq_usage, irqs->irq_data->irq_scp_en_reg, NULL); return 0; } static int mtk_scp_spk_pcm_close(struct snd_pcm_substream *substream) { int msg_id; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_scp_spk *scp_spk = snd_soc_platform_get_drvdata(rtd->platform); struct mtk_base_afe *afe = get_afe_base(); struct mtk_base_afe_memif *memif; dev_info(scp_spk->dev, "%s() spk_dl_memif_id:%d, system cycle:%llu\n", __func__, scp_spk->spk_mem.spk_dl_memif_id, GET_SYSTEM_TIMER_CYCLE()); if (scp_spk->spk_mem.spk_dl_memif_id < 0) { dev_info(scp_spk->dev, "%s() spk_dl_memif_id < 0, return\n", __func__); return 0; } memif = &afe->memif[scp_spk->spk_mem.spk_dl_memif_id]; /* send to task with close information */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) msg_id = SPK_PROTECT_CLOSE; else msg_id = SPK_PROTECT_SPEECH_CLOSE; mtk_scp_spk_ipi_send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_NEED_ACK, msg_id, 0, 0, NULL); scp_spk->spk_mem.substream = NULL; set_afe_irq_target(memif->irq_usage, false); scp_deregister_feature(SPEAKER_PROTECT_FEATURE_ID); return 0; } static void mtk_scp_spk_pcm_hw_params_dl(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_scp_spk *scp_spk = snd_soc_platform_get_drvdata(rtd->platform); struct mtk_base_scp_spk_mem *spk_mem = &scp_spk->spk_mem; struct mtk_base_afe *afe = get_afe_base(); struct RingBuf *ring_buf = &spk_mem->platform_ringbuf; unsigned int payload_len = 0; int dl_using_dram = afe->memif[spk_mem->spk_dl_memif_id].using_sram ? 0 : 1; int iv_using_dram = afe->memif[spk_mem->spk_iv_memif_id].using_sram ? 0 : 1; dev_info(scp_spk->dev, "%s(), system cycle:%llu\n", __func__, GET_SYSTEM_TIMER_CYCLE()); substream->runtime->dma_bytes = params_buffer_bytes(params); mtk_scp_spk_allocate_platform_buf(substream->runtime->dma_bytes, &substream->runtime->dma_addr, &substream->runtime->dma_area); init_ring_buf(ring_buf, (char *)spk_mem->platform_dma_buf.area, spk_mem->platform_dma_buf.bytes); payload_len = mtk_scp_spk_pack_payload(SPK_PROTECT_PLATMEMPARAM, 0, 0, &spk_mem->platform_dma_buf, substream); mtk_scp_spk_ipi_send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, SPK_PROTECT_PLATMEMPARAM, payload_len, 0, (char *)spk_mem->ipi_payload_buf); payload_len = mtk_scp_spk_pack_payload(SPK_PROTECT_DLMEMPARAM, dl_using_dram, spk_mem->spk_dl_memif_id, &spk_mem->spk_dl_dma_buf, substream); mtk_scp_spk_ipi_send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, SPK_PROTECT_DLMEMPARAM, payload_len, 0, (char *)spk_mem->ipi_payload_buf); payload_len = mtk_scp_spk_pack_payload(SPK_PROTECT_IVMEMPARAM, iv_using_dram, spk_mem->spk_iv_memif_id, &spk_mem->spk_iv_dma_buf, substream); mtk_scp_spk_ipi_send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, SPK_PROTECT_IVMEMPARAM, payload_len, 0, (char *)spk_mem->ipi_payload_buf); } static void mtk_scp_spk_pcm_hw_params_ul(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_scp_spk *scp_spk = snd_soc_platform_get_drvdata(rtd->platform); struct mtk_base_scp_spk_mem *spk_mem = &scp_spk->spk_mem; struct mtk_base_afe *afe = get_afe_base(); unsigned int payload_len = 0; int dl_using_dram = afe->memif[spk_mem->spk_dl_memif_id].using_sram ? 0 : 1; int iv_using_dram = afe->memif[spk_mem->spk_iv_memif_id].using_sram ? 0 : 1; int md_ul_using_dram = afe->memif[spk_mem->spk_md_ul_memif_id].using_sram ? 0 : 1; dev_info(scp_spk->dev, "%s(), system cycle:%llu\n", __func__, GET_SYSTEM_TIMER_CYCLE()); payload_len = mtk_scp_spk_pack_payload( SPK_PROTECT_SPEECH_MDFEEDBACKPARAM, md_ul_using_dram, spk_mem->spk_md_ul_memif_id, &spk_mem->spk_md_ul_dma_buf, substream); mtk_scp_spk_ipi_send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, SPK_PROTECT_SPEECH_MDFEEDBACKPARAM, payload_len, 0, (char *)spk_mem->ipi_payload_buf); payload_len = mtk_scp_spk_pack_payload(SPK_PROTECT_SPEECH_DLMEMPARAM, dl_using_dram, spk_mem->spk_dl_memif_id, &spk_mem->spk_dl_dma_buf, substream); mtk_scp_spk_ipi_send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, SPK_PROTECT_SPEECH_DLMEMPARAM, payload_len, 0, (char *)spk_mem->ipi_payload_buf); payload_len = mtk_scp_spk_pack_payload(SPK_PROTECT_SPEECH_IVMEMPARAM, iv_using_dram, spk_mem->spk_iv_memif_id, &spk_mem->spk_iv_dma_buf, substream); mtk_scp_spk_ipi_send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, SPK_PROTECT_SPEECH_IVMEMPARAM, payload_len, 0, (char *)spk_mem->ipi_payload_buf); } static int mtk_scp_spk_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) mtk_scp_spk_pcm_hw_params_dl(substream, params); else mtk_scp_spk_pcm_hw_params_ul(substream); return 0; } static int mtk_scp_spk_pcm_hw_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_scp_spk *scp_spk = snd_soc_platform_get_drvdata(rtd->platform); int payload_len = 0; int msg_id; dev_info(scp_spk->dev, "%s(), system cycle:%llu\n", __func__, GET_SYSTEM_TIMER_CYCLE()); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) msg_id = SPK_PROTECT_PREPARE; else msg_id = SPK_PROTECT_SPEECH_PREPARE; payload_len = mtk_scp_spk_pack_payload(msg_id, 0, 0, NULL, substream); mtk_scp_spk_ipi_send(AUDIO_IPI_PAYLOAD, AUDIO_IPI_MSG_NEED_ACK, msg_id, payload_len, 0, (char *)scp_spk->spk_mem.ipi_payload_buf); return 0; } static int mtk_scp_spk_pcm_start(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_scp_spk *scp_spk = snd_soc_platform_get_drvdata(rtd->platform); struct mtk_base_afe *afe = get_afe_base(); struct mtk_base_scp_spk_mem *spk_mem = &scp_spk->spk_mem; struct mtk_base_afe_memif *dl_memif = &afe->memif[spk_mem->spk_dl_memif_id]; struct mtk_base_afe_memif *iv_memif = &afe->memif[spk_mem->spk_iv_memif_id]; struct mtk_base_afe_memif *md_ul_memif = &afe->memif[spk_mem->spk_md_ul_memif_id]; int irq_id = dl_memif->irq_usage; struct mtk_base_afe_irq *irqs = &afe->irqs[irq_id]; const struct mtk_base_irq_data *irq_data = irqs->irq_data; struct snd_pcm_runtime * const runtime = substream->runtime; unsigned int counter = runtime->period_size; int fs; dev_info(scp_spk->dev, "%s(), counter:%d, stream:%d, system cycle:%llu\n", __func__, counter, substream->stream, GET_SYSTEM_TIMER_CYCLE()); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { /* start md ul memif */ regmap_update_bits(afe->regmap, md_ul_memif->data->enable_reg, 1 << md_ul_memif->data->enable_shift, 1 << md_ul_memif->data->enable_shift); } /* start iv memif */ regmap_update_bits(afe->regmap, iv_memif->data->enable_reg, 1 << iv_memif->data->enable_shift, 1 << iv_memif->data->enable_shift); /* set dl irq counter */ regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg, irq_data->irq_cnt_maskbit << irq_data->irq_cnt_shift, counter << irq_data->irq_cnt_shift); /* set dl irq fs */ fs = afe->irq_fs(substream, runtime->rate); if (fs < 0) return -EINVAL; regmap_update_bits(afe->regmap, irq_data->irq_fs_reg, irq_data->irq_fs_maskbit << irq_data->irq_fs_shift, fs << irq_data->irq_fs_shift); /* start dl memif */ regmap_update_bits(afe->regmap, dl_memif->data->enable_reg, 1 << dl_memif->data->enable_shift, 1 << dl_memif->data->enable_shift); /* start dl irq */ regmap_update_bits(afe->regmap, irq_data->irq_en_reg, 1 << irq_data->irq_en_shift, 1 << irq_data->irq_en_shift); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { mtk_scp_spk_ipi_send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_DIRECT_SEND, SPK_PROTECT_SPEECH_START, 1, 0, NULL); } else { mtk_scp_spk_ipi_send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_DIRECT_SEND, SPK_PROTECT_START, 1, 0, NULL); } return 0; } static int mtk_scp_spk_pcm_stop(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_scp_spk *scp_spk = snd_soc_platform_get_drvdata(rtd->platform); struct RingBuf *ring_buf = &scp_spk->spk_mem.platform_ringbuf; struct mtk_base_afe *afe = get_afe_base(); struct mtk_base_scp_spk_mem *spk_mem = &scp_spk->spk_mem; struct mtk_base_afe_memif *dl_memif = &afe->memif[spk_mem->spk_dl_memif_id]; struct mtk_base_afe_memif *iv_memif = &afe->memif[spk_mem->spk_iv_memif_id]; struct mtk_base_afe_memif *md_ul_memif = &afe->memif[spk_mem->spk_md_ul_memif_id]; int irq_id = dl_memif->irq_usage; struct mtk_base_afe_irq *irqs = &afe->irqs[irq_id]; const struct mtk_base_irq_data *irq_data = irqs->irq_data; dev_info(scp_spk->dev, "%s(), system cycle:%llu\n", __func__, GET_SYSTEM_TIMER_CYCLE()); RingBuf_Reset(ring_buf); /* stop dl memif */ regmap_update_bits(afe->regmap, dl_memif->data->enable_reg, 1 << dl_memif->data->enable_shift, 0); /* stop iv memif */ regmap_update_bits(afe->regmap, iv_memif->data->enable_reg, 1 << iv_memif->data->enable_shift, 0); /* stop dl irq */ regmap_update_bits(afe->regmap, irq_data->irq_en_reg, 1 << irq_data->irq_en_shift, 0); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { /* start md ul memif */ regmap_update_bits(afe->regmap, md_ul_memif->data->enable_reg, 1 << md_ul_memif->data->enable_shift, 0); mtk_scp_spk_ipi_send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_DIRECT_SEND, SPK_PROTECT_SPEECH_STOP, 1, 0, NULL); } else { mtk_scp_spk_ipi_send(AUDIO_IPI_MSG_ONLY, AUDIO_IPI_MSG_DIRECT_SEND, SPK_PROTECT_STOP, 1, 0, NULL); } return 0; } static int mtk_scp_spk_pcm_hw_trigger(struct snd_pcm_substream *substream, int cmd) { switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: return mtk_scp_spk_pcm_start(substream); case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: return mtk_scp_spk_pcm_stop(substream); } return -EINVAL; } static int mtk_scp_spk_pcm_copy(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, void __user *buf, snd_pcm_uframes_t count) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_base_scp_spk *scp_spk = snd_soc_platform_get_drvdata(rtd->platform); struct mtk_base_scp_spk_mem *spk_mem = &scp_spk->spk_mem; struct RingBuf *ringbuf = &spk_mem->platform_ringbuf; unsigned int payload_len = 0; int ack_type = AUDIO_IPI_MSG_DIRECT_SEND; int availsize = 0, copy_size = 0, frame_cnt = 0; Ringbuf_Check(ringbuf); availsize = RingBuf_getFreeSpace(ringbuf); copy_size = scp_spk_word_size_align(count); // count: bytes frame_cnt = copy_size / snd_pcm_format_size( substream->runtime->format, substream->runtime->channels); scp_spk_debug("%s(), datacount:%d, copy_size:%d, count:%d, frame_cnt:%d\n", __func__, ringbuf->datacount, copy_size, count, frame_cnt); if (availsize >= copy_size) { RingBuf_copyFromUserLinear(ringbuf, buf, copy_size); } else { dev_info(scp_spk->dev, "%s() fail copy_size = %d availsize = %d\n", __func__, copy_size, RingBuf_getFreeSpace(ringbuf)); } Ringbuf_Check(ringbuf); payload_len = mtk_scp_spk_pack_payload(SPK_PROTECT_DLCOPY, pos, frame_cnt, NULL, substream); if (substream->runtime->status->state != SNDRV_PCM_STATE_RUNNING) ack_type = AUDIO_IPI_MSG_NEED_ACK; mtk_scp_spk_ipi_send(AUDIO_IPI_PAYLOAD, ack_type, SPK_PROTECT_DLCOPY, payload_len, 0, (char *)spk_mem->ipi_payload_buf); return 0; } static int mtk_scp_spk_pcm_new(struct snd_soc_pcm_runtime *rtd) { int ret = 0; struct mtk_base_scp_spk *scp_spk = snd_soc_platform_get_drvdata(rtd->platform); dev_info(scp_spk->dev, "%s()\n", __func__); snd_soc_add_platform_controls(rtd->platform, scp_spk_platform_kcontrols, ARRAY_SIZE(scp_spk_platform_kcontrols)); ret = mtk_scp_spk_reserved_dram_init(); if (ret < 0) return ret; audio_ipi_client_spkprotect_init(); mtk_scp_spk_dump_msg(&scp_spk->spk_dump); ret = audio_task_register_callback(TASK_SCENE_SPEAKER_PROTECTION, mtk_scp_spk_ipi_recv, NULL); if (ret < 0) return ret; return ret; } static const struct snd_pcm_ops mtk_scp_spk_pcm_ops = { .open = mtk_scp_spk_pcm_open, .close = mtk_scp_spk_pcm_close, .hw_params = mtk_scp_spk_pcm_hw_params, .prepare = mtk_scp_spk_pcm_hw_prepare, .trigger = mtk_scp_spk_pcm_hw_trigger, .ioctl = snd_pcm_lib_ioctl, .pointer = mtk_scp_spk_pcm_pointer, .copy_user = mtk_scp_spk_pcm_copy, }; const struct snd_soc_platform_driver mtk_scp_spk_pcm_platform = { .ops = &mtk_scp_spk_pcm_ops, .pcm_new = mtk_scp_spk_pcm_new, }; EXPORT_SYMBOL_GPL(mtk_scp_spk_pcm_platform); MODULE_DESCRIPTION("Mediatek scp spk platform driver"); MODULE_AUTHOR("Shane Chien "); MODULE_LICENSE("GPL v2");