nt9856x/BSP/linux-kernel/sound/soc/novatek/na51055_soc_plat_l.c
2023-03-28 15:07:53 +08:00

974 lines
26 KiB
C
Executable File

/*
* AMD ALSA SoC PCM Driver for ACP 2.x
*
* Copyright 2014-2015 Advanced Micro Devices, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/sizes.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <sound/soc.h>
#include "nvt_alsa.h"
#include "dai.h"
#include "dai_int.h"
#include "dai_reg.h"
#define DBG_WRN(fmt, args...) //printk(fmt, ##args)
#define ENABLE 1
#define DISABLE 0
/*
Currently the dtsi is not used. Todo in the future
*/
#define IOBASE_DAI 0xF0630000
#define IOIRQ_DAI 51
#define PLAYBACK_MIN_NUM_PERIODS 2
#define PLAYBACK_MAX_NUM_PERIODS 16
#define PLAYBACK_MAX_PERIOD_SIZE 16384
#define PLAYBACK_MIN_PERIOD_SIZE 1024
#define CAPTURE_MIN_NUM_PERIODS 2
#define CAPTURE_MAX_NUM_PERIODS 16
#define CAPTURE_MAX_PERIOD_SIZE 16384
#define CAPTURE_MIN_PERIOD_SIZE 1024
#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
#define MIN_BUFFER MAX_BUFFER
static const struct snd_pcm_hardware nvt_pcm_hardware_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
.period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
.period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
.periods_min = PLAYBACK_MIN_NUM_PERIODS,
.periods_max = PLAYBACK_MAX_NUM_PERIODS,
};
static const struct snd_pcm_hardware nvt_pcm_hardware_capture = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
.periods_min = CAPTURE_MIN_NUM_PERIODS,
.periods_max = CAPTURE_MAX_NUM_PERIODS,
};
struct nvtdai_drv_data {
struct snd_pcm_substream *play_stream;
struct snd_pcm_substream *capture_stream;
void __iomem *dai_mmio;
};
void nvt_set_sample_rate (UINT32 samplerate)
{
/* struct clk *eac_clk, *pll7_clk;
static UINT32 stored_sr = 0;
if(stored_sr != samplerate) {
DBG_WRN("nvt_set_sample_rate %d\n", samplerate);
stored_sr = samplerate;
switch (samplerate) {
case 11025:
case 22050:
case 44100: {
pll7_clk = clk_get(NULL, "pll7");
if (IS_ERR(pll7_clk)) {
printk("get pll7 failed\r\n");
}
clk_set_rate(pll7_clk, 349977600);
} break;
case 8000:
case 12000:
case 16000:
case 24000:
case 32000:
case 48000:
default: {
pll7_clk = clk_get(NULL, "pll7");
if (IS_ERR(pll7_clk)) {
printk("get pll7 failed\r\n");
}
clk_set_rate(pll7_clk, 344064000);
} break;
}
clk_put(pll7_clk);
eac_clk = clk_get(NULL, "f0640000.eacdac");
if (IS_ERR(eac_clk)) {
printk("failed to get eac-da clk\n");
}
clk_set_rate(eac_clk, samplerate*256);
clk_put(eac_clk);
}*/
}
static irqreturn_t dma_irq_handler(int irq, void *arg)
{
T_DAI_INTSTATUS_REG reg_status;
struct nvtdai_drv_data *irq_data;
struct device *dev = arg;
//UINT32 temp = 0;
if(dai_is_txlb_enable() == FALSE)
return IRQ_NONE;
reg_status.reg = DAI_GETREG(DAI_INTSTATUS_REG_OFS) & 0x40000;
if (reg_status.reg == 0) {
return IRQ_NONE;
}
DAI_SETREG(DAI_INTSTATUS_REG_OFS, reg_status.reg);
//printk("2");
irq_data = dev_get_drvdata(dev);
if (reg_status.reg) {
//temp = dai_get_rx_config(DAI_RXCFG_ID_TIMECODE_TRIG)
// + irq_data->capture_stream->runtime->period_size;
//dai_set_txlb_config(DAI_RXCFG_ID_TIMECODE_TRIG, temp);
snd_pcm_period_elapsed(irq_data->capture_stream);
}
return IRQ_HANDLED;
}
static int nvt_dma_open(struct snd_pcm_substream *substream)
{
//u16 bank;
int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *prtd = substream->private_data;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, "alsa_component_l");
struct nvtdai_drv_data *intr_data = dev_get_drvdata(component->dev);
struct audio_substream_data *adata =
kzalloc(sizeof(struct audio_substream_data), GFP_KERNEL);
DBG_WRN("nvt_dma_open\r\n");
if (adata == NULL)
return -ENOMEM;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
runtime->hw = nvt_pcm_hardware_playback;
else
runtime->hw = nvt_pcm_hardware_capture;
ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0) {
dev_err(component->dev, "set integer constraint failed\n");
kfree(adata);
return ret;
}
adata->dai_mmio = intr_data->dai_mmio;
runtime->private_data = adata;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
intr_data->play_stream = substream;
} else {
intr_data->capture_stream = substream;
}
return 0;
}
static int nvt_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int status;
uint64_t size;
struct snd_dma_buffer *dma_buffer;
struct snd_pcm_runtime *runtime;
struct audio_substream_data *rtd;
DBG_WRN("nvt_dma_hw_params\r\n");
dma_buffer = &substream->dma_buffer;
runtime = substream->runtime;
rtd = runtime->private_data;
if (WARN_ON(!rtd))
return -EINVAL;
size = params_buffer_bytes(params);
status = snd_pcm_lib_malloc_pages(substream, size);
return status;
}
static int nvt_dma_hw_free(struct snd_pcm_substream *substream)
{
DBG_WRN("nvt_dma_hw_free\r\n");
return snd_pcm_lib_free_pages(substream);
}
static snd_pcm_uframes_t nvt_dma_pointer(struct snd_pcm_substream *substream)
{
snd_pcm_uframes_t frames;
unsigned long bytes;
struct snd_pcm_runtime *runtime = substream->runtime;
//DBG_WRN("nvt_dma_pointer\r\n");
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
bytes = DAI_GETREG(DAI_TX1DMACURRENT_REG_OFS) - DAI_GETREG(DAI_TX1DMASTART_REG_OFS);
} else {
bytes = DAI_GETREG(DAI_TXLBDMACURRENT_REG_OFS) - DAI_GETREG(DAI_TXLBDMASTART_REG_OFS);
}
frames = bytes_to_frames(runtime, bytes);
if (frames >= runtime->buffer_size)
frames -= runtime->buffer_size;
return frames;
}
static int nvt_dma_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
DBG_WRN("nvt_dma_mmap\r\n");
return snd_pcm_lib_default_mmap(substream, vma);
}
static int nvt_dma_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
//struct audio_substream_data *rtd = runtime->private_data;
DBG_WRN("nvt_dma_prepare\r\n");
DBG_WRN("rt format=%d rate=%d ch=%d frmbits=%d\r\n", (int)runtime->format, (int)runtime->rate, (int)runtime->channels, (int)runtime->frame_bits);
DBG_WRN("rt addr=0x%08X bufsz=%d-frames prdsz=%d-frames\r\n", (int)runtime->dma_addr, (int)runtime->buffer_size, (int)runtime->period_size);
// Set clock rate
nvt_set_sample_rate(runtime->rate);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_PCMLEN, DAI_DRAMPCM_16);
} else if (runtime->format == SNDRV_PCM_FORMAT_S32_LE) {
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_PCMLEN, DAI_DRAMPCM_32);
} else if (runtime->format == SNDRV_PCM_FORMAT_U8) {
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_PCMLEN, DAI_DRAMPCM_8);
}
if (runtime->channels == 2) {
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_DRAMCH, DAI_DRAMPCM_STEREO);
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_CHANNEL, DAI_CH_STEREO);
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_TOTAL_CH, DAI_TOTCH_2CH);
} else if (runtime->channels == 1) {
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_DRAMCH, DAI_DRAMPCM_MONO);
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_CHANNEL, DAI_CH_MONO_LEFT);
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_TOTAL_CH, DAI_TOTCH_2CH);
} else if (runtime->channels == 4) {
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_DRAMCH, DAI_DRAMPCM_STEREO);
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_CHANNEL, DAI_CH_STEREO);
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_TOTAL_CH, DAI_TOTCH_4CH);
} else if (runtime->channels == 6) {
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_DRAMCH, DAI_DRAMPCM_STEREO);
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_CHANNEL, DAI_CH_STEREO);
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_TOTAL_CH, DAI_TOTCH_6CH);
} else if (runtime->channels == 8) {
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_DRAMCH, DAI_DRAMPCM_STEREO);
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_CHANNEL, DAI_CH_STEREO);
dai_set_tx_config(DAI_TXCH_TX1, DAI_TXCFG_ID_TOTAL_CH, DAI_TOTCH_8CH);
} else {
printk(KERN_ERR "%s:bad ch-no %d\n", __func__, runtime->channels);
return -EINVAL;
}
dai_set_tx_dma_para(DAI_TXCH_TX1, (UINT32)runtime->dma_area, frames_to_bytes(runtime, runtime->buffer_size)>>2);
} else {
if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
dai_set_txlb_config(DAI_TXLBCFG_ID_PCMLEN, DAI_DRAMPCM_16);
} else if (runtime->format == SNDRV_PCM_FORMAT_S32_LE) {
dai_set_txlb_config(DAI_TXLBCFG_ID_PCMLEN, DAI_DRAMPCM_32);
} else if (runtime->format == SNDRV_PCM_FORMAT_U8) {
dai_set_txlb_config(DAI_TXLBCFG_ID_PCMLEN, DAI_DRAMPCM_8);
}
if (runtime->channels == 2) {
dai_set_txlb_config(DAI_TXLBCFG_ID_DRAMCH, DAI_DRAMPCM_STEREO);
dai_set_txlb_config(DAI_TXLBCFG_ID_CHANNEL, DAI_CH_STEREO);
dai_set_txlb_config(DAI_TXLBCFG_ID_TOTAL_CH, DAI_TOTCH_2CH);
} else if (runtime->channels == 1) {
dai_set_txlb_config(DAI_TXLBCFG_ID_DRAMCH, DAI_DRAMPCM_MONO);
dai_set_txlb_config(DAI_TXLBCFG_ID_CHANNEL, DAI_CH_MONO_LEFT);
dai_set_txlb_config(DAI_TXLBCFG_ID_TOTAL_CH, DAI_TOTCH_2CH);
} else if (runtime->channels == 4) {
dai_set_txlb_config(DAI_TXLBCFG_ID_DRAMCH, DAI_DRAMPCM_STEREO);
dai_set_txlb_config(DAI_TXLBCFG_ID_CHANNEL, DAI_CH_STEREO);
dai_set_txlb_config(DAI_TXLBCFG_ID_TOTAL_CH, DAI_TOTCH_4CH);
} else if (runtime->channels == 6) {
dai_set_txlb_config(DAI_TXLBCFG_ID_DRAMCH, DAI_DRAMPCM_STEREO);
dai_set_txlb_config(DAI_TXLBCFG_ID_CHANNEL, DAI_CH_STEREO);
dai_set_txlb_config(DAI_TXLBCFG_ID_TOTAL_CH, DAI_TOTCH_6CH);
} else if (runtime->channels == 8) {
dai_set_txlb_config(DAI_TXLBCFG_ID_DRAMCH, DAI_DRAMPCM_STEREO);
dai_set_txlb_config(DAI_TXLBCFG_ID_CHANNEL, DAI_CH_STEREO);
dai_set_txlb_config(DAI_TXLBCFG_ID_TOTAL_CH, DAI_TOTCH_8CH);
} else {
printk(KERN_ERR "%s:bad ch-no %d\n", __func__, runtime->channels);
return -EINVAL;
}
dai_set_txlb_dma_para((UINT32)runtime->dma_area, frames_to_bytes(runtime, runtime->buffer_size)>>2);
}
return 0;
}
static int nvt_dma_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret;
//u32 loops = 1000;
struct snd_pcm_runtime *runtime = substream->runtime;
//struct snd_soc_pcm_runtime *prtd = substream->private_data;
struct audio_substream_data *rtd = runtime->private_data;
DBG_WRN("nvt_dma_trigger %d\r\n", cmd);
if (!rtd)
return -EINVAL;
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dai_enable_tx(DAI_TXCH_TX1, ENABLE);
} else {
dai_enable_txlb(ENABLE);
}
ret = 0;
break;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dai_set_tx_config(0, DAI_TXCFG_ID_TIMECODE_OFS, 0);
dai_set_tx_config(0, DAI_TXCFG_ID_TIMECODE_TRIG, runtime->period_size);
dai_set_config(DAI_CONFIG_ID_SET_INTEN, DAI_TX1TCHIT_INT);
dai_enable_tx_dma(DAI_TXCH_TX1, ENABLE);
udelay(2);
dai_enable_tx(DAI_TXCH_TX1, ENABLE);
} else {
//******************************************************************************
//dai_set_txlb_config(DAI_RXCFG_ID_TIMECODE_OFS, 0);
//dai_set_txlb_config(DAI_RXCFG_ID_TIMECODE_TRIG, runtime->period_size);
//dai_set_config(DAI_CONFIG_ID_SET_INTEN, DAI_TXLBDMADONE_INT);
dai_enable_txlb_dma(ENABLE);
udelay(2);
dai_enable_txlb(ENABLE);
}
ret = 0;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dai_enable_tx(DAI_TXCH_TX1, DISABLE);
} else {
dai_enable_txlb(DISABLE);
}
ret = 0;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dai_enable_tx(DAI_TXCH_TX1, DISABLE);
udelay(2);
dai_enable_tx_dma(DAI_TXCH_TX1, DISABLE);
dai_set_config(DAI_CONFIG_ID_CLR_INTEN, DAI_TX1TCHIT_INT);
} else {
dai_enable_txlb(DISABLE);
udelay(2);
dai_enable_txlb_dma(DISABLE);
//******************************************************************************
//dai_set_config(DAI_CONFIG_ID_CLR_INTEN, DAI_TXLBDMADONE_INT);
}
ret = 0;
break;
default:
ret = -EINVAL;
}
return ret;
}
static int nvt_dma_new(struct snd_soc_pcm_runtime *rtd)
{
DBG_WRN("nvt_dma_new\r\n");
return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
SNDRV_DMA_TYPE_DEV, NULL, MIN_BUFFER, MAX_BUFFER);
}
static int nvt_dma_close(struct snd_pcm_substream *substream)
{
//u16 bank;
struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data;
struct snd_soc_pcm_runtime *prtd = substream->private_data;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, "alsa_component_l");
struct nvtdai_drv_data *adata = dev_get_drvdata(component->dev);
kfree(rtd);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
adata->play_stream = NULL;
} else {
adata->capture_stream = NULL;
}
DBG_WRN("nvt_dma_close\r\n");
return 0;
}
#if 1
/*static const struct snd_soc_component_driver fake_component = {
.name = "alsa_component",
};*/
/*
static int nvt_cpudai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
DBG_WRN("nvt_cpudai_prepare\n");
return 0;
}
static int nvt_cpudai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{
DBG_WRN("nvt_cpudai_trigger\n");
return 0;
}
static int nvt_cpudai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
//struct platform_device *pdev = to_platform_device(dai->dev);
DBG_WRN("nvt_cpudai_startup\n");
return 0;
}
static void nvt_cpudai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
//struct platform_device *pdev = to_platform_device(dai->dev);
DBG_WRN("nvt_cpudai_shutdown\n");
}
static int nvt_cpudai_set_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
DBG_WRN("nvt_cpudai_set_clkdiv\n");
return 0;
}
static int nvt_cpudai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
DBG_WRN("nvt_cpudai_set_sysclk\n");
return 0;
}
static int nvt_cpudai_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
DBG_WRN("nvt_cpudai_set_channel_map\n");
return 0;
}
static int nvt_cpudai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
DBG_WRN("nvt_cpudai_hw_params\n");
#if 0
//////////////////////////////////////////////////////////////////////////
snd_soc_dai_set_sysclk
snd_soc_codec_set_sysclk
snd_soc_dai_set_clkdiv
snd_soc_dai_set_bclk_ratio
// used in the machine driver
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
// set the codec system clock for DAC and ADC
ret = snd_soc_dai_set_sysclk(codec_dai, 0, 24576000, SND_SOC_CLOCK_IN);
// set codec DAI slots, 8 channels, all channels are enabled
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32);
//////////////////////////////////////////////////////////////////////////
#endif
return 0;
}
*/
#if 0
static int nvt_cpudai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask,
int slots, int width)
{
DBG_WRN("nvt_cpudai_set_tdm_slot slot-%d width-%d tm=0x%02X rm=0x%02X\n", slots, width, tx_mask, rx_mask);
if (width == 16) {
dai_set_i2s_config(DAI_I2SCONFIG_ID_CHANNEL_LEN, 0);
} else if (width == 32) {
dai_set_i2s_config(DAI_I2SCONFIG_ID_CHANNEL_LEN, 1);
} else {
printk(KERN_ERR "%s:bad width %d\n", __func__, width);
return -EINVAL;
}
return 0;
}
static int nvt_cpudai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
DBG_WRN("nvt_cpudai_set_dai_fmt 0x%08X\n",fmt);
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
dai_set_config(DAI_CONFIG_ID_EXTCODEC_EN, ENABLE);
dai_set_config(DAI_CONFIG_ID_RX_SRC_SEL, DAI_RX_SRC_I2S);
break;
default:
dai_set_config(DAI_CONFIG_ID_EXTCODEC_EN, DISABLE);
dai_set_config(DAI_CONFIG_ID_RX_SRC_SEL, DAI_RX_SRC_EMBEDDED);
printk(KERN_ERR "%s:bad format\n", __func__);
return -EINVAL;
}
/* Set FCK Invert */
if((fmt & SND_SOC_DAIFMT_INV_MASK) == SND_SOC_DAIFMT_NB_IF) {
dai_set_i2s_config(DAI_I2SCONFIG_ID_CLK_INV, ENABLE);
} else {
dai_set_i2s_config(DAI_I2SCONFIG_ID_CLK_INV, DISABLE);
}
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
/* cpu is master */
dai_set_i2s_config(DAI_I2SCONFIG_ID_OPMODE, DAI_OP_MASTER);
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* codec is master */
dai_set_i2s_config(DAI_I2SCONFIG_ID_OPMODE, DAI_OP_SLAVE);
break;
default:
printk(KERN_ERR "%s:bad master\n", __func__);
return -EINVAL;
}
return 0;
}
static int nvt_cpudai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
DBG_WRN("nvt_cpudai_set_bclk_ratio %d\n",ratio);
if (ratio == 32) {
dai_set_i2s_config(DAI_I2SCONFIG_ID_CLKRATIO, DAI_I2SCLKR_256FS_32BIT);
} else if (ratio == 64) {
dai_set_i2s_config(DAI_I2SCONFIG_ID_CLKRATIO, DAI_I2SCLKR_256FS_64BIT);
} else if (ratio == 128) {
dai_set_i2s_config(DAI_I2SCONFIG_ID_CLKRATIO, DAI_I2SCLKR_256FS_128BIT);
} else if (ratio == 256) {
dai_set_i2s_config(DAI_I2SCONFIG_ID_CLKRATIO, DAI_I2SCLKR_256FS_256BIT);
}
return 0;
}
#endif
/*
*/
static const struct snd_soc_dai_ops nvt_cpudai_ops = {
//.prepare = nvt_cpudai_prepare,
//.startup = nvt_cpudai_startup,
//.shutdown = nvt_cpudai_shutdown,
//.trigger = nvt_cpudai_trigger,
//.hw_params = nvt_cpudai_hw_params,
//.set_clkdiv = nvt_cpudai_set_clkdiv,
//.set_sysclk = nvt_cpudai_set_sysclk,
//.set_channel_map = nvt_cpudai_set_channel_map,
//.set_tdm_slot = nvt_cpudai_set_tdm_slot,
//.set_fmt = nvt_cpudai_set_dai_fmt,
//.set_bclk_ratio = nvt_cpudai_set_bclk_ratio,
};
static struct snd_soc_dai_driver nvt_platform_cpudai[] = {
{
.name = "nvt,cpu_dai_l",
.id = 0,
/*
.playback = {
.stream_name = "cpudai-play",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
},
*/
.capture = {
.stream_name = "cpudai-loopback",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &nvt_cpudai_ops,
},
#if 1
// When the component of dai = 1, dai name depends on "device_name"."device_id"
// if the compenent of dai > 1, dai name used dai_drv->name
// To fit this logic in soc driver, add one more define(not used)
{
.name = "nvt-cpu-dai.1",
.id = 1,
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,// | SNDRV_PCM_FMTBIT_S32_LE,
},
},
#endif
};
#endif
static const struct snd_pcm_ops nvt_dma_ops = {
.open = nvt_dma_open,
.close = nvt_dma_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = nvt_dma_hw_params,
.hw_free = nvt_dma_hw_free,
.trigger = nvt_dma_trigger,
.pointer = nvt_dma_pointer,
.mmap = nvt_dma_mmap,
.prepare = nvt_dma_prepare,
};
static struct snd_soc_component_driver nvt_asoc_component = {
.name = "alsa_component_l",
.ops = &nvt_dma_ops,
.pcm_new = nvt_dma_new,
};
static int nvt_audio_probe(struct platform_device *pdev)
{
int status;
struct nvtdai_drv_data *nvtdai_drv_data;
//struct clk *dai_clk;//, *eac_clk;
//struct resource *res;
nvtdai_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct nvtdai_drv_data),
GFP_KERNEL);
if (nvtdai_drv_data == NULL)
return -ENOMEM;
//res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//nvtdai_drv_data->dai_mmio = devm_ioremap_resource(&pdev->dev, res);
nvtdai_drv_data->dai_mmio = ioremap_nocache(IOBASE_DAI, 0x100);
_DAI_REG_BASE_ADDR[0] = (UINT32)nvtdai_drv_data->dai_mmio;
//dai_clk = clk_get(NULL, "f0630000.dai");
//if (IS_ERR(dai_clk)) {
// printk("failed to get dai clk\n");
//}
// reset controller
//clk_prepare(dai_clk);
//clk_put(dai_clk);
nvt_set_sample_rate(48000);
nvtdai_drv_data->play_stream = NULL;
nvtdai_drv_data->capture_stream = NULL;
//res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
//if (!res) {
// dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
// return -ENODEV;
//}
//dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED %d\n",res->start);
status = devm_request_irq(&pdev->dev, IOIRQ_DAI, dma_irq_handler,
IRQF_SHARED | IRQF_TRIGGER_HIGH, "DAI_IRQ", &pdev->dev);
if (status) {
dev_err(&pdev->dev, "DAI IRQ request failed\n");
return status;
}
dev_set_drvdata(&pdev->dev, nvtdai_drv_data);
/* Initialize the ACP */
//acp_init(nvtdai_drv_data->dai_mmio);
/*status = snd_soc_register_platform(&pdev->dev, &nvt_asoc_platform);
if (status != 0) {
dev_err(&pdev->dev, "Fail to register ALSA platform device\n");
return status;
}*/
status = snd_soc_register_component(&pdev->dev, &nvt_asoc_component, nvt_platform_cpudai, ARRAY_SIZE(nvt_platform_cpudai));
if (status)
{
dev_err(&pdev->dev, "snd_soc_register_component failed(%d)!\n", status);
return status;
}
dai_open(NULL);
dai_enable_dai(ENABLE);
/*
Not used first
*/
//pm_runtime_set_autosuspend_delay(&pdev->dev, 10000);
//pm_runtime_use_autosuspend(&pdev->dev);
//pm_runtime_enable(&pdev->dev);
DBG_WRN("nvt_audio_probe\r\n");
return status;
}
static int nvt_audio_remove(struct platform_device *pdev)
{
//struct nvtdai_drv_data *adata = dev_get_drvdata(&pdev->dev);
//acp_deinit(adata->dai_mmio);
snd_soc_unregister_component(&pdev->dev);
//pm_runtime_disable(&pdev->dev);
//dai_enable_dai(DISABLE);
//dai_close();
DBG_WRN("nvt_audio_remove\r\n");
return 0;
}
static int nvt_pcm_resume(struct device *dev)
{
/*
u16 bank;
struct nvtdai_drv_data *adata = dev_get_drvdata(dev);
acp_init(adata->dai_mmio);
if (adata->play_stream && adata->play_stream->runtime) {
for (bank = 1; bank <= 4; bank++)
acp_set_sram_bank_state(adata->dai_mmio, bank,
true);
config_acp_dma(adata->dai_mmio,
adata->play_stream->runtime->private_data);
}
if (adata->capture_stream && adata->capture_stream->runtime) {
for (bank = 5; bank <= 8; bank++)
acp_set_sram_bank_state(adata->dai_mmio, bank,
true);
config_acp_dma(adata->dai_mmio,
adata->capture_stream->runtime->private_data);
}
acp_reg_write(1, adata->dai_mmio, mmACP_EXTERNAL_INTR_ENB);
*/
DBG_WRN("nvt_pcm_resume\r\n");
return 0;
}
static int nvt_pcm_runtime_suspend(struct device *dev)
{
//struct nvtdai_drv_data *adata = dev_get_drvdata(dev);
//acp_deinit(adata->dai_mmio);
//acp_reg_write(0, adata->dai_mmio, mmACP_EXTERNAL_INTR_ENB);
DBG_WRN("nvt_pcm_runtime_suspend\r\n");
return 0;
}
static int nvt_pcm_runtime_resume(struct device *dev)
{
//struct nvtdai_drv_data *adata = dev_get_drvdata(dev);
//acp_init(adata->dai_mmio);
//acp_reg_write(1, adata->dai_mmio, mmACP_EXTERNAL_INTR_ENB);
DBG_WRN("nvt_pcm_runtime_resume\r\n");
return 0;
}
static const struct dev_pm_ops nvt_pm_ops = {
.resume = nvt_pcm_resume,
.runtime_suspend = nvt_pcm_runtime_suspend,
.runtime_resume = nvt_pcm_runtime_resume,
};
//#ifdef CONFIG_OF
//static const struct of_device_id of_nvtplat_match[] = {
// {
// .compatible = "nvt,alsa_plat"
// },
// { },
//};
//MODULE_DEVICE_TABLE(of, of_nvtplat_match);
//#endif
static struct platform_driver nvt_dma_driver = {
.probe = nvt_audio_probe,
.remove = nvt_audio_remove,
.driver = {
.name = "nvt,alsa_plat_l",
.pm = &nvt_pm_ops,
//#ifdef CONFIG_OF
// .of_match_table = of_match_ptr(of_nvtplat_match),
//#endif
},
};
static struct platform_device *nvt_pdev;
int __init nvt_alsapcm_module_init(void)
{
int iRet;
struct platform_device *pdev;
DBG_WRN("nvt_alsapcm_module_init\r\n");
iRet = platform_driver_register(&nvt_dma_driver);
if (iRet) {
printk("nvt_alsapcm_module_init platform_driver_register error\n");
}
pdev = platform_device_alloc("nvt,alsa_plat_l",-1);
if (!pdev)
{
printk("platform_device_alloc failed!!\n");
}
iRet = platform_device_add(pdev);
if (iRet)
{
platform_device_put(pdev);
printk( "platform_device_add failed(%d)! \n", iRet);
}
nvt_pdev = pdev;
return 0;
}
void __exit nvt_alsapcm_module_exit(void)
{
DBG_WRN("nvt_alsapcm_module_exit\r\n");
platform_device_unregister(nvt_pdev);
platform_driver_unregister(&nvt_dma_driver);
}
module_init(nvt_alsapcm_module_init);
module_exit(nvt_alsapcm_module_exit);
//module_platform_driver(nvt_dma_driver);
MODULE_AUTHOR("klins_chen@novatek.com.tw");
MODULE_DESCRIPTION("NOVATEK Audio PCM Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:nvt-dma-audio");