1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-07-14 08:44:45 +00:00
Files
a155-U-u1/kernel-5.10/sound/soc/mediatek/audio_dsp/mtk-dsp-platform-driver.c
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

1416 lines
41 KiB
C

// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2018 MediaTek Inc.
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/string.h>
#include <sound/soc.h>
#include <audio_task_manager.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/pm_wakeup.h>
#include <linux/mutex.h>
#include <adsp_helper.h>
#include <audio_ipi_platform.h>
#include <audio_messenger_ipi.h>
#include "mtk-dsp-mem-control.h"
#include "mtk-base-dsp.h"
#include "mtk-dsp-common.h"
#include "mtk-dsp-platform-driver.h"
#include "mtk-base-afe.h"
static DEFINE_MUTEX(adsp_wakelock_lock);
#define IPIMSG_SHARE_MEM (1024)
#define DSP_IRQ_LOOP_COUNT (3)
static int adsp_wakelock_count;
static struct wakeup_source *adsp_audio_wakelock;
static int ktv_status;
//#define DEBUG_VERBOSE
//#define DEBUG_VERBOSE_IRQ
static inline unsigned long clr_bit(int bit, unsigned long *addr)
{
return (*addr & ~(1UL << bit));
}
static int dsp_task_attr_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int val = ucontrol->value.integer.value[0];
int id;
int dsp_task_id = -1;
int attr_id = -1;
const char *name = NULL;
/* get task attribute id */
if (strstr(kcontrol->id.name, "ref_runtime"))
attr_id = ADSP_TASK_ATTR_REF_RUNTIME;
else if (strstr(kcontrol->id.name, "runtime"))
attr_id = ADSP_TASK_ATTR_RUNTIME;
else if (strstr(kcontrol->id.name, "default"))
attr_id = ADSP_TASK_ATTR_DEFAULT;
else {
pr_info("%s(), attr_id not support: %s\n",
__func__, kcontrol->id.name);
return -1;
}
/* get dsp task id */
for (id = 0; id < AUDIO_TASK_DAI_NUM; id++) {
name = get_str_by_dsp_dai_id(id);
if (!name)
continue;
if (strstr(kcontrol->id.name, name)) {
dsp_task_id = id;
break;
}
}
if (dsp_task_id < 0) {
pr_info("%s(), %s dsp_task_id not support\n",
kcontrol->id.name, __func__);
return -1;
}
set_task_attr(dsp_task_id, attr_id, val);
return 0;
}
static int dsp_task_attr_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int id;
int dsp_task_id = -1;
int attr_id;
const char *name = NULL;
/* get task attribute id */
if (strstr(kcontrol->id.name, "ref_runtime"))
attr_id = ADSP_TASK_ATTR_REF_RUNTIME;
else if (strstr(kcontrol->id.name, "runtime"))
attr_id = ADSP_TASK_ATTR_RUNTIME;
else if (strstr(kcontrol->id.name, "default"))
attr_id = ADSP_TASK_ATTR_DEFAULT;
else {
pr_info("%s(), attr_id not support: %s\n",
__func__, kcontrol->id.name);
return -1;
}
/* get dsp task id */
for (id = 0; id < AUDIO_TASK_DAI_NUM; id++) {
name = get_str_by_dsp_dai_id(id);
if (!name)
continue;
if (strstr(kcontrol->id.name, name)) {
dsp_task_id = id;
break;
}
}
if (dsp_task_id < 0) {
pr_info("%s(), %s dsp_task_id not support\n",
kcontrol->id.name, __func__);
return -1;
}
ucontrol->value.integer.value[0] = get_task_attr(dsp_task_id, attr_id);
return 0;
}
static int dsp_wakelock_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int val = ucontrol->value.integer.value[0];
mutex_lock(&adsp_wakelock_lock);
if (val) {
adsp_wakelock_count++;
if (adsp_wakelock_count == 1)
aud_wake_lock(adsp_audio_wakelock);
} else {
adsp_wakelock_count--;
if (adsp_wakelock_count == 0)
aud_wake_unlock(adsp_audio_wakelock);
if (adsp_wakelock_count < 0) {
pr_info("%s not paired", __func__);
adsp_wakelock_count = 0;
}
}
mutex_unlock(&adsp_wakelock_lock);
return 0;
}
static int dsp_wakelock_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = adsp_wakelock_count;
return 0;
}
static int audio_dsp_version_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct mtk_base_dsp *dsp = snd_soc_component_get_drvdata(cmpnt);
if (!dsp)
return -1;
dsp->dsp_ver = ucontrol->value.integer.value[0];
return 0;
}
static int audio_dsp_version_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct mtk_base_dsp *dsp = snd_soc_component_get_drvdata(cmpnt);
if (!dsp)
return -1;
ucontrol->value.integer.value[0] = dsp->dsp_ver;
return 0;
}
static int smartpa_swdsp_process_enable_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
unsigned int val = ucontrol->value.integer.value[0];
set_task_attr(AUDIO_TASK_PLAYBACK_ID, ADSP_TASK_ATTR_SMARTPA, val);
return 0;
}
static int smartpa_swdsp_process_enable_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] =
get_task_attr(AUDIO_TASK_PLAYBACK_ID,
ADSP_TASK_ATTR_SMARTPA);
return 0;
}
static int ktv_status_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ktv_status = ucontrol->value.integer.value[0];
pr_debug("%s() ktv_status = %d\n", __func__, ktv_status);
return 0;
}
static int ktv_status_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = ktv_status;
pr_debug("%s() ktv_status = %ld\n", __func__, ktv_status);
return 0;
}
static const struct snd_kcontrol_new dsp_platform_kcontrols[] = {
SOC_SINGLE_EXT("dsp_primary_default_en", SND_SOC_NOPM, 0, 0xff, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_deepbuf_default_en", SND_SOC_NOPM, 0, 0xff, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_voipdl_default_en", SND_SOC_NOPM, 0, 0xff, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_playback_default_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_captureul1_default_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_offload_default_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_a2dp_default_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_bledl_default_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_bleul_default_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_dataprovider_default_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_call_final_default_en", SND_SOC_NOPM, 0, 0xff, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_fast_default_en", SND_SOC_NOPM, 0, 0xff, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_ktv_default_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_captureraw_default_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_fm_default_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_primary_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_deepbuf_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_voipdl_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_playback_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_music_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_captureul1_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_offload_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_a2dp_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_bledl_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_bleul_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_dataprovider_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_fast_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_ktv_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_captureraw_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_fm_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_call_final_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_captureul1_ref_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_playback_ref_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("dsp_call_final_ref_runtime_en", SND_SOC_NOPM, 0, 0x1, 0,
dsp_task_attr_get, dsp_task_attr_set),
SOC_SINGLE_EXT("audio_dsp_version", SND_SOC_NOPM, 0, 0xff, 0,
audio_dsp_version_get, audio_dsp_version_set),
SOC_SINGLE_EXT("swdsp_smartpa_process_enable", SND_SOC_NOPM, 0, 0xff, 0,
smartpa_swdsp_process_enable_get,
smartpa_swdsp_process_enable_set),
SOC_SINGLE_EXT("ktv_status", SND_SOC_NOPM, 0, 0x1, 0,
ktv_status_get, ktv_status_set),
SOC_SINGLE_EXT("audio_dsp_wakelock", SND_SOC_NOPM, 0, 0x1, 0,
dsp_wakelock_get, dsp_wakelock_set),
};
static snd_pcm_uframes_t mtk_dsphw_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 snd_soc_component *component =
snd_soc_rtdcom_lookup(rtd, AFE_DSP_NAME);
struct mtk_base_dsp *dsp = snd_soc_component_get_drvdata(component);
struct mtk_base_dsp_mem *dsp_mem = &dsp->dsp_mem[id];
int ptr_bytes;
#ifdef DEBUG_VERBOSE
dump_rbuf_s(__func__, &dsp_mem->ring_buf);
#endif
ptr_bytes = dsp_mem->ring_buf.pWrite - dsp_mem->ring_buf.pBufBase;
return bytes_to_frames(substream->runtime, ptr_bytes);
}
static unsigned int dsp_word_size_align(unsigned int in_size)
{
unsigned int align_size;
align_size = in_size & 0xFFFFFF80;
return align_size;
}
static int afe_remap_dsp_pointer
(struct buf_attr dst, struct buf_attr src, int bytes)
{
int retval = bytes;
retval = (retval * snd_pcm_format_physical_width(src.format))
/ snd_pcm_format_physical_width(dst.format);
retval = (retval * src.rate) / dst.rate;
retval = (retval * src.channel) / dst.channel;
return retval;
}
static snd_pcm_uframes_t mtk_dsphw_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 snd_soc_component *component =
snd_soc_rtdcom_lookup(rtd, AFE_DSP_NAME);
struct mtk_base_dsp *dsp = snd_soc_component_get_drvdata(component);
struct mtk_base_dsp_mem *dsp_mem = &dsp->dsp_mem[id];
struct mtk_base_afe *afe;
struct mtk_base_afe_memif *memif;
const struct mtk_base_memif_data *memif_data;
struct regmap *regmap;
struct device *dev;
int reg_ofs_base;
int reg_ofs_cur;
unsigned int hw_ptr = 0, hw_base = 0;
int ret, pcm_ptr_bytes, pcm_remap_ptr_bytes;
spinlock_t *ringbuf_lock = &dsp_mem->ringbuf_lock;
unsigned long flags = 0;
if (dsp->dsp_ver)
goto SYNC_READINDEX;
afe = get_afe_base();
if (!afe) {
pr_info("%s afe = %p\n", __func__, afe);
AUD_ASSERT(0);
return 0;
}
memif = &afe->memif[get_afememdl_by_afe_taskid(id)];
if (!memif) {
pr_info("%s memif = %p\n", __func__, memif);
AUD_ASSERT(0);
return 0;
}
memif_data = memif->data;
if (!memif_data) {
pr_info("%s memif_data = %p\n", __func__, memif_data);
AUD_ASSERT(0);
return 0;
}
regmap = afe->regmap;
if (!regmap) {
pr_info("%s regmap = %p\n", __func__, regmap);
AUD_ASSERT(0);
return 0;
}
dev = afe->dev;
if (!dev) {
pr_info("%s dev = %p\n", __func__, dev);
AUD_ASSERT(0);
return 0;
}
reg_ofs_base = memif_data->reg_ofs_base;
reg_ofs_cur = memif_data->reg_ofs_cur;
ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
if (ret || hw_ptr == 0) {
dev_err(dev, "1 %s hw_ptr err\n", __func__);
pcm_ptr_bytes = 0;
pcm_remap_ptr_bytes = 0;
goto POINTER_RETURN_FRAMES;
}
ret = regmap_read(regmap, reg_ofs_base, &hw_base);
if (ret || hw_base == 0) {
dev_err(dev, "2 %s hw_ptr err\n", __func__);
pcm_ptr_bytes = 0;
pcm_remap_ptr_bytes = 0;
goto POINTER_RETURN_FRAMES;
}
pcm_ptr_bytes = hw_ptr - hw_base;
pcm_remap_ptr_bytes =
afe_remap_dsp_pointer(
dsp_mem->audio_afepcm_buf.aud_buffer.buffer_attr,
dsp_mem->adsp_buf.aud_buffer.buffer_attr,
pcm_ptr_bytes);
pcm_remap_ptr_bytes = dsp_word_size_align(pcm_remap_ptr_bytes);
if (pcm_remap_ptr_bytes >=
dsp_mem->adsp_buf.aud_buffer.buf_bridge.bufLen)
pr_info("%s pcm_remap_ptr_bytes = %d",
__func__,
pcm_remap_ptr_bytes);
else
dsp_mem->adsp_buf.aud_buffer.buf_bridge.pRead =
(dsp_mem->adsp_buf.aud_buffer.buf_bridge.pBufBase +
pcm_remap_ptr_bytes);
spin_lock_irqsave(ringbuf_lock, flags);
ret = sync_ringbuf_readidx(
&dsp_mem->ring_buf,
&dsp_mem->adsp_buf.aud_buffer.buf_bridge);
spin_unlock_irqrestore(ringbuf_lock, flags);
if (ret) {
pr_info("%s sync_ringbuf_readidx underflow\n", __func__);
return -1;
}
#ifdef DEBUG_VERBOSE
pr_info("%s id = %d reg_ofs_base = %d reg_ofs_cur = %d pcm_ptr_bytes = %d pcm_remap_ptr_bytes = %d\n",
__func__, id, reg_ofs_base, reg_ofs_cur,
pcm_ptr_bytes, pcm_remap_ptr_bytes);
#endif
POINTER_RETURN_FRAMES:
return bytes_to_frames(substream->runtime, pcm_remap_ptr_bytes);
SYNC_READINDEX:
#ifdef DEBUG_VERBOSE
dump_rbuf_s("-mtk_dsphw_pcm_pointer_dl", &dsp_mem->ring_buf);
#endif
/* handle for underflow */
if (dsp_mem->underflowed)
return -1;
spin_lock_irqsave(ringbuf_lock, flags);
pcm_ptr_bytes = (int)(dsp_mem->ring_buf.pRead -
dsp_mem->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_dsphw_pcm_pointer
(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return mtk_dsphw_pcm_pointer_dl(substream);
else
return mtk_dsphw_pcm_pointer_ul(substream);
}
static void mtk_dsp_dl_handler(struct mtk_base_dsp *dsp,
struct ipi_msg_t *ipi_msg, int id)
{
if (dsp->dsp_mem[id].substream == NULL) {
pr_info("%s = substream == NULL\n", __func__);
goto DSP_IRQ_HANDLER_ERR;
}
if (!snd_pcm_running(dsp->dsp_mem[id].substream)) {
pr_info("%s = state[%d]\n", __func__,
dsp->dsp_mem[id].substream->runtime->status->state);
goto DSP_IRQ_HANDLER_ERR;
}
/* notify subsream */
snd_pcm_period_elapsed(dsp->dsp_mem[id].substream);
DSP_IRQ_HANDLER_ERR:
return;
}
static bool mtk_dsp_check_exception(struct mtk_base_dsp *dsp,
struct ipi_msg_t *ipi_msg, int id)
{
const char *task_name = get_str_by_dsp_dai_id(id);
if (id < 0 || id >= AUDIO_TASK_DAI_NUM)
return false;
if (!dsp->dsp_mem[id].substream) {
pr_info_ratelimited("%s() %s substream NULL\n",
__func__, task_name);
return false;
}
if (!snd_pcm_running(dsp->dsp_mem[id].substream)) {
pr_info_ratelimited("%s() %s state[%d]\n",
__func__, task_name,
dsp->dsp_mem[id].substream->runtime->status->state);
return false;
}
/* adsp reset message */
if (ipi_msg && ipi_msg->param2 == ADSP_DL_CONSUME_RESET) {
pr_info("%s() %s adsp reset\n", __func__, task_name);
RingBuf_Reset(&dsp->dsp_mem[id].ring_buf);
snd_pcm_period_elapsed(dsp->dsp_mem[id].substream);
return true;
}
/* adsp underflow message */
if (ipi_msg && ipi_msg->param2 == ADSP_DL_CONSUME_UNDERFLOW) {
pr_info("%s() %s adsp underflow\n", __func__, task_name);
dsp->dsp_mem[id].underflowed = true;
snd_pcm_period_elapsed(dsp->dsp_mem[id].substream);
return true;
}
return false;
}
static void mtk_dsp_dl_consume_handler(struct mtk_base_dsp *dsp,
struct ipi_msg_t *ipi_msg, int id)
{
void *ipi_audio_buf;
struct mtk_base_dsp_mem *dsp_mem = &dsp->dsp_mem[id];
spinlock_t *ringbuf_lock = &dsp->dsp_mem[id].ringbuf_lock;
struct snd_pcm_substream *substream;
unsigned long flags = 0;
if (!dsp->dsp_mem[id].substream) {
return;
}
if (!snd_pcm_running(dsp->dsp_mem[id].substream)) {
return;
}
substream = dsp->dsp_mem[id].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 *)&dsp_mem->adsp_work_buf, ipi_msg->payload,
sizeof(struct audio_hw_buffer));
} else {
ipi_audio_buf = (void *)dsp_mem->msg_dtoa_share_buf.va_addr;
memcpy((void *)&dsp_mem->adsp_work_buf, (void *)ipi_audio_buf,
sizeof(struct audio_hw_buffer));
}
spin_lock_irqsave(ringbuf_lock, flags);
dsp->dsp_mem[id].adsp_buf.aud_buffer.buf_bridge.pRead =
dsp->dsp_mem[id].adsp_work_buf.aud_buffer.buf_bridge.pRead;
#ifdef DEBUG_VERBOSE_IRQ
dump_rbuf_s("dl_consume before sync", &dsp->dsp_mem[id].ring_buf);
dump_rbuf_bridge_s("dl_consume before sync",
&dsp->dsp_mem[id].adsp_buf.aud_buffer.buf_bridge);
#endif
sync_ringbuf_readidx(
&dsp->dsp_mem[id].ring_buf,
&dsp->dsp_mem[id].adsp_buf.aud_buffer.buf_bridge);
spin_unlock_irqrestore(ringbuf_lock, flags);
#ifdef DEBUG_VERBOSE_IRQ
dump_rbuf_s("dl_consume after sync", &dsp->dsp_mem[id].ring_buf);
#endif
/* notify subsream */
snd_pcm_period_elapsed(dsp->dsp_mem[id].substream);
}
static void mtk_dsp_ul_handler(struct mtk_base_dsp *dsp,
struct ipi_msg_t *ipi_msg, int id)
{
struct mtk_base_dsp_mem *dsp_mem = &dsp->dsp_mem[id];
void *ipi_audio_buf;
unsigned long flags;
spinlock_t *ringbuf_lock = &dsp->dsp_mem[id].ringbuf_lock;
if (!dsp->dsp_mem[id].substream) {
pr_info("%s substream NULL\n", __func__);
return;
}
if (!snd_pcm_running(dsp->dsp_mem[id].substream)) {
pr_info("%s = state[%d]\n", __func__,
dsp->dsp_mem[id].substream->runtime->status->state);
goto DSP_IRQ_HANDLER_ERR;
}
if (dsp->dsp_mem[id].substream->runtime->status->state
!= SNDRV_PCM_STATE_RUNNING) {
pr_info("%s = state[%d]\n", __func__,
dsp->dsp_mem[id].substream->runtime->status->state);
goto DSP_IRQ_HANDLER_ERR;
}
if (ipi_msg && ipi_msg->param2 == ADSP_UL_READ_RESET) {
spin_lock_irqsave(ringbuf_lock, flags);
RingBuf_Reset(&dsp->dsp_mem[id].ring_buf);
/* set buf size full to trigger pcm_read */
dsp->dsp_mem[id].ring_buf.datacount = dsp->dsp_mem[id].ring_buf.bufLen;
spin_unlock_irqrestore(ringbuf_lock, flags);
pr_info("%s reset UL\n", __func__);
snd_pcm_period_elapsed(dsp->dsp_mem[id].substream);
goto DSP_IRQ_HANDLER_ERR;
}
/* upadte for write index*/
ipi_audio_buf = (void *)dsp_mem->msg_dtoa_share_buf.va_addr;
memcpy((void *)&dsp_mem->adsp_work_buf, (void *)ipi_audio_buf,
sizeof(struct audio_hw_buffer));
dsp_mem->adsp_buf.aud_buffer.buf_bridge.pWrite =
(dsp_mem->adsp_work_buf.aud_buffer.buf_bridge.pWrite);
#ifdef DEBUG_VERBOSE
dump_rbuf_bridge_s(__func__,
&dsp_mem->adsp_work_buf.aud_buffer.buf_bridge);
dump_rbuf_bridge_s(__func__,
&dsp_mem->adsp_buf.aud_buffer.buf_bridge);
#endif
spin_lock_irqsave(ringbuf_lock, flags);
sync_ringbuf_writeidx(&dsp_mem->ring_buf,
&dsp_mem->adsp_buf.aud_buffer.buf_bridge);
spin_unlock_irqrestore(ringbuf_lock, flags);
#ifdef DEBUG_VERBOSE
dump_rbuf_s(__func__, &dsp_mem->ring_buf);
#endif
/* notify subsream */
snd_pcm_period_elapsed(dsp->dsp_mem[id].substream);
DSP_IRQ_HANDLER_ERR:
return;
}
void mtk_dsp_handler(struct mtk_base_dsp *dsp,
struct ipi_msg_t *ipi_msg)
{
int id = 0;
if (!dsp) {
pr_info("%s dsp NULL", __func__);
return;
}
if (ipi_msg == NULL) {
pr_info("%s ipi_msg == NULL\n", __func__);
return;
}
id = get_dspdaiid_by_dspscene(ipi_msg->task_scene);
if (id < 0)
return;
if (!is_audio_task_dsp_ready(ipi_msg->task_scene)) {
pr_info("%s(), is_adsp_ready send false\n", __func__);
return;
}
switch (ipi_msg->msg_id) {
case AUDIO_DSP_TASK_IRQDL:
mtk_dsp_dl_handler(dsp, ipi_msg, id);
break;
case AUDIO_DSP_TASK_IRQUL:
mtk_dsp_ul_handler(dsp, ipi_msg, id);
break;
case AUDIO_DSP_TASK_DL_CONSUME_DATA:
/* check exceptions in consume message */
if (mtk_dsp_check_exception(dsp, ipi_msg, id))
break;
/* Handle consume message for the platforms
* which not support audio IRQ.
*/
mtk_dsp_dl_consume_handler(dsp, ipi_msg, id);
break;
default:
break;
}
}
static int mtk_dsp_pcm_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
int ret = 0;
int retry_flag = false;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct mtk_base_dsp *dsp = snd_soc_component_get_drvdata(component);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int id = cpu_dai->id;
int dsp_feature_id = get_featureid_by_dsp_daiid(id);
const char *task_name = get_str_by_dsp_dai_id(id);
memcpy((void *)(&(runtime->hw)), (void *)dsp->mtk_dsp_hardware,
sizeof(struct snd_pcm_hardware));
ret = mtk_dsp_register_feature(dsp_feature_id);
if (ret) {
pr_info("%s(), %s register feature fail", __func__, task_name);
return -1;
}
RETRY:
/* send to task with open information */
ret = mtk_scp_ipi_send(get_dspscene_by_dspdaiid(id), AUDIO_IPI_MSG_ONLY,
AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_OPEN,
0, 0, NULL);
if (ret < 0) {
if (!retry_flag) {
wait_dsp_ready();
retry_flag = true;
goto RETRY;
} else {
mtk_dsp_deregister_feature(dsp_feature_id);
goto END;
}
}
/* set the wait_for_avail to 2 sec*/
substream->wait_time = msecs_to_jiffies(2 * 1000);
dsp->dsp_mem[id].substream = substream;
dsp->dsp_mem[id].underflowed = 0;
END:
pr_info("%s(), %s(%d) ret[%d] retry[%d]\n", __func__,
task_name, id, ret, retry_flag);
return ret;
}
static int mtk_dsp_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 mtk_base_dsp *dsp = snd_soc_component_get_drvdata(component);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int id = cpu_dai->id;
int dsp_feature_id = get_featureid_by_dsp_daiid(id);
const char *task_name = get_str_by_dsp_dai_id(id);
pr_info("%s() %s\n", __func__, task_name);
/* send to task with close information */
ret = mtk_scp_ipi_send(get_dspscene_by_dspdaiid(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);
mtk_dsp_deregister_feature(dsp_feature_id);
dsp->dsp_mem[id].substream = NULL;
return ret;
}
/*
* allocate dsp copy
* @param: @action true: allocate buffer false: free buffer
*/
static int mtk_dsp_manage_copybuf(bool action,
struct mtk_base_dsp_mem *dsp_mep,
unsigned int size)
{
if (dsp_mep == NULL)
return -ENOMEM;
if (action == true) {
if (size == 0)
return -EINVAL;
if (dsp_mep->dsp_copy_buf != NULL)
return -EINVAL;
dsp_mep->dsp_copy_buf = kzalloc(size, GFP_KERNEL);
if (!dsp_mep->dsp_copy_buf)
return -EINVAL;
} else {
if (dsp_mep->dsp_copy_buf == NULL)
return -EINVAL;
kfree(dsp_mep->dsp_copy_buf);
dsp_mep->dsp_copy_buf = NULL;
}
return 0;
}
static int mtk_dsp_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_base_dsp *dsp = snd_soc_component_get_drvdata(component);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int id = cpu_dai->id;
struct mtk_base_dsp_mem *dsp_mem = &dsp->dsp_mem[id];
int ret = 0;
pr_info("%s(), %s task_id: %d\n", __func__, cpu_dai->name, id);
reset_audiobuffer_hw(&dsp_mem->adsp_buf);
reset_audiobuffer_hw(&dsp_mem->audio_afepcm_buf);
reset_audiobuffer_hw(&dsp_mem->adsp_work_buf);
RingBuf_Reset(&dsp_mem->ring_buf);
dsp->request_dram_resource(dsp->dev);
/* gen pool related */
dsp_mem->gen_pool_buffer = mtk_get_adsp_dram_gen_pool(AUDIO_DSP_AFE_SHARE_MEM_ID);
if (!dsp_mem->gen_pool_buffer)
goto error;
/* if already allocate , free it.*/
if (substream->dma_buffer.area) {
mtk_adsp_genpool_free_sharemem_ring(dsp_mem, id);
release_snd_dmabuffer(&substream->dma_buffer);
}
/* allocate ring buffer with share memory */
ret = mtk_adsp_genpool_allocate_sharemem_ring(dsp_mem, params_buffer_bytes(params), id);
if (ret < 0) {
pr_warn("%s err\n", __func__);
return ret;
}
/* allocate copy buffer */
ret = mtk_dsp_manage_copybuf(true, &dsp->dsp_mem[id], params_buffer_bytes(params));
if (ret) {
pr_info("%s ret = %d\n", __func__, ret);
return -1;
}
#ifdef DEBUG_VERBOSE
dump_audio_dsp_dram(&dsp_mem->msg_atod_share_buf);
dump_audio_dsp_dram(&dsp_mem->msg_dtoa_share_buf);
dump_audio_dsp_dram(&dsp_mem->dsp_ring_share_buf);
dump_rbuf_s(__func__, &dsp_mem->ring_buf);
#endif
ret = dsp_dram_to_snd_dmabuffer(&dsp_mem->dsp_ring_share_buf, &substream->dma_buffer);
if (ret < 0)
goto error;
ret = set_audiobuffer_hw(&dsp_mem->adsp_buf, BUFFER_TYPE_SHARE_MEM);
if (ret < 0)
goto error;
ret = set_audiobuffer_memorytype(&dsp_mem->adsp_buf, MEMORY_AUDIO_DRAM);
if (ret < 0)
goto error;
ret = set_audiobuffer_attribute(&dsp_mem->adsp_buf, substream, params,
afe_get_pcmdir(substream->stream, dsp_mem->adsp_buf));
if (ret < 0)
goto error;
memcpy(&dsp_mem->adsp_work_buf, &dsp_mem->adsp_buf, sizeof(struct audio_hw_buffer));
/* send to task with hw_param information , buffer and pcm attribute */
memcpy(dsp_mem->msg_atod_share_buf.vir_addr,
&dsp_mem->adsp_buf, sizeof(struct audio_hw_buffer));
mtk_scp_ipi_send(get_dspscene_by_dspdaiid(id), AUDIO_IPI_PAYLOAD,
AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_HWPARAM,
sizeof(dsp_mem->msg_atod_share_buf.phy_addr),
0,
(char *)&dsp_mem->msg_atod_share_buf.phy_addr);
return ret;
error:
pr_err("%s err\n", __func__);
return -1;
}
static int mtk_dsp_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_base_dsp *dsp = snd_soc_component_get_drvdata(component);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int id = cpu_dai->id;
struct mtk_base_dsp_mem *dsp_mem = &dsp->dsp_mem[id];
/* send to task with free status */
ret = mtk_scp_ipi_send(get_dspscene_by_dspdaiid(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);
mtk_adsp_genpool_free_sharemem_ring(dsp_mem, id);
release_snd_dmabuffer(&substream->dma_buffer);
/* free copy buffer */
ret = mtk_dsp_manage_copybuf(false, &dsp->dsp_mem[id], 0);
if (ret)
pr_info("%s ret = %d\n", __func__, ret);
/* release dsp memory */
reset_audiobuffer_hw(&dsp_mem->adsp_buf);
dsp->release_dram_resource(dsp->dev);
return 0;
}
static int mtk_dsp_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 mtk_base_dsp *dsp = snd_soc_component_get_drvdata(component);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int id = cpu_dai->id;
struct mtk_base_dsp_mem *dsp_mem = &dsp->dsp_mem[id];
struct audio_hw_buffer *adsp_buf = &dsp_mem->adsp_buf;
const char *task_name = get_str_by_dsp_dai_id(id);
/* 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(adsp_buf);
RingBuf_Reset(&dsp_mem->ring_buf);
RingBuf_Bridge_Reset(&adsp_buf->aud_buffer.buf_bridge);
RingBuf_Bridge_Reset(&dsp_mem->adsp_work_buf.aud_buffer.buf_bridge);
ret = set_audiobuffer_threshold(adsp_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,
adsp_buf->aud_buffer.start_threshold,
adsp_buf->aud_buffer.stop_threshold,
adsp_buf->aud_buffer.period_size,
adsp_buf->aud_buffer.period_count);
/* send audio_hw_buffer to SCP side */
memcpy(dsp_mem->msg_atod_share_buf.vir_addr, adsp_buf, sizeof(struct audio_hw_buffer));
/* send to task with prepare status */
mtk_scp_ipi_send(get_dspscene_by_dspdaiid(id), AUDIO_IPI_PAYLOAD,
AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_PREPARE,
sizeof(dsp_mem->msg_atod_share_buf.phy_addr),
0,
(char *)&dsp_mem->msg_atod_share_buf.phy_addr);
return ret;
}
static int mtk_dsp_start(struct snd_pcm_substream *substream,
struct mtk_base_dsp *dsp)
{
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 mtk_base_dsp_mem *dsp_mem = &dsp->dsp_mem[id];
const char *task_name = get_str_by_dsp_dai_id(id);
dev_info(dsp->dev, "%s() %s %s\n",
__func__, task_name,
dsp_mem->underflowed ? "just underflow" : "");
dsp_mem->underflowed = 0;
ret = mtk_scp_ipi_send(get_dspscene_by_dspdaiid(id), AUDIO_IPI_MSG_ONLY,
AUDIO_IPI_MSG_DIRECT_SEND, AUDIO_DSP_TASK_START,
1, 0, NULL);
return ret;
}
static int mtk_dsp_stop(struct snd_pcm_substream *substream,
struct mtk_base_dsp *dsp)
{
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.
*/
ret = mtk_scp_ipi_send(get_dspscene_by_dspdaiid(id), AUDIO_IPI_MSG_ONLY,
AUDIO_IPI_MSG_DIRECT_SEND, AUDIO_DSP_TASK_STOP,
1, 0, NULL);
return ret;
}
static int mtk_dsp_pcm_hw_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct mtk_base_dsp *dsp = snd_soc_component_get_drvdata(component);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
return mtk_dsp_start(substream, dsp);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
return mtk_dsp_stop(substream, dsp);
}
return -EINVAL;
}
static int mtk_dsp_pcm_copy_dl(struct snd_pcm_substream *substream,
int copy_size,
struct mtk_base_dsp_mem *dsp_mem,
void __user *buf)
{
int ret = 0, availsize = 0, 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 = &dsp_mem->ring_buf;
struct ringbuf_bridge *buf_bridge =
&(dsp_mem->adsp_buf.aud_buffer.buf_bridge);
unsigned long flags = 0;
void *dsp_copy_buf = dsp_mem->dsp_copy_buf;
spinlock_t *ringbuf_lock = &dsp_mem->ringbuf_lock;
const char *task_name = get_str_by_dsp_dai_id(id);
#ifdef DEBUG_VERBOSE
dump_rbuf_s(__func__, &dsp_mem->ring_buf);
dump_rbuf_bridge_s(__func__,
&dsp_mem->adsp_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;
}
if (dsp_copy_buf == NULL)
return -ENOMEM;
Ringbuf_Check(ringbuf);
Ringbuf_Bridge_Check(
&dsp_mem->adsp_buf.aud_buffer.buf_bridge);
/* copy user space memory */
ret = copy_from_user(dsp_copy_buf, buf, copy_size);
if (ret) {
pr_info("%s copy_from_user fail line %d\n", __func__, __LINE__);
return -1;
}
spin_lock_irqsave(ringbuf_lock, flags);
availsize = RingBuf_getFreeSpace(ringbuf);
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", &dsp_mem->ring_buf);
dump_rbuf_bridge_s("check dlcopy",
&dsp_mem->adsp_buf.aud_buffer.buf_bridge);
spin_unlock_irqrestore(ringbuf_lock, flags);
return -1;
}
RingBuf_copyFromLinear(ringbuf, dsp_copy_buf, copy_size);
RingBuf_Bridge_update_writeptr(buf_bridge, copy_size);
spin_unlock_irqrestore(ringbuf_lock, flags);
/* send audio_hw_buffer to SCP side*/
ipi_audio_buf = (void *)dsp_mem->msg_atod_share_buf.va_addr;
memcpy((void *)ipi_audio_buf, (void *)&dsp_mem->adsp_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_ipi_send(
get_dspscene_by_dspdaiid(id), AUDIO_IPI_PAYLOAD,
ack_type, AUDIO_DSP_TASK_DLCOPY,
sizeof(dsp_mem->msg_atod_share_buf.phy_addr),
0,
(char *)&dsp_mem->msg_atod_share_buf.phy_addr);
#ifdef DEBUG_VERBOSE
dump_rbuf_s(__func__, ringbuf);
#endif
return ret;
}
static int mtk_dsp_pcm_copy_ul(struct snd_pcm_substream *substream,
int copy_size,
struct mtk_base_dsp_mem *dsp_mem,
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 = &(dsp_mem->ring_buf);
unsigned long flags = 0;
spinlock_t *ringbuf_lock = &dsp_mem->ringbuf_lock;
#ifdef DEBUG_VERBOSE
dump_rbuf_s(__func__, &dsp_mem->ring_buf);
dump_rbuf_bridge_s(__func__,
&dsp_mem->adsp_buf.aud_buffer.buf_bridge);
#endif
Ringbuf_Check(&dsp_mem->ring_buf);
Ringbuf_Bridge_Check(
&dsp_mem->adsp_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, &dsp_mem->ring_buf, copy_size);
spin_lock_irqsave(ringbuf_lock, flags);
sync_bridge_ringbuf_readidx(&dsp_mem->adsp_buf.aud_buffer.buf_bridge,
&dsp_mem->ring_buf);
spin_unlock_irqrestore(ringbuf_lock, flags);
ipi_audio_buf = (void *)dsp_mem->msg_atod_share_buf.va_addr;
memcpy((void *)ipi_audio_buf, (void *)&dsp_mem->adsp_buf,
sizeof(struct audio_hw_buffer));
ret = mtk_scp_ipi_send(
get_dspscene_by_dspdaiid(id), AUDIO_IPI_PAYLOAD,
AUDIO_IPI_MSG_NEED_ACK, AUDIO_DSP_TASK_ULCOPY,
sizeof(dsp_mem->msg_atod_share_buf.phy_addr),
0,
(char *)&dsp_mem->msg_atod_share_buf.phy_addr);
#ifdef DEBUG_VERBOSE
dump_rbuf_bridge_s("1 mtk_dsp_ul_handler",
&dsp_mem->adsp_buf.aud_buffer.buf_bridge);
dump_rbuf_s("1 mtk_dsp_ul_handler",
&dsp_mem->ring_buf);
#endif
return ret;
}
static int mtk_dsp_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 mtk_base_dsp *dsp = snd_soc_component_get_drvdata(component);
struct mtk_base_dsp_mem *dsp_mem = &dsp->dsp_mem[id];
int ret = 0;
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(get_dspscene_by_dspdaiid(id)) == false) {
pr_info("%s(), dsp not ready", __func__);
return -1;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
ret = mtk_dsp_pcm_copy_dl(substream, bytes, dsp_mem, buf);
else
ret = mtk_dsp_pcm_copy_ul(substream, bytes, dsp_mem, buf);
return ret;
}
static void audio_dsp_tasklet(struct mtk_base_dsp *dsp, unsigned int core_id)
{
unsigned long task_value;
int dsp_scene, task_id, loop_count;
unsigned long *pdtoa;
/* using semaphore to sync ap <=> adsp */
if (get_adsp_semaphore(SEMA_AUDIO))
pr_info("%s get semaphore fail\n", __func__);
pdtoa = (unsigned long *)
&dsp->core_share_mem.ap_adsp_core_mem[core_id]->dtoa_flag;
loop_count = DSP_IRQ_LOOP_COUNT;
/* rmb() ensure pdtoa read correct dram data */
rmb();
do {
/* valid bits */
task_value = fls(*pdtoa);
/* rmb() ensure task_value read dram(pdtoa) after fls */
rmb();
if (task_value) {
dsp_scene = task_value - 1;
task_id = get_dspdaiid_by_dspscene(dsp_scene);
#ifdef DEBUG_VERBOSE_IRQ
pr_info("+%s flag[%llx] task[%s] task_value[%lu] dsp_scene[%d]\n",
__func__, *pdtoa,
get_str_by_dsp_dai_id(task_id),
task_value, dsp_scene);
#endif
*pdtoa = clr_bit(dsp_scene, pdtoa);
/* wmb() ensure write data to dram(pdtoa) after clr_bit */
wmb();
#ifdef DEBUG_VERBOSE_IRQ
pr_info("-%s flag[%llx] task_id[%d] task_value[%lu]\n",
__func__, *pdtoa, task_id, task_value);
#endif
if (task_id >= 0 && task_id < AUDIO_TASK_DAI_NUM) {
if (!dsp->dsp_mem[task_id].underflowed)
mtk_dsp_dl_consume_handler(dsp, NULL, task_id);
}
}
loop_count--;
} while (*pdtoa && task_value && loop_count > 0);
release_adsp_semaphore(SEMA_AUDIO);
return;
}
static void audio_dsp_tasklet_core0(struct tasklet_struct *t)
{
struct mtk_base_dsp *dsp = from_tasklet(dsp, t, dsp_tasklet[ADSP_A_ID]);
audio_dsp_tasklet(dsp, ADSP_A_ID);
}
static void audio_dsp_tasklet_core1(struct tasklet_struct *t)
{
struct mtk_base_dsp *dsp = from_tasklet(dsp, t, dsp_tasklet[ADSP_B_ID]);
audio_dsp_tasklet(dsp, ADSP_B_ID);
}
void audio_irq_handler(int irq, void *data, int core_id)
{
struct mtk_base_dsp *dsp = (struct mtk_base_dsp *)data;
if (!dsp) {
pr_info("%s dsp[%p]\n", __func__, dsp);
goto IRQ_ERROR;
}
if (core_id >= get_adsp_core_total() || core_id < 0) {
pr_info("%s core_id[%d]\n", __func__, core_id);
goto IRQ_ERROR;
}
if (!dsp->core_share_mem.ap_adsp_core_mem[core_id]) {
pr_info("%s core_id [%d] ap_adsp_core_mem[%p]\n",
__func__, core_id,
dsp->core_share_mem.ap_adsp_core_mem[core_id]);
goto IRQ_ERROR;
}
audio_dsp_tasklet(dsp, core_id);
return;
IRQ_ERROR:
pr_info("IRQ_ERROR irq[%d] data[%p] core_id[%d] dsp[%p]\n",
irq, data, core_id, dsp);
}
#ifdef CFG_RECOVERY_SUPPORT
static int audio_send_reset_event(void)
{
int ret = 0, i;
for (i = 0; i < TASK_SCENE_SIZE; i++) {
if ((i == TASK_SCENE_DEEPBUFFER) ||
(i == TASK_SCENE_VOIP) ||
(i == TASK_SCENE_PRIMARY) ||
(i == TASK_SCENE_FAST) ||
(i == TASK_SCENE_CAPTURE_UL1) ||
(i == TASK_SCENE_CAPTURE_RAW)) {
ret = mtk_scp_ipi_send(i, AUDIO_IPI_MSG_ONLY,
AUDIO_IPI_MSG_BYPASS_ACK, AUDIO_DSP_TASK_RESET,
ADSP_EVENT_READY, 0, NULL);
pr_info("%s scene = %d\n", __func__, i);
}
}
return ret;
}
static int audio_event_receive(struct notifier_block *this, unsigned long event,
void *ptr)
{
int ret = 0;
switch (event) {
case ADSP_EVENT_STOP:
pr_info("%s event[%lu]\n", __func__, event);
break;
case ADSP_EVENT_READY: {
audio_send_reset_event();
pr_info("%s event[%lu]\n", __func__, event);
break;
}
default:
pr_info("%s event[%lu]\n", __func__, event);
}
return ret;
}
static struct notifier_block adsp_audio_notifier = {
.notifier_call = audio_event_receive,
.priority = PRIMARY_FEATURE_PRI,
};
#endif
static int mtk_dsp_probe(struct snd_soc_component *component)
{
int ret = 0, id = 0;
struct mtk_base_dsp *dsp = snd_soc_component_get_drvdata(component);
pr_info("%s dsp = %p\n", __func__, dsp);
adsp_audio_wakelock = aud_wake_lock_init(NULL, "adsp_audio_wakelock");
if (adsp_audio_wakelock == NULL)
pr_info("%s init adsp_audio_wakelock error\n", __func__);
ret = snd_soc_add_component_controls(component,
dsp_platform_kcontrols,
ARRAY_SIZE(dsp_platform_kcontrols));
if (ret)
pr_info("%s add_component err ret = %d\n", __func__, ret);
for (id = 0; id < AUDIO_TASK_DAI_NUM; id++) {
spin_lock_init(&dsp->dsp_mem[id].ringbuf_lock);
ret = audio_task_register_callback(get_dspscene_by_dspdaiid(id),
mtk_dsp_pcm_ipi_recv);
if (ret < 0)
return ret;
}
for (id = 0; id < get_adsp_core_total(); id++) {
if (adsp_irq_registration(id, ADSP_IRQ_AUDIO_ID, audio_irq_handler, dsp) < 0)
pr_info("%s, ADSP_IRQ_AUDIO not supported\n");
}
tasklet_setup(&dsp->dsp_tasklet[ADSP_A_ID], audio_dsp_tasklet_core0);
tasklet_setup(&dsp->dsp_tasklet[ADSP_B_ID], audio_dsp_tasklet_core1);
#ifdef CFG_RECOVERY_SUPPORT
adsp_register_notify(&adsp_audio_notifier);
#endif
return ret;
}
const struct snd_soc_component_driver mtk_dsp_pcm_platform = {
.name = AFE_DSP_NAME,
.probe = mtk_dsp_probe,
.open = mtk_dsp_pcm_open,
.close = mtk_dsp_pcm_close,
.hw_params = mtk_dsp_pcm_hw_params,
.hw_free = mtk_dsp_pcm_hw_free,
.prepare = mtk_dsp_pcm_hw_prepare,
.trigger = mtk_dsp_pcm_hw_trigger,
.pointer = mtk_dsphw_pcm_pointer,
.copy_user = mtk_dsp_pcm_copy,
};
EXPORT_SYMBOL_GPL(mtk_dsp_pcm_platform);
MODULE_DESCRIPTION("Mediatek dsp platform driver");
MODULE_AUTHOR("chipeng Chang <chipeng.chang@mediatek.com>");
MODULE_LICENSE("GPL v2");