kernel_samsung_a34x-permissive/sound/soc/mediatek/common_int/mtk-soc-pcm-hdmi.c
2024-04-28 15:51:13 +02:00

1103 lines
33 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Michael Hsiao <michael.hsiao@mediatek.com>
*/
/*******************************************************************************
*
* Filename:
* ---------
* mt_soc_pcm_hdmi.c
*
* Project:
* --------
* Audio Driver Kernel Function
*
* Description:
* ------------
* Audio hdmi 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 "mt6799-hdmi.h"
#include "mtk-auddrv-afe.h"
#include "mtk-auddrv-ana.h"
#include "mtk-auddrv-clk.h"
#include "mtk-auddrv-def.h"
#include "mtk-auddrv-kernel.h"
#include "mtk-soc-afe-control.h"
#include "mtk-soc-hdmi-type.h"
#include "mtk-soc-pcm-common.h"
#include "mtk-soc-pcm-common.h"
#include "mtk-soc-pcm-platform.h"
#include <linux/dma-mapping.h>
/* information about */
static struct afe_mem_control_t *pMemControl;
static bool mHDMIPrepareDone;
static struct audio_hdmi_format mAudioHDMIFormat;
static struct snd_dma_buffer *HDMI_dma_buf;
static DEFINE_SPINLOCK(auddrv_hdmi_lock);
/*
* function implementation
*/
static int mtk_hdmi_probe(struct platform_device *pdev);
static int mtk_pcm_hdmi_close(struct snd_pcm_substream *substream);
static int mtk_afe_hdmi_component_probe(struct snd_soc_component *component);
#define _DEBUG_TDM_KERNEL_ 1
static unsigned int table_sgen_golden_values[64] = {
0x0FE50FE5, 0x285E1C44, 0x3F4A285E, 0x53C73414, 0x650C3F4A, 0x726F49E3,
0x7B6C53C7, 0x7FAB5CDC, 0x7F02650C, 0x79776C43, 0x6F42726F, 0x60C67781,
0x4E917B6C, 0x39587E27, 0x21EB7FAB, 0x09307FF4, 0xF01A7F02, 0xD7A17CD6,
0xC0B67977, 0xAC3874ED, 0x9AF36F42, 0x8D906884, 0x849360C6, 0x80545818,
0x80FD4E91, 0x86884449, 0x90BD3958, 0x9F3A2DDA, 0xB16E21EB, 0xC6A715A8,
0xDE140930, 0xF6CFFCA1, 0x0FE5F01A, 0x285EE3BB, 0x3F4AD7A1, 0x53C7CBEB,
0x650CC0B6, 0x726FB61C, 0x7B6CAC38, 0x7FABA323, 0x7F029AF3, 0x797793BC,
0x6F428D90, 0x60C6887E, 0x4E918493, 0x395881D8, 0x21EB8054, 0x0930800B,
0xF01A80FD, 0xD7A18329, 0xC0B68688, 0xAC388B12, 0x9AF390BD, 0x8D90977B,
0x84939F3A, 0x8054A7E7, 0x80FDB16E, 0x8688BBB6, 0x90BDC6A7, 0x9F3AD225,
0xB16EDE14, 0xC6A7EA57, 0xDE14F6CF, 0xF6CF035E};
static unsigned int table_sgen_4ch_golden_values[128] = {
/* step 2 1 4 3 */
/* ch2 ch1 ch4 ch3 */
0x0FE50FE5, 0x0FE50FE5, 0x285E1C44, 0x3F4A3414, 0x3F4A285E, 0x650C53C7,
0x53C73414, 0x7B6C6C43, 0x650C3F4A, 0x7F027B6C, 0x726F49E3, 0x6F427FF4,
0x7B6C53C7, 0x4E917977, 0x7FAB5CDC, 0x21EB6884, 0x7F02650C, 0xF01A4E91,
0x79776C43, 0xC0B62DDA, 0x6F42726F, 0x9AF30930, 0x60C67781, 0x8493E3BB,
0x4E917B6C, 0x80FDC0B6, 0x39587E27, 0x90BDA323, 0x21EB7FAB, 0xB16E8D90,
0x09307FF4, 0xDE1481D8, 0xF01A7F02, 0x0FE580FD, 0xD7A17CD6, 0x0FE58B12,
0xC0B67977, 0x3F4A9F3A, 0xAC3874ED, 0x650CBBB6, 0x9AF36F42, 0x7B6CDE14,
0x8D906884, 0x7F02035E, 0x849360C6, 0x6F420FE5, 0x80545818, 0x4E913414,
0x80FD4E91, 0x21EB53C7, 0x86884449, 0xF01A6C43, 0x90BD3958, 0xC0B67B6C,
0x9F3A2DDA, 0x9AF37FF4, 0xB16E21EB, 0x84937977, 0xC6A715A8, 0x80FD6884,
0xDE140930, 0x90BD4E91, 0xF6CFFCA1, 0xB16E2DDA, 0x0FE5F01A, 0xDE140930,
0x285EE3BB, 0x0FE5E3BB, 0x3F4AD7A1, 0x0FE5C0B6, 0x53C7CBEB, 0x3F4AA323,
0x650CC0B6, 0x650C8D90, 0x726FB61C, 0x7B6C81D8, 0x7B6CAC38, 0x7F0280FD,
0x7FABA323, 0x6F428B12, 0x7F029AF3, 0x4E919F3A, 0x797793BC, 0x21EBBBB6,
0x6F428D90, 0xF01ADE14, 0x60C6887E, 0xC0B6035E, 0x4E918493, 0x9AF30FE5,
0x395881D8, 0x84933414, 0x21EB8054, 0x80FD53C7, 0x0930800B, 0x90BD6C43,
0xF01A80FD, 0xB16E7B6C, 0xD7A18329, 0xDE147FF4, 0xC0B68688, 0x0FE57977,
0xAC388B12, 0x0FE56884, 0x9AF390BD, 0x3F4A4E91, 0x8D90977B, 0x650C2DDA,
0x84939F3A, 0x7B6C0930, 0x8054A7E7, 0x7F02E3BB, 0x80FDB16E, 0x6F42C0B6,
0x8688BBB6, 0x4E91A323, 0x90BDC6A7, 0x21EB8D90, 0x9F3AD225, 0xF01A81D8,
0xB16EDE14, 0xC0B680FD, 0xC6A7EA57, 0x9AF38B12, 0xDE14F6CF, 0x84939F3A,
0xF6CF035E, 0x80FDBBB6};
static unsigned int table_sgen_8ch_golden_values[] = {
0x0FE50000, 0x0FE50FE5, 0x0FE50FE5, 0x0FE50FE5, 0x285E0000, 0x3F4A3414,
0x285E1C44, 0x3F4A3414, 0x3F4A0000, 0x650C53C7, 0x3F4A285E, 0x650C53C7,
0x53C70000, 0x7B6C6C43, 0x53C73414, 0x7B6C6C43, 0x650C0000, 0x7F027B6C,
0x650C3F4A, 0x7F027B6C, 0x726F0000, 0x6F427FF4, 0x726F49E3, 0x6F427FF4,
0x7B6C0000, 0x4E917977, 0x7B6C53C7, 0x4E917977, 0x7FAB0000, 0x21EB6884,
0x7FAB5CDC, 0x21EB6884, 0x7F020000, 0xF01A4E91, 0x7F02650C, 0xF01A4E91,
0x79770000, 0xC0B62DDA, 0x79776C43, 0xC0B62DDA, 0x6F420000, 0x9AF30930,
0x6F42726F, 0x9AF30930, 0x60C60000, 0x8493E3BB, 0x60C67781, 0x8493E3BB,
0x4E910000, 0x80FDC0B6, 0x4E917B6C, 0x80FDC0B6, 0x39580000, 0x90BDA323,
0x39587E27, 0x90BDA323, 0x21EB0000, 0xB16E8D90, 0x21EB7FAB, 0xB16E8D90,
0x09300000, 0xDE1481D8, 0x09307FF4, 0xDE1481D8, 0xF01A0000, 0x0FE580FD,
0xF01A7F02, 0x0FE580FD, 0xD7A10000, 0x0FE58B12, 0xD7A17CD6, 0x0FE58B12,
0xC0B60000, 0x3F4A9F3A, 0xC0B67977, 0x3F4A9F3A, 0xAC380000, 0x650CBBB6,
0xAC3874ED, 0x650CBBB6, 0x9AF30000, 0x7B6CDE14, 0x9AF36F42, 0x7B6CDE14,
0x8D900000, 0x7F02035E, 0x8D906884, 0x7F02035E, 0x84930000, 0x6F420FE5,
0x849360C6, 0x6F420FE5, 0x80540000, 0x4E913414, 0x80545818, 0x4E913414,
0x80FD0000, 0x21EB53C7, 0x80FD4E91, 0x21EB53C7, 0x86880000, 0xF01A6C43,
0x86884449, 0xF01A6C43, 0x90BD0000, 0xC0B67B6C, 0x90BD3958, 0xC0B67B6C,
0x9F3A0000, 0x9AF37FF4, 0x9F3A2DDA, 0x9AF37FF4, 0xB16E0000, 0x84937977,
0xB16E21EB, 0x84937977, 0xC6A70000, 0x80FD6884, 0xC6A715A8, 0x80FD6884,
0xDE140000, 0x90BD4E91, 0xDE140930, 0x90BD4E91, 0xF6CF0000, 0xB16E2DDA,
0xF6CFFCA1, 0xB16E2DDA, 0x0FE50000, 0xDE140930, 0x0FE5F01A, 0xDE140930,
0x285E0000, 0x0FE5E3BB, 0x285EE3BB, 0x0FE5E3BB, 0x3F4A0000, 0x0FE5C0B6,
0x3F4AD7A1, 0x0FE5C0B6, 0x53C70000, 0x3F4AA323, 0x53C7CBEB, 0x3F4AA323,
0x650C0000, 0x650C8D90, 0x650CC0B6, 0x650C8D90, 0x726F0000, 0x7B6C81D8,
0x726FB61C, 0x7B6C81D8, 0x7B6C0000, 0x7F0280FD, 0x7B6CAC38, 0x7F0280FD,
0x7FAB0000, 0x6F428B12, 0x7FABA323, 0x6F428B12, 0x7F020000, 0x4E919F3A,
0x7F029AF3, 0x4E919F3A, 0x79770000, 0x21EBBBB6, 0x797793BC, 0x21EBBBB6,
0x6F420000, 0xF01ADE14, 0x6F428D90, 0xF01ADE14, 0x60C60000, 0xC0B6035E,
0x60C6887E, 0xC0B6035E, 0x4E910000, 0x9AF30FE5, 0x4E918493, 0x9AF30FE5,
0x39580000, 0x84933414, 0x395881D8, 0x84933414, 0x21EB0000, 0x80FD53C7,
0x21EB8054, 0x80FD53C7, 0x09300000, 0x90BD6C43, 0x0930800B, 0x90BD6C43,
0xF01A0000, 0xB16E7B6C, 0xF01A80FD, 0xB16E7B6C, 0xD7A10000, 0xDE147FF4,
0xD7A18329, 0xDE147FF4, 0xC0B60000, 0x0FE57977, 0xC0B68688, 0x0FE57977,
0xAC380000, 0x0FE56884, 0xAC388B12, 0x0FE56884, 0x9AF30000, 0x3F4A4E91,
0x9AF390BD, 0x3F4A4E91, 0x8D900000, 0x650C2DDA, 0x8D90977B, 0x650C2DDA,
0x84930000, 0x7B6C0930, 0x84939F3A, 0x7B6C0930, 0x80540000, 0x7F02E3BB,
0x8054A7E7, 0x7F02E3BB, 0x80FD0000, 0x6F42C0B6, 0x80FDB16E, 0x6F42C0B6,
0x86880000, 0x4E91A323, 0x8688BBB6, 0x4E91A323, 0x90BD0000, 0x21EB8D90,
0x90BDC6A7, 0x21EB8D90, 0x9F3A0000, 0xF01A81D8, 0x9F3AD225, 0xF01A81D8,
0xB16E0000, 0xC0B680FD, 0xB16EDE14, 0xC0B680FD, 0xC6A70000, 0x9AF38B12,
0xC6A7EA57, 0x9AF38B12, 0xDE140000, 0x84939F3A, 0xDE14F6CF, 0x84939F3A,
0xF6CF0000, 0x80FDBBB6, 0xF6CF035E, 0x80FDBBB6,
};
static void copysinewavetohdmi(unsigned int channels)
{
uint8_t *Bufferaddr;
int Hdmi_Buffer_length;
unsigned int arraybytes;
unsigned int *SinewaveArr;
int i;
Bufferaddr = HDMI_dma_buf->area;
Hdmi_Buffer_length = HDMI_dma_buf->bytes;
SinewaveArr = NULL;
arraybytes = 0;
i = 0;
if (channels == 2) {
arraybytes = ARRAY_SIZE(table_sgen_golden_values) *
sizeof(table_sgen_golden_values[0]);
SinewaveArr = table_sgen_golden_values;
}
if (channels == 4) {
arraybytes = ARRAY_SIZE(table_sgen_4ch_golden_values) *
sizeof(table_sgen_4ch_golden_values[0]);
SinewaveArr = table_sgen_4ch_golden_values;
}
if (channels == 8) {
arraybytes = ARRAY_SIZE(table_sgen_8ch_golden_values) *
sizeof(table_sgen_8ch_golden_values[0]);
SinewaveArr = table_sgen_8ch_golden_values;
}
if (channels == 0) {
memset_io((void *)(Bufferaddr), 0x7f7f7f7f,
Hdmi_Buffer_length); /* using for observe data */
pr_info("use fix pattern Bufferaddr = %p Hhdmi_Buffer_length = %d\n",
Bufferaddr, Hdmi_Buffer_length);
return;
}
pr_debug("%s buffer area = %p arraybytes = %d bufferlength = %zu\n",
__func__, HDMI_dma_buf->area, arraybytes, HDMI_dma_buf->bytes);
for (i = 0; i < HDMI_dma_buf->bytes; i += arraybytes) {
memcpy((void *)(Bufferaddr + i), (void *)SinewaveArr,
arraybytes);
}
for (i = 0; i < 512; i++)
pr_debug("Bufferaddr[%d] = %x\n", i, *(Bufferaddr + i));
}
static void SetHDMIAddress(void)
{
Afe_Set_Reg(AFE_HDMI_BASE, HDMI_dma_buf->addr, 0xffffffff);
Afe_Set_Reg(AFE_HDMI_END,
HDMI_dma_buf->addr + (HDMI_dma_buf->bytes - 1), 0xffffffff);
}
static int mHdmi_sidegen_control;
static int mHdmi_display_control;
static const char *const HDMI_SIDEGEN[] = {"Off", "On"};
static const char *const HDMI_DISPLAY[] = {"MHL", "SLIMPORT"};
static bool irq5_user;
static const struct soc_enum Audio_Hdmi_Enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(HDMI_SIDEGEN), HDMI_SIDEGEN),
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(HDMI_DISPLAY), HDMI_DISPLAY),
};
static int Audio_hdmi_SideGen_Get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
pr_debug("%s(), mHdmi_sidegen_control = %d\n", __func__, mHdmi_sidegen_control);
ucontrol->value.integer.value[0] = mHdmi_sidegen_control;
return 0;
}
bool set_tdm_lrck_inverse_by_channel(int out_channel)
{
if (out_channel > 2)
return SetTDMLrckInverse(false);
else
return SetTDMLrckInverse(true);
}
static int Audio_hdmi_SideGen_Set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct audio_hdmi_format *ptrAudioHDMIFormat;
unsigned int runsamplerate = 44100;
unsigned int outchannel = 2;
/* unsigned int HDMIchannel = 8; */
snd_pcm_format_t format = SNDRV_PCM_FORMAT_S16_LE;
ptrAudioHDMIFormat = &mAudioHDMIFormat;
if (ucontrol->value.enumerated.item[0] > ARRAY_SIZE(HDMI_SIDEGEN)) {
pr_err("return -EINVAL\n");
return -EINVAL;
}
mHdmi_sidegen_control = ucontrol->value.integer.value[0];
if (mHdmi_sidegen_control) {
unsigned int MclkDiv = 0;
pr_debug("%s(),mHdmi_sidegen_control\n", __func__);
/* Open */
AudDrv_Clk_On();
mtk_Hdmi_Configuration_Init((void *)ptrAudioHDMIFormat);
mtk_Hdmi_Clock_Set((void *)ptrAudioHDMIFormat);
EnableApll1(true);
EnableApll2(true);
EnableI2SCLKDiv(ptrAudioHDMIFormat->mI2S_MCKDIV, true);
EnableI2SCLKDiv(ptrAudioHDMIFormat->mI2S_BCKDIV, true);
AudDrv_APLL1Tuner_Clk_On();
AudDrv_APLL2Tuner_Clk_On();
/* Hw params */
SetHighAddr(Soc_Aud_Digital_Block_MEM_HDMI, true,
HDMI_dma_buf->addr);
AudDrv_Emi_Clk_On();
SetHDMIAddress();
/* Prepare */
mtk_Hdmi_Configuration_Set((void *)ptrAudioHDMIFormat,
runsamplerate, outchannel, format,
mHdmi_display_control);
MclkDiv =
SetCLkMclk(ptrAudioHDMIFormat->mI2Snum, runsamplerate);
/* SET hdmi channels , samplerate and formats */
SetMemIfFetchFormatPerSample(
Soc_Aud_Digital_Block_MEM_HDMI,
ptrAudioHDMIFormat->mMemIfFetchFormatPerSample);
SetHDMIdatalength(ptrAudioHDMIFormat->mHDMI_Data_Lens);
SetTDMDatalength(ptrAudioHDMIFormat->mTDM_Data_Lens);
/*SetCLkBclk(MclkDiv, runtime->rate, runtime->channels,
* Soc_Aud_I2S_WLEN_WLEN_16BITS);
*/
SetCLkBclk(MclkDiv, runsamplerate, outchannel,
ptrAudioHDMIFormat->mClock_Data_Lens);
SetTDMLrckWidth(ptrAudioHDMIFormat->mTDM_LRCK);
SetTDMbckcycle(ptrAudioHDMIFormat->mClock_Data_Lens);
SetTDMChannelsSdata(ptrAudioHDMIFormat->msDATA_Channels);
SetTDMDatalength(ptrAudioHDMIFormat->mTDM_Data_Lens);
/* SetTDMLrckInverse(true); */
SetTDMBckInverse(false);
set_tdm_lrck_inverse_by_channel(outchannel);
/* SetHDMIsamplerate(runtime->rate); */
SetHDMIChannels(outchannel);
SetTDMI2Smode(Soc_Aud_I2S_FORMAT_I2S);
mtk_Hdmi_Set_Sdata((void *)ptrAudioHDMIFormat);
SetHDMIClockInverse();
mtk_Hdmi_Set_Interconnection((void *)ptrAudioHDMIFormat);
/* Start */
SetHDMIEnable(true);
SetTDMEnable(true);
AudDrv_TDM_Clk_On();
SetMemoryPathEnable(Soc_Aud_Digital_Block_MEM_HDMI, true);
#ifdef _DEBUG_TDM_KERNEL_
SetConnection(Soc_Aud_InterCon_Connection,
Soc_Aud_InterConnectionInput_I00,
Soc_Aud_InterConnectionOutput_O09);
SetConnection(Soc_Aud_InterCon_Connection,
Soc_Aud_InterConnectionInput_I01,
Soc_Aud_InterConnectionOutput_O10);
SetHDMIDebugEnable(true);
#endif
copysinewavetohdmi(outchannel);
if (!irq5_user) {
irq_add_user(&irq5_user,
irq_request_number(
Soc_Aud_Digital_Block_MEM_HDMI),
runsamplerate, 1024);
irq5_user = true;
}
EnableAfe(true);
SetHDMIDumpReg();
} else {
SetMemoryPathEnable(Soc_Aud_Digital_Block_MEM_HDMI, false);
SetHDMIEnable(false);
SetTDMEnable(false);
AudDrv_Emi_Clk_Off();
AudDrv_APLL1Tuner_Clk_Off();
AudDrv_APLL2Tuner_Clk_Off();
EnableI2SCLKDiv(ptrAudioHDMIFormat->mI2S_MCKDIV, false);
EnableI2SCLKDiv(ptrAudioHDMIFormat->mI2S_BCKDIV, false);
EnableApll1(false);
EnableApll2(false);
EnableAfe(false);
AudDrv_TDM_Clk_Off(); /* disable HDMI CK */
AudDrv_Clk_Off();
}
return 0;
}
static int Audio_hdmi_disport_Get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
pr_debug("Audio_hdmi_slimport_Get = %d\n", mHdmi_display_control);
ucontrol->value.integer.value[0] = mHdmi_display_control;
return 0;
}
static int Audio_hdmi_disport_Set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
pr_debug("Audio_hdmi_slimport_Set = %d\n", mHdmi_display_control);
if (ucontrol->value.enumerated.item[0] > ARRAY_SIZE(HDMI_DISPLAY)) {
pr_err("return -EINVAL\n");
return -EINVAL;
}
mHdmi_display_control = ucontrol->value.integer.value[0];
return 0;
}
static const struct snd_kcontrol_new Audio_snd_hdmi_controls[] = {
SOC_ENUM_EXT("Audio_Hdmi_SideGen_Switch", Audio_Hdmi_Enum[0],
Audio_hdmi_SideGen_Get, Audio_hdmi_SideGen_Set),
SOC_ENUM_EXT("Audio_Hdmi_Display_Switch", Audio_Hdmi_Enum[1],
Audio_hdmi_disport_Get, Audio_hdmi_disport_Set),
};
static struct snd_pcm_hardware mtk_hdmi_hardware = {
.info = (SNDRV_PCM_INFO_MMAP | 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 = HDMI_USE_CHANNELS_MIN,
.channels_max = HDMI_USE_CHANNELS_MAX,
.buffer_bytes_max = HDMI_MAX_BUFFER_SIZE,
.period_bytes_max = HDMI_MAX_PERIODBYTE_SIZE,
.periods_min = HDMI_MIN_PERIOD_SIZE,
.periods_max = HDMI_MAX_8CH_24BIT_PERIOD_SIZE,
.fifo_size = 0,
};
static int mtk_pcm_hdmi_stop(struct snd_pcm_substream *substream)
{
struct afe_block_t *Afe_Block = &(pMemControl->rBlock);
pr_debug("%s()\n", __func__);
irq_remove_user(substream,
irq_request_number(Soc_Aud_Digital_Block_MEM_HDMI));
SetMemoryPathEnable(Soc_Aud_Digital_Block_MEM_HDMI, false);
#ifdef _DEBUG_TDM_KERNEL_
SetHDMIDebugEnable(false);
/*msleep(1); */
#endif
SetTDMEnable(false); /* disable TDM */
SetHDMIEnable(false);
AudDrv_TDM_Clk_Off(); /* disable HDMI CK */
EnableAfe(false);
RemoveMemifSubStream(Soc_Aud_Digital_Block_MEM_HDMI, substream);
Afe_Block->u4DMAReadIdx = 0;
Afe_Block->u4WriteIdx = 0;
Afe_Block->u4DataRemained = 0;
return 0;
}
static snd_pcm_uframes_t
mtk_pcm_hdmi_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);
#if defined(HDMI_DEBUG_LOG)
pr_debug(" %s Afe_Block->u4DMAReadIdx = 0x%x\n", __func__,
Afe_Block->u4DMAReadIdx);
#endif
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_MEM_HDMI) == true) {
HW_Cur_ReadIdx = Afe_Get_Reg(AFE_HDMI_CUR);
if (HW_Cur_ReadIdx == 0) {
#if defined(HDMI_DEBUG_LOG)
pr_debug("[Auddrv] HW_Cur_ReadIdx ==0\n");
#endif
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;
}
Afe_Block->u4DataRemained -= Afe_consumed_bytes;
Afe_Block->u4DMAReadIdx += Afe_consumed_bytes;
Afe_Block->u4DMAReadIdx %= Afe_Block->u4BufferSize;
#if defined(HDMI_DEBUG_LOG)
pr_debug(
"[Auddrv] HW_Cur_ReadIdx = 0x%x, HW_memory_index = 0x%x, Afe_consumed_bytes = 0x%x\n",
HW_Cur_ReadIdx, HW_memory_index, Afe_consumed_bytes);
#endif
return audio_bytes_to_frame(substream, Afe_Block->u4DMAReadIdx);
}
Frameidx = audio_bytes_to_frame(substream, Afe_Block->u4DMAReadIdx);
return Frameidx;
}
static void SetHDMIBuffer(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
kal_uint32 u4tmpMrg1;
struct snd_pcm_runtime *runtime = substream->runtime;
struct afe_block_t *pblock = &(pMemControl->rBlock);
pblock->pucPhysBufAddr = runtime->dma_addr;
pblock->pucVirtBufAddr = runtime->dma_area;
pblock->u4BufferSize = runtime->dma_bytes;
pblock->u4SampleNumMask = 0x001f; /* 32 byte align */
pblock->u4WriteIdx = 0;
pblock->u4DMAReadIdx = 0;
pblock->u4DataRemained = 0;
pblock->u4fsyncflag = false;
pblock->uResetFlag = true;
#if defined(HDMI_DEBUG_LOG)
pr_debug("%s, dma_bytes = %d, dma_area = %p, dma_addr = 0x%x\n",
__func__, pblock->u4BufferSize, pblock->pucVirtBufAddr,
pblock->pucPhysBufAddr);
#endif
Afe_Set_Reg(AFE_HDMI_BASE, pblock->pucPhysBufAddr, 0xffffffff);
Afe_Set_Reg(AFE_HDMI_END,
pblock->pucPhysBufAddr + (pblock->u4BufferSize - 1),
0xffffffff);
u4tmpMrg1 = Afe_Get_Reg(AFE_HDMI_BASE);
u4tmpMrg1 &= 0x00ffffff;
#if defined(HDMI_DEBUG_LOG)
pr_debug("%s(), AFE_HDMI_BASE = 0x%x\n", __func__, u4tmpMrg1);
#endif
}
static int mtk_pcm_hdmi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
int ret = 0;
#if defined(HDMI_DEBUG_LOG)
pr_debug("%s\n", __func__);
#endif
dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
dma_buf->dev.dev = substream->pcm->card->dev;
dma_buf->private_data = NULL;
#if defined(HDMI_DEBUG_LOG)
pr_debug("%s(), HDMI_dma_buf->area\n", __func__);
#endif
HDMI_dma_buf->bytes = substream->runtime->dma_bytes =
params_buffer_bytes(hw_params);
runtime->dma_area = HDMI_dma_buf->area;
runtime->dma_addr = HDMI_dma_buf->addr;
SetHighAddr(Soc_Aud_Digital_Block_MEM_HDMI, true, runtime->dma_addr);
AudDrv_Emi_Clk_On();
#if defined(HDMI_DEBUG_LOG)
pr_debug("2 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
SetHDMIBuffer(substream, hw_params);
return ret;
}
static int mtk_pcm_hdmi_hw_free(struct snd_pcm_substream *substream)
{
AudDrv_Emi_Clk_Off();
return 0;
}
/* Conventional and unconventional sample rate supported */
static unsigned int supported_sample_rates[] = {8000, 11025, 12000, 16000,
22050, 24000, 32000, 44100,
48000, 88200, 96000, 192000};
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
.count = ARRAY_SIZE(supported_sample_rates),
.list = supported_sample_rates,
.mask = 0,
};
static int mtk_pcm_hdmi_open(struct snd_pcm_substream *substream)
{
struct audio_hdmi_format *ptrAudioHDMIFormat;
struct snd_pcm_runtime *runtime;
int ret;
runtime = substream->runtime;
ptrAudioHDMIFormat = &mAudioHDMIFormat;
pr_debug("%s()\n", __func__);
pMemControl = Get_Mem_ControlT(Soc_Aud_Digital_Block_MEM_HDMI);
runtime->hw = mtk_hdmi_hardware;
AudDrv_Clk_On();
mtk_Hdmi_Configuration_Init((void *)ptrAudioHDMIFormat);
mtk_Hdmi_Clock_Set((void *)ptrAudioHDMIFormat);
memcpy((void *)(&(runtime->hw)), (void *)&mtk_hdmi_hardware,
sizeof(struct snd_pcm_hardware));
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&constraints_sample_rates);
/* print for hw pcm information */
#if defined(HDMI_DEBUG_LOG)
pr_debug(
"%s(), runtime rate = %d, channels = %d, substream->pcm->device = %d\n",
__func__, runtime->rate, runtime->channels, substream->pcm->device);
#endif
runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED;
runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
EnableApll1(true);
EnableApll2(true);
EnableI2SCLKDiv(ptrAudioHDMIFormat->mI2S_MCKDIV, true);
EnableI2SCLKDiv(ptrAudioHDMIFormat->mI2S_BCKDIV, true);
AudDrv_APLL1Tuner_Clk_On();
AudDrv_APLL2Tuner_Clk_On();
#if defined(HDMI_DEBUG_LOG)
pr_debug("%s(), return\n", __func__);
#endif
return 0;
}
static int mtk_pcm_hdmi_close(struct snd_pcm_substream *substream)
{
struct audio_hdmi_format *ptrAudioHDMIFormat;
ptrAudioHDMIFormat = &mAudioHDMIFormat;
pr_debug("%s\n", __func__);
AudDrv_APLL1Tuner_Clk_Off();
AudDrv_APLL2Tuner_Clk_Off();
mHDMIPrepareDone = false;
EnableI2SCLKDiv(ptrAudioHDMIFormat->mI2S_MCKDIV, false);
EnableI2SCLKDiv(ptrAudioHDMIFormat->mI2S_BCKDIV, false);
EnableApll1(false);
EnableApll2(false);
AudDrv_Clk_Off();
return 0;
}
static int mtk_pcm_hdmi_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
struct audio_hdmi_format *ptrAudioHDMIFormat;
unsigned int MclkDiv;
runtime = substream->runtime;
ptrAudioHDMIFormat = &mAudioHDMIFormat;
pr_debug("%s format =%d, rate = %d ch = %d size = %lu, control=%d\n",
__func__, runtime->format, runtime->rate, runtime->channels,
runtime->period_size, mHdmi_display_control);
if (mHDMIPrepareDone == false) {
mtk_Hdmi_Configuration_Set((void *)ptrAudioHDMIFormat,
runtime->rate, runtime->channels,
runtime->format,
mHdmi_display_control);
MclkDiv =
SetCLkMclk(ptrAudioHDMIFormat->mI2Snum, runtime->rate);
/* SET hdmi channels , samplerate and formats */
SetMemIfFetchFormatPerSample(
Soc_Aud_Digital_Block_MEM_HDMI,
ptrAudioHDMIFormat->mMemIfFetchFormatPerSample);
SetHDMIdatalength(ptrAudioHDMIFormat->mHDMI_Data_Lens);
SetTDMDatalength(ptrAudioHDMIFormat->mTDM_Data_Lens);
/*SetCLkBclk(MclkDiv, runtime->rate, runtime->channels,
* Soc_Aud_I2S_WLEN_WLEN_16BITS);
*/
SetCLkBclk(MclkDiv, runtime->rate,
ptrAudioHDMIFormat->mHDMI_Channels,
ptrAudioHDMIFormat->mClock_Data_Lens);
SetTDMLrckWidth(ptrAudioHDMIFormat->mTDM_LRCK);
SetTDMbckcycle(ptrAudioHDMIFormat->mClock_Data_Lens);
SetTDMChannelsSdata(ptrAudioHDMIFormat->msDATA_Channels);
SetTDMDatalength(ptrAudioHDMIFormat->mTDM_Data_Lens);
/*SetTDMLrckInverse(true);*/
SetTDMBckInverse(false);
if (runtime->channels > 2)
SetTDMLrckInverse(false);
else
SetTDMLrckInverse(true);
/*SetHDMIsamplerate(runtime->rate);*/
#ifdef __2CH_TO_8CH
SetHDMIChannels(8);
#else
SetHDMIChannels(runtime->channels);
#endif
SetTDMI2Smode(Soc_Aud_I2S_FORMAT_I2S);
mtk_Hdmi_Set_Sdata((void *)ptrAudioHDMIFormat);
SetHDMIClockInverse();
mtk_Hdmi_Set_Interconnection((void *)ptrAudioHDMIFormat);
mHDMIPrepareDone = true;
}
return 0;
}
static int mtk_pcm_hdmi_start(struct snd_pcm_substream *substream)
{
/* unsigned int u32AudioI2S = 0; */
SetMemifSubStream(Soc_Aud_Digital_Block_MEM_HDMI, substream);
SetHDMIEnable(true);
SetTDMEnable(true);
/*Afe_Set_Reg(AUDIO_AUDIO_TOP_CON0, 0 << 20, 1 << 20); // enable HDMI
* CK
*/
AudDrv_TDM_Clk_On();
SetMemoryPathEnable(Soc_Aud_Digital_Block_MEM_HDMI, true);
#ifdef _DEBUG_TDM_KERNEL_
SetConnection(Soc_Aud_InterCon_Connection,
Soc_Aud_InterConnectionInput_I00,
Soc_Aud_InterConnectionOutput_O09);
SetConnection(Soc_Aud_InterCon_Connection,
Soc_Aud_InterConnectionInput_I01,
Soc_Aud_InterConnectionOutput_O10);
SetHDMIDebugEnable(true);
#endif
#ifdef _TONE_TEST
copysinewavetohdmi(substream->runtime->channels);
#endif
/* here to set interrupt */
/* ALPS01889945 , stereo , multi channel switch A/V sync issue */
/* 32bit , stereo , 64 BCK for one count, (hal size)8192 bytes/(64/8) =
* 1024 count
*/
irq_add_user(substream,
irq_request_number(Soc_Aud_Digital_Block_MEM_HDMI),
substream->runtime->rate, 1024);
EnableAfe(true);
SetHDMIDumpReg();
return 0;
}
static int mtk_pcm_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
{
#if defined(HDMI_DEBUG_LOG)
pr_debug("%s(), cmd = %d\n", __func__, cmd);
#endif
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
return mtk_pcm_hdmi_start(substream);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
return mtk_pcm_hdmi_stop(substream);
}
return -EINVAL;
}
static int mtk_pcm_hdmi_copy(struct snd_pcm_substream *substream,
int channel,
unsigned long pos,
void __user *buf,
unsigned long bytes)
{
struct afe_block_t *Afe_Block = NULL;
int copy_size = 0, Afe_WriteIdx_tmp;
unsigned long flags;
char *data_w_ptr = (char *)buf;
unsigned long count = bytes;
/* check which memif nned to be write */
Afe_Block = &(pMemControl->rBlock);
/* handle for buffer management */
#if defined(HDMI_DEBUG_LOG)
pr_debug(
"%s(), count = %d, WriteIdx = %x, ReadIdx = %x, DataRemained = %x\n",
__func__, (kal_uint32)count, Afe_Block->u4WriteIdx,
Afe_Block->u4DMAReadIdx, Afe_Block->u4DataRemained);
#endif
if (Afe_Block->u4BufferSize == 0) {
#if defined(HDMI_DEBUG_LOG)
pr_debug("AudDrv_write: u4BufferSize=0 Error");
#endif
return 0;
}
spin_lock_irqsave(&auddrv_hdmi_lock, flags);
copy_size = Afe_Block->u4BufferSize -
Afe_Block->u4DataRemained; /* free space of the buffer */
spin_unlock_irqrestore(&auddrv_hdmi_lock, flags);
if (count <= copy_size) {
if (copy_size < 0)
copy_size = 0;
else
copy_size = count;
}
if (copy_size != 0) {
spin_lock_irqsave(&auddrv_hdmi_lock, flags);
Afe_WriteIdx_tmp = Afe_Block->u4WriteIdx;
spin_unlock_irqrestore(&auddrv_hdmi_lock, flags);
if (Afe_WriteIdx_tmp + copy_size <
Afe_Block->u4BufferSize) { /* copy once */
if (!access_ok(VERIFY_READ, data_w_ptr, copy_size)) {
#if defined(HDMI_DEBUG_LOG)
pr_debug(
"%s(), 0ptr invalid data_w_ptr = %p, size = %d, u4BufferSize = %d, u4DataRemained = %d",
__func__,
data_w_ptr, copy_size,
Afe_Block->u4BufferSize,
Afe_Block->u4DataRemained);
#endif
} else {
#if defined(HDMI_DEBUG_LOG)
pr_debug(
"%s(), WriteIdx = 0x%x, data_w_ptr = %p, copy_size = 0x%x\n",
__func__, Afe_Block->pucVirtBufAddr +
Afe_WriteIdx_tmp,
data_w_ptr, copy_size);
#endif
if (copy_from_user((Afe_Block->pucVirtBufAddr +
Afe_WriteIdx_tmp),
data_w_ptr, copy_size)) {
#if defined(HDMI_DEBUG_LOG)
pr_debug("AudDrv_write Fail copy from user\n");
#endif
return -1;
}
}
spin_lock_irqsave(&auddrv_hdmi_lock, flags);
Afe_Block->u4DataRemained += copy_size;
Afe_Block->u4WriteIdx = Afe_WriteIdx_tmp + copy_size;
Afe_Block->u4WriteIdx %= Afe_Block->u4BufferSize;
spin_unlock_irqrestore(&auddrv_hdmi_lock, flags);
data_w_ptr += copy_size;
count -= copy_size;
#if defined(HDMI_DEBUG_LOG)
pr_debug(
"%s(), finish1, copy_size = %x, WriteIdx = %x, ReadIdx = %x, Remained = %x \r\n",
__func__, copy_size, Afe_Block->u4WriteIdx,
Afe_Block->u4DMAReadIdx,
Afe_Block->u4DataRemained);
#endif
} else { /* copy twice */
kal_uint32 size_1 = 0, size_2 = 0;
size_1 = Afe_Block->u4BufferSize - Afe_WriteIdx_tmp;
size_2 = copy_size - size_1;
if (!access_ok(VERIFY_READ, data_w_ptr, size_1)) {
#if defined(HDMI_DEBUG_LOG)
pr_warn("HDMI_write 1ptr invalid data_w_ptr = %p, size_1 = %d, u4BufferSize = %d, u4DataRemained = %d",
data_w_ptr, size_1,
Afe_Block->u4BufferSize,
Afe_Block->u4DataRemained);
#endif
} else {
#if defined(HDMI_DEBUG_LOG)
pr_debug(
"%s(), WriteIdx = %x, data_w_ptr = %p, size_1 = %x\n",
__func__, Afe_Block->pucVirtBufAddr +
Afe_WriteIdx_tmp,
data_w_ptr, size_1);
#endif
if ((copy_from_user((Afe_Block->pucVirtBufAddr +
Afe_WriteIdx_tmp),
data_w_ptr, size_1))) {
#if defined(HDMI_DEBUG_LOG)
pr_warn("HDMI_write Fail 1 copy from user");
#endif
return -1;
}
}
spin_lock_irqsave(&auddrv_hdmi_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(&auddrv_hdmi_lock, flags);
if (!access_ok(VERIFY_READ, data_w_ptr + size_1,
size_2)) {
#if defined(HDMI_DEBUG_LOG)
pr_warn(
"HDMI_write 2ptr invalid data_w_ptr=%p, size_1=%d, size_2=%d u4BufferSize=%d, u4DataRemained=%d",
data_w_ptr, size_1, size_2,
Afe_Block->u4BufferSize,
Afe_Block->u4DataRemained);
#endif
} else {
#if defined(HDMI_DEBUG_LOG)
pr_debug(
"%s(), WriteIdx = %x, data_w_ptr+size_1 = %p, size_2 = %x\n",
__func__, Afe_Block->pucVirtBufAddr +
Afe_WriteIdx_tmp,
data_w_ptr + size_1, size_2);
#endif
if ((copy_from_user((Afe_Block->pucVirtBufAddr +
Afe_WriteIdx_tmp),
(data_w_ptr + size_1),
size_2))) {
#if defined(HDMI_DEBUG_LOG)
pr_warn(
"%s(), Fail 2 copy from user", __func__);
#endif
return -1;
}
}
spin_lock_irqsave(&auddrv_hdmi_lock, flags);
Afe_Block->u4DataRemained += size_2;
Afe_Block->u4WriteIdx = Afe_WriteIdx_tmp + size_2;
Afe_Block->u4WriteIdx %= Afe_Block->u4BufferSize;
spin_unlock_irqrestore(&auddrv_hdmi_lock, flags);
count -= copy_size;
data_w_ptr += copy_size;
#if defined(HDMI_DEBUG_LOG)
pr_debug(
"%s(), finish2, size = %x, WriteIdx = %x, ReadIdx = %x, DataRemained = %x \r\n",
__func__, copy_size, Afe_Block->u4WriteIdx,
Afe_Block->u4DMAReadIdx,
Afe_Block->u4DataRemained);
#endif
}
}
return 0;
/* pr_debug("dummy_pcm_copy return\n"); */
}
static int mtk_pcm_hdmi_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_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_hdmi_ops = {
.open = mtk_pcm_hdmi_open,
.close = mtk_pcm_hdmi_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = mtk_pcm_hdmi_hw_params,
.hw_free = mtk_pcm_hdmi_hw_free,
.prepare = mtk_pcm_hdmi_prepare,
.trigger = mtk_pcm_hdmi_trigger,
.pointer = mtk_pcm_hdmi_pointer,
.copy_user = mtk_pcm_hdmi_copy,
.fill_silence = mtk_pcm_hdmi_silence,
.page = mtk_pcm_page,
};
static const struct snd_soc_component_driver mtk_hdmi_soc_component = {
.name = AFE_PCM_NAME,
.ops = &mtk_hdmi_ops,
.probe = mtk_afe_hdmi_component_probe,
};
static int mtk_hdmi_probe(struct platform_device *pdev)
{
#if defined(HDMI_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_HDMI_PCM);
pdev->name = pdev->dev.kobj.name;
#if defined(HDMI_DEBUG_LOG)
pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
#endif
return snd_soc_register_component(&pdev->dev,
&mtk_hdmi_soc_component,
NULL,
0);
}
static int mtk_afe_hdmi_component_probe(struct snd_soc_component *component)
{
pr_debug("%s\n", __func__);
/* allocate dram */
AudDrv_Allocate_mem_Buffer(component->dev,
Soc_Aud_Digital_Block_MEM_HDMI,
HDMI_MAX_BUFFER_SIZE);
HDMI_dma_buf = Get_Mem_Buffer(Soc_Aud_Digital_Block_MEM_HDMI);
snd_soc_add_component_controls(component, Audio_snd_hdmi_controls,
ARRAY_SIZE(Audio_snd_hdmi_controls));
return 0;
}
static int mtk_afe_remove(struct platform_device *pdev)
{
#if defined(HDMI_DEBUG_LOG)
pr_debug("%s\n", __func__);
#endif
snd_soc_unregister_component(&pdev->dev);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id mt_soc_pcm_hdmi_of_ids[] = {
{
.compatible = "mediatek,mt_soc_pcm_hdmi",
},
{} };
#endif
static struct platform_driver mtk_hdmi_driver = {
.driver = {
.name = MT_SOC_HDMI_PCM,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = mt_soc_pcm_hdmi_of_ids,
#endif
},
.probe = mtk_hdmi_probe,
.remove = mtk_afe_remove,
};
#ifndef CONFIG_OF
static struct platform_device *soc_mtkhdmi_dev;
#endif
static int __init mtk_hdmi_soc_platform_init(void)
{
int ret;
#if defined(HDMI_DEBUG_LOG)
pr_debug("%s\n", __func__);
#endif
#ifndef CONFIG_OF
soc_mtkhdmi_dev = platform_device_alloc(MT_SOC_HDMI_PCM, -1);
if (!soc_mtkhdmi_dev)
return -ENOMEM;
ret = platform_device_add(soc_mtkhdmi_dev);
if (ret != 0) {
platform_device_put(soc_mtkhdmi_dev);
return ret;
}
#endif
ret = platform_driver_register(&mtk_hdmi_driver);
return ret;
}
module_init(mtk_hdmi_soc_platform_init);
static void __exit mtk_hdmi_soc_platform_exit(void)
{
platform_driver_unregister(&mtk_hdmi_driver);
}
module_exit(mtk_hdmi_soc_platform_exit);
/* EXPORT_SYMBOL(Auddrv_Hdmi_Interrupt_Handler); */
MODULE_DESCRIPTION("AFE PCM module platform driver");
MODULE_LICENSE("GPL");