TERES/SOFTWARE/A64-TERES/linux-a64/sound/soc/sunxi/sunxi_hub.c
Dimitar Gamishev f9b0e7a283 linux
2017-10-13 14:07:04 +03:00

197 lines
5.2 KiB
C
Executable File

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/jiffies.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <asm/dma.h>
#include <linux/dma/sunxi-dma.h>
#include "sunxi_dma.h"
#include "sunxi_tdm_utils.h"
#include "sunxi_hub.h"
#include "sunxi_spdif.h"
#include "sunxi_tdm_utils.h"
extern void sndhdmi_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
extern int sndhdmi_perpare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
extern int sndhdmi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,struct snd_soc_dai *dai);
static int sunxi_hubhdmi_hwfree(struct snd_pcm_substream *substream )
{
int reg_val;
struct snd_soc_pcm_runtime *rtd = NULL;
struct snd_soc_dai *cpu_dai = NULL;
struct sunxi_tdm_info *sunxi_tdmhdmi = NULL;
if (!substream) {
pr_err("error:%s,line:%d\n", __func__, __LINE__);
return -EAGAIN;
}
rtd = substream->private_data;
cpu_dai = rtd->cpu_dai;
sunxi_tdmhdmi = snd_soc_dai_get_drvdata(cpu_dai);
reg_val = readl(sunxi_tdmhdmi->regs + SUNXI_DAUDIOFCTL);
reg_val &= ~SUNXI_DAUDIOFCTL_HUBEN;
writel(reg_val, sunxi_tdmhdmi->regs + SUNXI_DAUDIOFCTL);
return 0;
}
static int sunxi_hubhdmi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int ret = 0;
int reg_val;
struct snd_soc_pcm_runtime *rtd = NULL;
struct snd_soc_dai *cpu_dai = NULL;
struct sunxi_tdm_info *sunxi_tdmhdmi = NULL;
u32 freq = 22579200;
unsigned long sample_rate = params_rate(params);
if (!substream) {
pr_err("error:%s,line:%d\n", __func__, __LINE__);
return -EAGAIN;
}
rtd = substream->private_data;
cpu_dai = rtd->cpu_dai;
sunxi_tdmhdmi = snd_soc_dai_get_drvdata(cpu_dai);
sunxi_tdmhdmi->others = 1;/*pcm*/
switch (sample_rate) {
case 8000:
case 16000:
case 32000:
case 64000:
case 128000:
case 12000:
case 24000:
case 48000:
case 96000:
case 192000:
freq = 24576000;
break;
}
/*set system clock source freq and set the mode as i2s0 or pcm*/
ret = snd_soc_dai_set_sysclk(cpu_dai, 0 , freq, 0);
if (ret < 0) {
return ret;
}
/*
* I2S mode \normal bit clock + frame\codec clk & FRM slave
*/
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0) {
return ret;
}
ret = snd_soc_dai_set_clkdiv(cpu_dai, 0, sample_rate);
if (ret < 0) {
return ret;
}
reg_val = readl(sunxi_tdmhdmi->regs + SUNXI_DAUDIOFCTL);
reg_val |= SUNXI_DAUDIOFCTL_HUBEN;
writel(reg_val, sunxi_tdmhdmi->regs + SUNXI_DAUDIOFCTL);
sndhdmi_hw_params(substream,params,NULL);
sndhdmi_perpare(substream,NULL);
return 0;
}
void sunxi_hubhdmi_shutdown(struct snd_pcm_substream *substream)
{
sndhdmi_shutdown(substream,NULL);
}
static struct snd_soc_ops sunxi_hubhdmi_ops = {
.hw_params = sunxi_hubhdmi_hw_params,
.hw_free = sunxi_hubhdmi_hwfree,
.shutdown = sunxi_hubhdmi_shutdown,
};
static int sunxi_hubspdif_hwfree(struct snd_pcm_substream *substream )
{
int reg_val;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sunxi_spdif_info *sunxi_spdif = snd_soc_dai_get_drvdata(cpu_dai);
reg_val = readl(sunxi_spdif->regs + SUNXI_SPDIF_FCTL);
reg_val |= SUNXI_SPDIFFCTL_HUBEN;
writel(reg_val, sunxi_spdif->regs + SUNXI_SPDIF_FCTL);
return 0;
}
static int sunxi_hubspdif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int ret = 0,reg_val = 0;
unsigned long rate = params_rate(params);
struct sunxi_spdif_info *sunxi_spdif = snd_soc_dai_get_drvdata(cpu_dai);
unsigned int fmt = 1;
u32 mclk_div=0, mpll=0, bclk_div=0, mult_fs=0;
get_clock_divder(rate, 32, &mclk_div, &mpll, &bclk_div, &mult_fs);
if (ret < 0)
return ret;
//fmt = 1;
if(fmt > 1){
fmt = 1;
}else{
fmt = 0;
}
ret = snd_soc_dai_set_fmt(cpu_dai, fmt);//0:pcm,1:raw data
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(cpu_dai, 0 , mpll, 0);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_MCLK, mclk_div);
if (ret < 0)
return ret;
reg_val = readl(sunxi_spdif->regs + SUNXI_SPDIF_FCTL);
reg_val |= SUNXI_SPDIFFCTL_HUBEN;
writel(reg_val, sunxi_spdif->regs + SUNXI_SPDIF_FCTL);
return 0;
}
static struct snd_soc_ops sunxi_hubspdif_ops = {
.hw_params = sunxi_hubspdif_hw_params,
.hw_free = sunxi_hubspdif_hwfree,
};
/*dailink for hub*/
struct snd_soc_dai_link sunxi_hub_dai_link[2] = {
{
.name = "hub-hdmi",
.stream_name = "hub-hdmi",
.cpu_dai_name = "1c22800.daudio",
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.ops = &sunxi_hubhdmi_ops,
.ignore_suspend = 1,
},
/**/
{
.name = "hub-spdif",
.stream_name = "hub-spdif",
.cpu_dai_name = "1c21000.spdif-controller",
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.ops = &sunxi_hubspdif_ops,
.ignore_suspend = 1,
},
/**/
};