1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-07-03 17:37:33 +00:00
Files
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

528 lines
13 KiB
C

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