kernel_samsung_a34x-permissive/sound/soc/mediatek/common_int/mtk-soc-pcm-dl2.c
2024-04-28 15:49:01 +02:00

1069 lines
29 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Michael Hsiao <michael.hsiao@mediatek.com>
*/
/*******************************************************************************
*
* Filename:
* ---------
* mt_soc_pcm_dl2.c
*
* Project:
* --------
* Audio Driver Kernel Function
*
* Description:
* ------------
* Audio dl2 data1 playback
*
* Author:
* -------
* Chipeng Chang
*
*------------------------------------------------------------------------------
*
******************************************************************************
*/
/*****************************************************************************
* C O M P I L E R F L A G S
*****************************************************************************/
/*****************************************************************************
* E X T E R N A L R E F E R E N C E S
*****************************************************************************/
#include "mtk-auddrv-afe.h"
#include "mtk-auddrv-ana.h"
#include "mtk-auddrv-clk.h"
#include "mtk-auddrv-common.h"
#include "mtk-auddrv-def.h"
#include "mtk-auddrv-kernel.h"
#include "mtk-soc-afe-control.h"
#include "mtk-soc-pcm-common.h"
#include "mtk-soc-pcm-platform.h"
#include <linux/ftrace.h>
static int fast_dl_hdoutput;
static struct afe_mem_control_t *pMemControl;
static struct snd_dma_buffer *Dl2_Playback_dma_buf;
static unsigned int UnderflowTime;
static bool StartCheckTime;
static unsigned long PrevTime;
static unsigned long NowTime;
#ifdef AUDIO_DL2_ISR_COPY_SUPPORT
static const int ISRCopyMaxSize = 256 * 2 * 4; /* 256 frames, stereo, 32bit */
static struct afe_dl_isr_copy_t ISRCopyBuffer = {0};
#endif
const char * const fast_dl_hd_output[] = {"Off", "On"};
static const struct soc_enum fast_dl_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(fast_dl_hd_output), fast_dl_hd_output),
};
static int fast_dl_hdoutput_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
pr_debug("%s() = %d\n", __func__, fast_dl_hdoutput);
ucontrol->value.integer.value[0] = fast_dl_hdoutput;
return 0;
}
static int fast_dl_hdoutput_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
/* pr_debug("%s()\n", __func__); */
if (ucontrol->value.enumerated.item[0] >
ARRAY_SIZE(fast_dl_hd_output)) {
pr_warn("%s(), return -EINVAL\n", __func__);
return -EINVAL;
}
fast_dl_hdoutput = ucontrol->value.integer.value[0];
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_MEM_HDMI) == true) {
pr_debug("return HDMI enabled\n");
return 0;
}
return 0;
}
static int dataTransfer(void *dest, const void *src, uint32_t size);
enum DEBUG_DL2 {
DEBUG_DL2_LOG = 1,
DEBUG_DL2_LOG_DETECT_DTAT = 2,
DEBUG_DL2_AEE_UNDERFLOW = 4,
DEBUG_DL2_AEE_OTHERS = 8
};
#define PRINT_DEBUG_LOG(format, args...) \
{ \
if (unlikely(get_LowLatencyDebug() & DEBUG_DL2_LOG)) \
pr_debug(format, ##args); \
}
/*
* function implementation
*/
/* void StartAudioPcmHardware(void); */
/* void StopAudioPcmHardware(void); */
static int mtk_soc_dl2_probe(struct platform_device *pdev);
static int mtk_soc_pcm_dl2_close(struct snd_pcm_substream *substream);
static int mtk_asoc_dl2_component_probe(struct snd_soc_component *component);
static bool mPrepareDone;
#define USE_RATE (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000)
#define USE_RATE_MIN 8000
#define USE_RATE_MAX 192000
#define USE_CHANNELS_MIN 1
#define USE_CHANNELS_MAX 2
#define USE_PERIODS_MIN 512
#define USE_PERIODS_MAX 8192
static const struct snd_kcontrol_new fast_dl_controls[] = {
SOC_ENUM_EXT("fast_dl_hd_Switch", fast_dl_enum[0],
fast_dl_hdoutput_get, fast_dl_hdoutput_set),
};
static struct snd_pcm_hardware mtk_pcm_dl2_hardware = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SND_SOC_ADV_MT_FMTS,
.rates = SOC_HIGH_USE_RATE,
.rate_min = SOC_HIGH_USE_RATE_MIN,
.rate_max = SOC_HIGH_USE_RATE_MAX,
.channels_min = SOC_NORMAL_USE_CHANNELS_MIN,
.channels_max = SOC_NORMAL_USE_CHANNELS_MAX,
.buffer_bytes_max = Dl2_MAX_BUFFER_SIZE,
.period_bytes_max = Dl2_MAX_PERIOD_SIZE,
.periods_min = SOC_NORMAL_USE_PERIODS_MIN,
.periods_max = SOC_NORMAL_USE_PERIODS_MAX,
.fifo_size = 0,
};
static int mtk_pcm_dl2_stop(struct snd_pcm_substream *substream)
{
PRINT_DEBUG_LOG("%s\n", __func__);
StartCheckTime = false;
if (unlikely(get_LowLatencyDebug())) {
struct afe_block_t *Afe_Block = &pMemControl->rBlock;
if (Afe_Block->u4DataRemained < 0) {
pr_warn("%s, dl2 underflow\n", __func__);
if (get_LowLatencyDebug() & DEBUG_DL2_AEE_UNDERFLOW)
AUDIO_AEE("mtk_pcm_dl2_stop - dl2 underflow");
}
}
SetMemoryPathEnable(Soc_Aud_Digital_Block_MEM_DL2, false);
irq_remove_substream_user(
substream, irq_request_number(Soc_Aud_Digital_Block_MEM_DL2));
/* here start digital part */
SetIntfConnection(Soc_Aud_InterCon_DisConnect,
Soc_Aud_AFE_IO_Block_MEM_DL2,
Soc_Aud_AFE_IO_Block_I2S1_DAC);
SetIntfConnection(Soc_Aud_InterCon_DisConnect,
Soc_Aud_AFE_IO_Block_MEM_DL2,
Soc_Aud_AFE_IO_Block_I2S1_DAC_2);
SetIntfConnection(Soc_Aud_InterCon_DisConnect,
Soc_Aud_AFE_IO_Block_MEM_DL2,
Soc_Aud_AFE_IO_Block_I2S3);
ClearMemBlock(Soc_Aud_Digital_Block_MEM_DL2);
return 0;
}
static snd_pcm_uframes_t
mtk_pcm_dl2_pointer(struct snd_pcm_substream *substream)
{
kal_int32 HW_memory_index = 0;
kal_int32 HW_Cur_ReadIdx = 0;
kal_uint32 Frameidx = 0;
kal_int32 Afe_consumed_bytes = 0;
struct afe_block_t *Afe_Block = &pMemControl->rBlock;
unsigned long flags;
/* struct snd_pcm_runtime *runtime = substream->runtime; */
#ifdef DL2_DEBUG_LOG
pr_debug(" %s Afe_Block->u4DMAReadIdx = 0x%x\n", __func__,
Afe_Block->u4DMAReadIdx);
#endif
spin_lock_irqsave(&pMemControl->substream_lock, flags);
/* get total bytes to copy */
/* Frameidx = audio_bytes_to_frame(substream , Afe_Block->u4DMAReadIdx);
*/
/* return Frameidx; */
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_MEM_DL2) == true) {
HW_Cur_ReadIdx = Afe_Get_Reg(AFE_DL2_CUR);
if (HW_Cur_ReadIdx == 0) {
pr_warn("[Auddrv] HW_Cur_ReadIdx ==0\n");
HW_Cur_ReadIdx = Afe_Block->pucPhysBufAddr;
}
HW_memory_index = (HW_Cur_ReadIdx - Afe_Block->pucPhysBufAddr);
if (HW_memory_index >= Afe_Block->u4DMAReadIdx) {
Afe_consumed_bytes =
HW_memory_index - Afe_Block->u4DMAReadIdx;
} else {
Afe_consumed_bytes = Afe_Block->u4BufferSize +
HW_memory_index -
Afe_Block->u4DMAReadIdx;
}
#ifdef AUDIO_64BYTE_ALIGN /* no need to do 64byte align */
Afe_consumed_bytes = word_size_align(Afe_consumed_bytes);
#endif
PRINT_DEBUG_LOG(
"+%s DataRemained:%d, consumed_bytes:%d, HW_memory_index = %d, ReadIdx:%d, WriteIdx:%d\n",
__func__, Afe_Block->u4DataRemained, Afe_consumed_bytes,
HW_memory_index, Afe_Block->u4DMAReadIdx,
Afe_Block->u4WriteIdx);
Afe_Block->u4DataRemained -= Afe_consumed_bytes;
Afe_Block->u4DMAReadIdx += Afe_consumed_bytes;
Afe_Block->u4DMAReadIdx %= Afe_Block->u4BufferSize;
PRINT_DEBUG_LOG(
"-%s DataRemained:%d, consumed_bytes:%d, HW_memory_index = %d, ReadIdx:%d, WriteIdx:%d\n",
__func__, Afe_Block->u4DataRemained, Afe_consumed_bytes,
HW_memory_index, Afe_Block->u4DMAReadIdx,
Afe_Block->u4WriteIdx);
if (Afe_Block->u4DataRemained < 0)
PRINT_DEBUG_LOG("[AudioWarn] u4DataRemained=0x%x\n",
Afe_Block->u4DataRemained);
Frameidx = audio_bytes_to_frame(substream,
Afe_Block->u4DMAReadIdx);
} else {
Frameidx = audio_bytes_to_frame(substream,
Afe_Block->u4DMAReadIdx);
}
spin_unlock_irqrestore(&pMemControl->substream_lock, flags);
return Frameidx;
}
static int mtk_pcm_dl2_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
/* struct snd_dma_buffer *dma_buf = &substream->dma_buffer; */
int ret = 0;
/* runtime->dma_bytes has to be set manually to allow mmap */
substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
substream->runtime->dma_area = Dl2_Playback_dma_buf->area;
substream->runtime->dma_addr = Dl2_Playback_dma_buf->addr;
SetHighAddr(Soc_Aud_Digital_Block_MEM_DL2, true,
substream->runtime->dma_addr);
set_mem_block(substream, hw_params, pMemControl,
Soc_Aud_Digital_Block_MEM_DL2);
#if defined(DL2_DEBUG_LOG)
pr_debug("dma_bytes = %zu dma_area = %p dma_addr = 0x%lx\n",
substream->runtime->dma_bytes,
substream->runtime->dma_area,
(long)substream->runtime->dma_addr);
#endif
return ret;
}
static int mtk_pcm_dl2_hw_free(struct snd_pcm_substream *substream)
{
#if defined(DL2_DEBUG_LOG)
pr_debug("%s()\n", __func__);
#endif
return 0;
}
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
.count = ARRAY_SIZE(soc_high_supported_sample_rates),
.list = soc_high_supported_sample_rates,
.mask = 0,
};
static int mtk_pcm_dl2_open(struct snd_pcm_substream *substream)
{
int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
mtk_pcm_dl2_hardware.buffer_bytes_max = GetPLaybackDramSize();
AudDrv_Emi_Clk_On();
#if defined(DL2_DEBUG_LOG)
pr_debug("mtk_pcm_dl2_hardware.buffer_bytes_max = %zu\n",
mtk_pcm_dl2_hardware.buffer_bytes_max);
#endif
runtime->hw = mtk_pcm_dl2_hardware;
AudDrv_Clk_On();
memcpy((void *)(&(runtime->hw)), (void *)&mtk_pcm_dl2_hardware,
sizeof(struct snd_pcm_hardware));
pMemControl = Get_Mem_ControlT(Soc_Aud_Digital_Block_MEM_DL2);
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&constraints_sample_rates);
if (ret < 0)
pr_err("snd_pcm_hw_constraint_integer failed\n");
if (ret < 0) {
pr_err("ret < 0 mtk_soc_pcm_dl2_close\n");
mtk_soc_pcm_dl2_close(substream);
return ret;
}
#ifdef AUDIO_DL2_ISR_COPY_SUPPORT
if (!ISRCopyBuffer.pBufferBase) {
ISRCopyBuffer.pBufferBase = kmalloc(ISRCopyMaxSize, GFP_KERNEL);
if (!ISRCopyBuffer.pBufferBase)
pr_err("%s alloc ISRCopyBuffer fail\n", __func__);
else
ISRCopyBuffer.u4BufferSizeMax = ISRCopyMaxSize;
}
#endif
return 0;
}
static int mtk_soc_pcm_dl2_close(struct snd_pcm_substream *substream)
{
pr_debug("%s, mPrepareDone = %d, fast_dl_hdoutput = %d\n",
__func__, mPrepareDone, fast_dl_hdoutput);
if (mPrepareDone == true) {
/* stop DAC output */
set_memif_pbuf_size(Soc_Aud_Digital_Block_MEM_DL2,
MEMIF_PBUF_SIZE_256_BYTES);
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, false);
if (GetI2SDacEnable() == false)
SetI2SDacEnable(false);
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_2, false);
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_2) ==
false)
Afe_Set_Reg(AFE_I2S_CON3, 0x0, 0x1);
RemoveMemifSubStream(Soc_Aud_Digital_Block_MEM_DL2, substream);
if (fast_dl_hdoutput == true) {
/* here to close APLL */
if (!mtk_soc_always_hd) {
DisableAPLLTunerbySampleRate(
substream->runtime->rate);
DisableALLbySampleRate(
substream->runtime->rate);
}
}
EnableAfe(false);
mPrepareDone = false;
}
AudDrv_Emi_Clk_Off();
AudDrv_Clk_Off();
#ifdef AUDIO_DL2_ISR_COPY_SUPPORT
kfree(ISRCopyBuffer.pBufferBase);
memset(&ISRCopyBuffer, 0, sizeof(ISRCopyBuffer));
#endif
return 0;
}
static int mtk_pcm_dl2_prepare(struct snd_pcm_substream *substream)
{
bool mI2SWLen = Soc_Aud_I2S_WLEN_WLEN_16BITS;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int u32AudioI2S = 0;
pr_debug("%s\n", __func__);
if (mPrepareDone == false) {
pr_info(
"%s format = %d SNDRV_PCM_FORMAT_S32_LE = %d SNDRV_PCM_FORMAT_U32_LE = %d, fast_dl_hdoutput = %d\n",
__func__, runtime->format, SNDRV_PCM_FORMAT_S32_LE,
SNDRV_PCM_FORMAT_U32_LE, fast_dl_hdoutput);
SetMemifSubStream(Soc_Aud_Digital_Block_MEM_DL2, substream);
set_memif_pbuf_size(Soc_Aud_Digital_Block_MEM_DL2,
MEMIF_PBUF_SIZE_32_BYTES);
if (runtime->format == SNDRV_PCM_FORMAT_S32_LE ||
runtime->format == SNDRV_PCM_FORMAT_U32_LE) {
/* not support 24bit +++ */
SetMemIfFetchFormatPerSample(
Soc_Aud_Digital_Block_MEM_DL2,
AFE_WLEN_32_BIT_ALIGN_8BIT_0_24BIT_DATA);
SetConnectionFormat(OUTPUT_DATA_FORMAT_24BIT,
Soc_Aud_AFE_IO_Block_I2S1_DAC);
SetConnectionFormat(OUTPUT_DATA_FORMAT_24BIT,
Soc_Aud_AFE_IO_Block_I2S1_DAC_2);
SetConnectionFormat(OUTPUT_DATA_FORMAT_24BIT,
Soc_Aud_AFE_IO_Block_I2S3);
/* not support 24bit --- */
mI2SWLen = Soc_Aud_I2S_WLEN_WLEN_32BITS;
} else {
/* not support 24bit +++ */
SetMemIfFetchFormatPerSample(
Soc_Aud_Digital_Block_MEM_DL2, AFE_WLEN_16_BIT);
SetConnectionFormat(OUTPUT_DATA_FORMAT_16BIT,
Soc_Aud_AFE_IO_Block_I2S1_DAC);
SetConnectionFormat(OUTPUT_DATA_FORMAT_16BIT,
Soc_Aud_AFE_IO_Block_I2S1_DAC_2);
SetConnectionFormat(OUTPUT_DATA_FORMAT_16BIT,
Soc_Aud_AFE_IO_Block_I2S3);
/* not support 24bit --- */
mI2SWLen = Soc_Aud_I2S_WLEN_WLEN_16BITS;
}
SetSampleRate(Soc_Aud_Digital_Block_MEM_I2S, runtime->rate);
u32AudioI2S =
SampleRateTransform(runtime->rate,
Soc_Aud_Digital_Block_I2S_OUT_2)
<< 8;
u32AudioI2S |= Soc_Aud_I2S_FORMAT_I2S << 3; /* use I2s format */
u32AudioI2S |= mI2SWLen << 1;
if (fast_dl_hdoutput == true) {
/* here to open APLL */
if (!mtk_soc_always_hd) {
EnableALLbySampleRate(runtime->rate);
EnableAPLLTunerbySampleRate(runtime->rate);
}
/* Low jitter mode */
u32AudioI2S |= Soc_Aud_LOW_JITTER_CLOCK << 12;
} else {
u32AudioI2S &= ~(Soc_Aud_LOW_JITTER_CLOCK << 12);
}
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_2) ==
false) {
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_2,
true);
Afe_Set_Reg(AFE_I2S_CON3, u32AudioI2S | 1,
AFE_MASK_ALL);
} else {
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_2,
true);
}
/* start I2S DAC out */
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC) ==
false) {
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC,
true);
SetI2SDacOut(substream->runtime->rate, fast_dl_hdoutput,
mI2SWLen);
SetI2SDacEnable(true);
} else {
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC,
true);
}
EnableAfe(true);
mPrepareDone = true;
}
return 0;
}
static int mtk_pcm_dl2_start(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
pr_debug("%s\n", __func__);
/* here start digital part */
SetIntfConnection(Soc_Aud_InterCon_Connection,
Soc_Aud_AFE_IO_Block_MEM_DL2,
Soc_Aud_AFE_IO_Block_I2S1_DAC);
SetIntfConnection(Soc_Aud_InterCon_Connection,
Soc_Aud_AFE_IO_Block_MEM_DL2,
Soc_Aud_AFE_IO_Block_I2S1_DAC_2);
SetIntfConnection(Soc_Aud_InterCon_Connection,
Soc_Aud_AFE_IO_Block_MEM_DL2,
Soc_Aud_AFE_IO_Block_I2S3);
#ifdef CONFIG_MTK_FPGA
/* set loopback test interconnection */
SetIntfConnection(Soc_Aud_InterCon_Connection,
Soc_Aud_AFE_IO_Block_MEM_DL2,
Soc_Aud_AFE_IO_Block_MEM_VUL);
#endif
/* here to set interrupt */
irq_add_substream_user(
substream, irq_request_number(Soc_Aud_Digital_Block_MEM_DL2),
runtime->rate, runtime->period_size);
SetSampleRate(Soc_Aud_Digital_Block_MEM_DL2, runtime->rate);
SetChannels(Soc_Aud_Digital_Block_MEM_DL2, runtime->channels);
SetMemoryPathEnable(Soc_Aud_Digital_Block_MEM_DL2, true);
EnableAfe(true);
StartCheckTime = true;
PrevTime = NowTime = 0;
UnderflowTime = runtime->period_size * runtime->periods * 1000000000 /
runtime->rate;
return 0;
}
static int mtk_pcm_dl2_trigger(struct snd_pcm_substream *substream, int cmd)
{
#if defined(DL2_DEBUG_LOG)
pr_debug("mtk_pcm_trigger cmd = %d\n", cmd);
#endif
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
return mtk_pcm_dl2_start(substream);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
return mtk_pcm_dl2_stop(substream);
}
return -EINVAL;
}
static int mtk_pcm_dl2_copy_(void __user *dst, unsigned long *size,
struct afe_block_t *Afe_Block, bool bCopy)
{
int copy_size = 0, Afe_WriteIdx_tmp;
unsigned long flags;
/* struct snd_pcm_runtime *runtime = substream->runtime; */
char *data_w_ptr = (char *)dst;
unsigned long count = *size;
#ifdef DL2_DEBUG_LOG
pr_debug(
"AudDrv_write WriteIdx=0x%x, ReadIdx=0x%x, DataRemained=0x%x\n",
Afe_Block->u4WriteIdx, Afe_Block->u4DMAReadIdx,
Afe_Block->u4DataRemained);
#endif
if (Afe_Block->u4BufferSize == 0) {
pr_err("AudDrv_write: u4BufferSize=0 Error");
return 0;
}
spin_lock_irqsave(&pMemControl->substream_lock, flags);
copy_size = Afe_Block->u4BufferSize -
Afe_Block->u4DataRemained; /* free space of the buffer */
spin_unlock_irqrestore(&pMemControl->substream_lock, flags);
if (count <= copy_size) {
if (copy_size < 0)
copy_size = 0;
else
copy_size = count;
} else {
if (unlikely(get_LowLatencyDebug() & DEBUG_DL2_AEE_OTHERS)) {
pr_debug("%s, Insufficient data !\n", __func__);
AUDIO_AEE("ISRCopy has remaining data !!");
}
}
#ifdef AUDIO_64BYTE_ALIGN /* no need to do 64byte align */
copy_size = word_size_align(copy_size);
#endif
*size = copy_size;
PRINT_DEBUG_LOG(
"%s, copy_size=%d, count=%d, bCopy %d, %pf %pf %pf %pf\n",
__func__, copy_size, (unsigned int)count, bCopy,
(void *)CALLER_ADDR0, (void *)CALLER_ADDR1,
(void *)CALLER_ADDR2, (void *)CALLER_ADDR3);
PRINT_DEBUG_LOG(
"AudDrv_write DataRemained:%d, ReadIdx=%d, WriteIdx:%d\r\n",
Afe_Block->u4DataRemained, Afe_Block->u4DMAReadIdx,
Afe_Block->u4WriteIdx);
if (copy_size != 0) {
spin_lock_irqsave(&pMemControl->substream_lock, flags);
Afe_WriteIdx_tmp = Afe_Block->u4WriteIdx;
spin_unlock_irqrestore(&pMemControl->substream_lock, flags);
#ifdef DL2_DEBUG_LOG
pr_debug(
"Afe_WriteIdx_tmp %d, copy_size %d, u4BufferSize %d\n",
Afe_WriteIdx_tmp, copy_size, Afe_Block->u4BufferSize);
#endif
if (Afe_WriteIdx_tmp + copy_size <
Afe_Block->u4BufferSize) { /* copy once */
if (bCopy) {
if (dataTransfer((Afe_Block->pucVirtBufAddr +
Afe_WriteIdx_tmp),
data_w_ptr, copy_size) == -1)
return -1;
}
spin_lock_irqsave(&pMemControl->substream_lock, flags);
Afe_Block->u4DataRemained += copy_size;
Afe_Block->u4WriteIdx = Afe_WriteIdx_tmp + copy_size;
Afe_Block->u4WriteIdx %= Afe_Block->u4BufferSize;
spin_unlock_irqrestore(&pMemControl->substream_lock,
flags);
data_w_ptr += copy_size;
count -= copy_size;
#ifdef DL2_DEBUG_LOG
PRINT_DEBUG_LOG(
"AudDrv_write finish1, DataRemained:%d, ReadIdx=%d, WriteIdx:%d\r\n",
Afe_Block->u4DataRemained,
Afe_Block->u4DMAReadIdx, Afe_Block->u4WriteIdx);
#endif
} else { /* copy twice */
kal_uint32 size_1 = 0, size_2 = 0;
#ifdef AUDIO_64BYTE_ALIGN /* no need to do 64byte align */
size_1 = word_size_align(
(Afe_Block->u4BufferSize - Afe_WriteIdx_tmp));
size_2 = word_size_align((copy_size - size_1));
#else
size_1 = Afe_Block->u4BufferSize - Afe_WriteIdx_tmp;
size_2 = copy_size - size_1;
#endif
#ifdef DL2_DEBUG_LOG
pr_debug("size_1=0x%x, size_2=0x%x\n", size_1,
size_2);
#endif
if (bCopy) {
if (dataTransfer((Afe_Block->pucVirtBufAddr +
Afe_WriteIdx_tmp),
data_w_ptr, size_1) == -1)
return -1;
}
spin_lock_irqsave(&pMemControl->substream_lock, flags);
Afe_Block->u4DataRemained += size_1;
Afe_Block->u4WriteIdx = Afe_WriteIdx_tmp + size_1;
Afe_Block->u4WriteIdx %= Afe_Block->u4BufferSize;
Afe_WriteIdx_tmp = Afe_Block->u4WriteIdx;
spin_unlock_irqrestore(&pMemControl->substream_lock,
flags);
if (bCopy) {
if (dataTransfer((Afe_Block->pucVirtBufAddr +
Afe_WriteIdx_tmp),
(data_w_ptr + size_1),
size_2) == -1)
return -1;
}
spin_lock_irqsave(&pMemControl->substream_lock, flags);
Afe_Block->u4DataRemained += size_2;
Afe_Block->u4WriteIdx = Afe_WriteIdx_tmp + size_2;
Afe_Block->u4WriteIdx %= Afe_Block->u4BufferSize;
spin_unlock_irqrestore(&pMemControl->substream_lock,
flags);
count -= copy_size;
data_w_ptr += copy_size;
PRINT_DEBUG_LOG(
"AudDrv_write finish2, DataRemained:%d, ReadIdx=%d, WriteIdx:%d\r\n",
Afe_Block->u4DataRemained,
Afe_Block->u4DMAReadIdx, Afe_Block->u4WriteIdx);
}
}
return 0;
}
#ifdef AUDIO_DL2_ISR_COPY_SUPPORT
static void detectData(char *ptr, unsigned long count)
{
if (get_LowLatencyDebug() & DEBUG_DL2_LOG_DETECT_DTAT) {
int i;
for (i = 0; i < count; i++) {
if (ptr[i]) {
pr_debug(
"mtk_pcm_dl2_copy has data, i %d /%ld\n",
i, count);
break;
}
}
if (i == count)
pr_debug("mtk_pcm_dl2_copy no data\n");
}
}
static int dataTransfer(void *dest, const void *src, uint32_t size)
{
if (unlikely(!dest || !src)) {
pr_err("%s, fail. dest %p, src %p\n", __func__, dest, src);
return 0;
}
memcpy(dest, src, size);
return 0;
}
void mtk_dl2_copy2buffer(const void *addr, uint32_t size)
{
bool again = false;
PRINT_DEBUG_LOG("%s, addr 0x%p 0x%p, size %d %d\n", __func__, addr,
ISRCopyBuffer.pBufferBase, size,
ISRCopyBuffer.u4BufferSize);
#ifdef AUDIO_64BYTE_ALIGN /* no need to do 64byte align */
size = word_size_align(size);
#endif
Auddrv_Dl2_Spinlock_lock();
retry:
if (unlikely(ISRCopyBuffer.u4BufferSize)) {
pr_info("%s, remaining data %d\n", __func__,
ISRCopyBuffer.u4BufferSize);
if (unlikely(get_LowLatencyDebug() & DEBUG_DL2_AEE_OTHERS))
AUDIO_AEE("ISRCopy has remaining data !!");
}
if (unlikely(!ISRCopyBuffer.pBufferBase ||
size > ISRCopyBuffer.u4BufferSizeMax)) {
if (!again) {
/* realloc memory */
kfree(ISRCopyBuffer.pBufferBase);
ISRCopyBuffer.pBufferBase = kmalloc(size, GFP_ATOMIC);
if (!ISRCopyBuffer.pBufferBase)
pr_err("%s, alloc ISRCopyBuffer fail, size %d\n",
__func__, size);
else
ISRCopyBuffer.u4BufferSizeMax = size;
again = true;
goto retry;
}
pr_err("%s, alloc ISRCopyBuffer fail, again %d!!\n", __func__,
again);
goto exit;
}
if (unlikely(copy_from_user(ISRCopyBuffer.pBufferBase, (char *)addr,
size))) {
pr_warn("%s Fail copy from user !!\n", __func__);
goto exit;
}
ISRCopyBuffer.pBufferIndx = ISRCopyBuffer.pBufferBase;
ISRCopyBuffer.u4BufferSize = size;
ISRCopyBuffer.u4IsrConsumeSize = 0; /* Restart */
exit:
Auddrv_Dl2_Spinlock_unlock();
}
void mtk_dl2_copy_l(void)
{
struct afe_block_t Afe_Block = pMemControl->rBlock;
unsigned long count = ISRCopyBuffer.u4BufferSize;
if (unlikely(!ISRCopyBuffer.u4BufferSize || !ISRCopyBuffer.pBufferIndx))
return;
/* for debug */
detectData((char *)ISRCopyBuffer.pBufferIndx, count);
mtk_pcm_dl2_copy_((void *)ISRCopyBuffer.pBufferIndx, &count, &Afe_Block,
true);
ISRCopyBuffer.pBufferIndx += count;
ISRCopyBuffer.u4BufferSize -= count;
ISRCopyBuffer.u4IsrConsumeSize += count;
}
static int mtk_pcm_dl2_copy(struct snd_pcm_substream *substream, int channel,
unsigned long pos, void __user *dst,
unsigned long count)
{
struct afe_block_t *Afe_Block = &pMemControl->rBlock;
int remainCount = 0;
int ret = 0;
int retryCount = 0;
#if defined(DL2_DEBUG_LOG)
pr_debug(
"%s pos = %lu count = %lu, BufferSize %d, ConsumeSize %d\n",
__func__, pos, count, ISRCopyBuffer.u4BufferSize,
ISRCopyBuffer.u4IsrConsumeSize);
#endif
Auddrv_Dl2_Spinlock_lock();
if (unlikely(!ISRCopyBuffer.pBufferIndx))
goto exit;
if (unlikely(get_LowLatencyDebug())) {
/* check underflow */
if (StartCheckTime) {
if (PrevTime == 0)
PrevTime = sched_clock();
NowTime = sched_clock();
if ((NowTime - PrevTime) > UnderflowTime) {
pr_warn("%s, dl2 underflow, UnderflowTime %d, start %ld, end %ld\n",
__func__, UnderflowTime, PrevTime,
NowTime);
if (get_LowLatencyDebug() &
DEBUG_DL2_AEE_UNDERFLOW)
AUDIO_AEE(
"mtk_pcm_dl2_copy - dl2 underflow");
}
PrevTime = NowTime;
}
}
retry:
if (ISRCopyBuffer.u4IsrConsumeSize <= 0) {
if (!ISRCopyBuffer.u4BufferSize)
goto exit;
if (unlikely(ISRCopyBuffer.u4BufferSize < count))
count = ISRCopyBuffer.u4BufferSize;
/* for debug */
detectData((char *)ISRCopyBuffer.pBufferIndx, count);
ret = mtk_pcm_dl2_copy_((void *)ISRCopyBuffer.pBufferIndx,
&count, Afe_Block, true);
ISRCopyBuffer.pBufferIndx += count;
ISRCopyBuffer.u4BufferSize -= count;
} else {
if (unlikely(ISRCopyBuffer.u4IsrConsumeSize < count)) {
remainCount = count - ISRCopyBuffer.u4IsrConsumeSize;
count = ISRCopyBuffer.u4IsrConsumeSize;
}
ret = mtk_pcm_dl2_copy_((void *)ISRCopyBuffer.pBufferIndx,
&count, Afe_Block, false);
ISRCopyBuffer.u4IsrConsumeSize -= count;
if (ISRCopyBuffer.u4IsrConsumeSize < 0)
ISRCopyBuffer.u4IsrConsumeSize = 0;
if (unlikely(remainCount)) {
if ((++retryCount) > 1) {
pr_debug(
"%s, retryCount %d, remainCount %d, count %d\n",
__func__, retryCount, remainCount,
(int)count);
if (retryCount > 5)
goto exit;
}
count = remainCount;
goto retry;
}
}
exit:
Auddrv_Dl2_Spinlock_unlock();
return ret;
}
#else
static int dataTransfer(void *dest, const void *src, uint32_t size)
{
int ret = 0;
if (unlikely(!access_ok(VERIFY_READ, src, size))) {
#if defined(DL2_DEBUG_LOG)
pr_debug(
"AudDrv_write 0ptr invalid data_w_ptr=%p, size=%d\n",
src, size);
#endif
} else {
#if defined(DL2_DEBUG_LOG)
pr_debug(
"memcpy VirtBufAddr+Afe_WriteIdx= %p,data_w_ptr = %p copy_size = 0x%x\n",
dest, src, size);
#endif
if (unlikely(copy_from_user(dest, src, size))) {
#if defined(DL2_DEBUG_LOG)
pr_debug("AudDrv_write Fail copy from user\n");
#endif
ret = -1;
}
}
return ret;
}
static int mtk_pcm_dl2_copy(struct snd_pcm_substream *substream,
int channel,
unsigned long pos,
void __user *buf,
unsigned long bytes)
{
struct afe_block_t *Afe_Block = &pMemControl->rBlock;
/* get total bytes to copy */
unsigned long count = bytes;
#if defined(DL2_DEBUG_LOG)
pr_debug("+%s(), pos = %lu, count = %lu\n", __func__, pos, count);
#endif
return mtk_pcm_dl2_copy_(buf, &count, Afe_Block, true);
}
#endif
static int mtk_pcm_dl2_silence(struct snd_pcm_substream *substream,
int channel,
unsigned long pos,
unsigned long bytes)
{
return 0; /* do nothing */
}
static void *dummy_page[2];
static struct page *mtk_pcm_dl2_page(struct snd_pcm_substream *substream,
unsigned long offset)
{
return virt_to_page(dummy_page[substream->stream]); /* the same page */
}
static struct snd_pcm_ops mtk_dl2_ops = {
.open = mtk_pcm_dl2_open,
.close = mtk_soc_pcm_dl2_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = mtk_pcm_dl2_params,
.hw_free = mtk_pcm_dl2_hw_free,
.prepare = mtk_pcm_dl2_prepare,
.trigger = mtk_pcm_dl2_trigger,
.pointer = mtk_pcm_dl2_pointer,
.copy_user = mtk_pcm_dl2_copy,
.fill_silence = mtk_pcm_dl2_silence,
.page = mtk_pcm_dl2_page,
.mmap = mtk_pcm_mmap,
};
static const struct snd_soc_component_driver mtk_soc_component = {
.name = AFE_PCM_NAME,
.ops = &mtk_dl2_ops,
.probe = mtk_asoc_dl2_component_probe,
};
#ifdef CONFIG_OF
static const struct of_device_id mt_soc_pcm_dl2_of_ids[] = {
{
.compatible = "mediatek,mt_soc_pcm_dl2",
},
{} };
#endif
static int mtk_soc_dl2_probe(struct platform_device *pdev)
{
#if defined(DL2_DEBUG_LOG)
pr_debug("%s\n", __func__);
#endif
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
if (!pdev->dev.dma_mask)
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
if (pdev->dev.of_node) {
dev_set_name(&pdev->dev, "%s", MT_SOC_DL2_PCM);
pdev->name = pdev->dev.kobj.name;
} else {
pr_err("%s invalid of_node\n", __func__);
return -ENODEV;
}
pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
return snd_soc_register_component(&pdev->dev,
&mtk_soc_component,
NULL,
0);
}
static int mtk_asoc_dl2_component_probe(struct snd_soc_component *component)
{
#if defined(DL2_DEBUG_LOG)
pr_debug("%s\n", __func__);
#endif
snd_soc_add_component_controls(component, fast_dl_controls,
ARRAY_SIZE(fast_dl_controls));
/* allocate dram */
AudDrv_Allocate_mem_Buffer(component->dev,
Soc_Aud_Digital_Block_MEM_DL2,
Dl2_MAX_BUFFER_SIZE);
Dl2_Playback_dma_buf = Get_Mem_Buffer(Soc_Aud_Digital_Block_MEM_DL2);
return 0;
}
static int mtk_soc_dl2_remove(struct platform_device *pdev)
{
AudDrv_Clk_Deinit(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
return 0;
}
static struct platform_driver mtk_dl2_driver = {
.driver = {
.name = MT_SOC_DL2_PCM,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = mt_soc_pcm_dl2_of_ids,
#endif
},
.probe = mtk_soc_dl2_probe,
.remove = mtk_soc_dl2_remove,
};
#ifndef CONFIG_OF
static struct platform_device *soc_mtkdl2_dev;
#endif
static int __init mtk_dl2_soc_platform_init(void)
{
int ret;
#if defined(DL2_DEBUG_LOG)
pr_debug("%s\n", __func__);
#endif
#ifndef CONFIG_OF
soc_mtkdl2_dev = platform_device_alloc(MT_SOC_DL2_PCM, -1);
if (!soc_mtkdl2_dev)
return -ENOMEM;
ret = platform_device_add(soc_mtkdl2_dev);
if (ret != 0) {
platform_device_put(soc_mtkdl2_dev);
return ret;
}
#endif
ret = platform_driver_register(&mtk_dl2_driver);
return ret;
}
module_init(mtk_dl2_soc_platform_init);
static void __exit mtk_dl2_soc_platform_exit(void)
{
platform_driver_unregister(&mtk_dl2_driver);
}
module_exit(mtk_dl2_soc_platform_exit);
MODULE_DESCRIPTION("AFE PCM module platform driver");
MODULE_LICENSE("GPL");