1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-02-15 00:18:03 +00:00
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

1589 lines
43 KiB
C

// 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 <audio_task_manager.h>
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)
{
dev_info(&pdev->dev, "%s()\n", __func__);
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);
if (task_base == NULL) {
pr_info("%s task_base == NULL\n", __func__);
return -1;
}
/* send share message to adsp 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 == NULL) {
pr_info("%s task_base == NULL\n", __func__);
return -1;
}
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);
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;
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 = %lu\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_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 == NULL) {
pr_info("%s task_base == NULL\n", __func__);
return 0;
}
#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 == NULL) {
pr_info("%s task_base == NULL\n", __func__);
return 0;
}
#ifdef DEBUG_VERBOSE
dump_rbuf_s("-mtk_dsphw_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));
for (id = 0; id < SCP_AUD_TASK_DAI_NUM; id++) {
task_base = get_taskbase_by_daiid(id);
if (task_base == NULL) {
pr_info("%s task_base NULL, id: %d\n", __func__, id);
return -1;
}
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<zhixiong.wang@mediatek.com>");
MODULE_LICENSE("GPL v2");