1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-11-29 06:53:44 +02:00
openwrt-xburst/target/linux/xburst/files-2.6.31/sound/soc/codecs/jzdlv.c

982 lines
23 KiB
C
Raw Normal View History

2009-08-20 12:38:17 +03:00
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "../jz4750/jz4750-pcm.h"
#include "jzdlv.h"
#define AUDIO_NAME "jzdlv"
#define JZDLV_VERSION "1.0"
/*
* Debug
*/
#define JZDLV_DEBUG 0
#ifdef JZDLV_DEBUG
#define dbg(format, arg...) \
printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
#else
#define dbg(format, arg...) do {} while (0)
#endif
#define err(format, arg...) \
printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
#define info(format, arg...) \
printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
#define warn(format, arg...) \
printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
struct snd_soc_codec_device soc_codec_dev_jzdlv;
/* codec private data */
struct jzdlv_priv {
unsigned int sysclk;
};
/*
* jzdlv register cache
*/
static u16 jzdlv_reg[JZDLV_CACHEREGNUM];
int read_codec_file(int addr)
{
while (__icdc_rgwr_ready());
__icdc_set_addr(addr);
mdelay(1);
return(__icdc_get_value());
}
static void printk_codec_files(void)
{
int cnt, val;
//printk("\n");
#if 0
printk("REG_CPM_I2SCDR=0x%08x\n",REG_CPM_I2SCDR);
printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR);
printk("REG_CPM_CPCCR=0x%08x\n",REG_CPM_CPCCR);
printk("REG_AIC_FR=0x%08x\n",REG_AIC_FR);
printk("REG_AIC_CR=0x%08x\n",REG_AIC_CR);
printk("REG_AIC_I2SCR=0x%08x\n",REG_AIC_I2SCR);
printk("REG_AIC_SR=0x%08x\n",REG_AIC_SR);
printk("REG_ICDC_RGDATA=0x%08x\n",REG_ICDC_RGDATA);
#endif
for (cnt = 0; cnt < JZDLV_CACHEREGNUM ; cnt++) {
val = read_codec_file(cnt);
jzdlv_reg[cnt] = val;
//printk(" ( %d : 0x%x ) ",cnt ,jzdlv_reg[cnt]);
}
//printk("\n");
}
void write_codec_file(int addr, int val)
{
while (__icdc_rgwr_ready());
__icdc_set_addr(addr);
__icdc_set_cmd(val); /* write */
mdelay(1);
__icdc_set_rgwr();
mdelay(1);
//jzdlv_reg[addr] = val;
}
int write_codec_file_bit(int addr, int bitval, int mask_bit)
{
int val;
while (__icdc_rgwr_ready());
__icdc_set_addr(addr);
mdelay(1);
val = __icdc_get_value(); /* read */
val &= ~(1 << mask_bit);
if (bitval == 1)
val |= 1 << mask_bit;
#if 0
while (__icdc_rgwr_ready());
__icdc_set_addr(addr);
__icdc_set_cmd(val); /* write */
mdelay(1);
__icdc_set_rgwr();
mdelay(1);
#else
write_codec_file(addr, val);
#endif
while (__icdc_rgwr_ready());
__icdc_set_addr(addr);
val = __icdc_get_value(); /* read */
if (((val >> mask_bit) & bitval) == bitval)
return 1;
else
return 0;
}
/*
* read jzdlv register cache
*/
static inline unsigned int jzdlv_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
if (reg >= JZDLV_CACHEREGNUM)
return -1;
return cache[reg];
}
static inline unsigned int jzdlv_read(struct snd_soc_codec *codec,
unsigned int reg)
{
u8 data;
data = reg;
if (codec->hw_write(codec->control_data, &data, 1) != 1)
return -EIO;
if (codec->hw_read(codec->control_data, &data, 1) != 1)
return -EIO;
return data;
}
/*
* write jzdlv register cache
*/
static inline void jzdlv_write_reg_cache(struct snd_soc_codec *codec,
unsigned int reg, u16 value)
{
u16 *cache = codec->reg_cache;
if (reg >= JZDLV_CACHEREGNUM) {
return;
}
cache[reg] = value;
}
/*
* write to the jzdlv register space
*/
static int jzdlv_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
jzdlv_write_reg_cache(codec, reg, value);
if(codec->hw_write)
codec->hw_write(&value, NULL, reg);
return 0;
}
static const char *jzdlv_input_select[] = {"Line In", "Mic"};
static const char *jzdlv_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
static const struct soc_enum jzdlv_enum[] = {
SOC_ENUM_SINGLE(0x04, 2, 2, jzdlv_input_select),
SOC_ENUM_SINGLE(0x05, 1, 4, jzdlv_deemph),
};
/* set Audio data replay */
void set_audio_data_replay(void)
{
write_codec_file(9, 0xff);
write_codec_file(8, 0x20);// only CCMC
mdelay(10);
/* DAC path */
write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
mdelay(100);
write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
mdelay(100);
write_codec_file_bit(1, 0, 5);//DAC_MUTE->0
}
/* unset Audio data replay */
void unset_audio_data_replay(void)
{
write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
mdelay(200);
write_codec_file_bit(5, 1, 6);//SB_OUT->1
write_codec_file_bit(5, 1, 7);//SB_DAC->1
write_codec_file_bit(5, 1, 4);//SB_MIX->1
write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
write_codec_file_bit(6, 1, 1);//SB->1
write_codec_file(9, 0xff);
write_codec_file(8, 0x3f);
}
/* set Record MIC input audio without playback */
static void set_record_mic_input_audio_without_playback(void)
{
/* ADC path for MIC IN */
write_codec_file_bit(1, 1, 2);
write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
//write_codec_file_bit(1, 1, 6);//CR1.MONO->1
write_codec_file(22, 0x40);//mic 1
write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1
write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
write_codec_file_bit(6, 1, 3);// gain set
write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
mdelay(100);
write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
write_codec_file(1, 0x4);
}
/* unset Record MIC input audio without playback */
static void unset_record_mic_input_audio_without_playback(void)
{
/* ADC path for MIC IN */
write_codec_file_bit(5, 1, 4);//SB_ADC->1
write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
write_codec_file(22, 0xc0);//CR3.SB_MIC1
}
#if 0
static irqreturn_t aic_codec_irq(int irq, void *dev_id)
{
u8 file_9 = read_codec_file(9);
u8 file_8 = read_codec_file(8);
//printk("--- 8:0x%x 9:0x%x ---\n",file_8,file_9);
if ((file_9 & 0x1f) == 0x10) {
// have hp short circuit
write_codec_file(8, 0x3f);//mask all interrupt
write_codec_file_bit(5, 1, 6);//SB_OUT->1
mdelay(300);
while ((read_codec_file(9) & 0x4) != 0x4);
while ((read_codec_file(9) & 0x10) == 0x10) {
write_codec_file(9, 0x10);
}
write_codec_file_bit(5, 0, 6);//SB_OUT->0
mdelay(300);
while ((read_codec_file(9) & 0x8) != 0x8);
write_codec_file(9, file_9);
write_codec_file(8, file_8);
return IRQ_HANDLED;
}
if (file_9 & 0x8)
ramp_up_end = jiffies;
else if (file_9 & 0x4)
ramp_down_end = jiffies;
else if (file_9 & 0x2)
gain_up_end = jiffies;
else if (file_9 & 0x1)
gain_down_end = jiffies;
write_codec_file(9, file_9);
if (file_9 & 0xf)
wake_up(&pop_wait_queue);
while (REG_ICDC_RGDATA & 0x100);
return IRQ_HANDLED;
}
#else
static irqreturn_t aic_codec_irq(int irq, void *dev_id)
{
u8 file_9 = read_codec_file(9);
u8 file_8 = read_codec_file(8);
//printk("--- 1 8:0x%x 9:0x%x ---\n",file_8,file_9);
if ((file_9 & 0x1f) == 0x10) {
write_codec_file(8, 0x3f);
write_codec_file_bit(5, 1, 6);//SB_OUT->1
mdelay(300);
while ((read_codec_file(9) & 0x4) != 0x4);
while ((read_codec_file(9) & 0x10) == 0x10) {
write_codec_file(9, 0x10);
}
write_codec_file_bit(5, 0, 6);//SB_OUT->0
mdelay(300);
while ((read_codec_file(9) & 0x8) != 0x8);
write_codec_file(9, file_9);
write_codec_file(8, file_8);
return IRQ_HANDLED;
}
/*if (file_9 & 0x8)
ramp_up_end = jiffies;
else if (file_9 & 0x4)
ramp_down_end = jiffies;
else if (file_9 & 0x2)
gain_up_end = jiffies;
else if (file_9 & 0x1)
gain_down_end = jiffies;*/
write_codec_file(9, file_9);
/*if (file_9 & 0xf)
wake_up(&pop_wait_queue);*/
while (REG_ICDC_RGDATA & 0x100);
return IRQ_HANDLED;
}
#endif
static int jzdlv_reset(struct snd_soc_codec *codec)
{
/* reset DLV codec. from hibernate mode to sleep mode */
write_codec_file(0, 0xf);
write_codec_file_bit(6, 0, 0);
write_codec_file_bit(6, 0, 1);
mdelay(200);
//write_codec_file(0, 0xf);
write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
write_codec_file_bit(5, 0, 4);//PMR1.SB_ADC->0
mdelay(10);//wait for stability
return 0;
}
#if 0
static int jzdlv_sync(struct snd_soc_codec *codec)
{
u16 *cache = codec->reg_cache;
int i, r = 0;
for (i = 0; i < JZDLV_CACHEREGNUM; i++)
r |= jzdlv_write(codec, i, cache[i]);
return r;
};
#endif
static const struct snd_kcontrol_new jzdlv_snd_controls[] = {
//SOC_DOUBLE_R("Master Playback Volume", 1, 1, 0, 3, 0),
SOC_DOUBLE_R("Master Playback Volume", DLV_CGR8, DLV_CGR9, 0, 31, 0),
//SOC_DOUBLE_R("MICBG", ICODEC_2_LOW, ICODEC_2_LOW, 4, 3, 0),
//SOC_DOUBLE_R("Line", 2, 2, 0, 31, 0),
SOC_DOUBLE_R("Line", DLV_CGR10, DLV_CGR10, 0, 15, 0),
};
/* add non dapm controls */
static int jzdlv_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(jzdlv_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&jzdlv_snd_controls[i], codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* Output Mixer */
static const struct snd_kcontrol_new jzdlv_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", 0x04, 3, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", 0x04, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", 0x04, 4, 1, 0),
};
/* Input mux */
static const struct snd_kcontrol_new jzdlv_input_mux_controls =
SOC_DAPM_ENUM("Input Select", jzdlv_enum[0]);
static const struct snd_soc_dapm_widget jzdlv_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Output Mixer", 0x06, 4, 1,
&jzdlv_output_mixer_controls[0],
ARRAY_SIZE(jzdlv_output_mixer_controls)),
//SND_SOC_DAPM_DAC("DAC", "HiFi Playback", 0x06, 3, 1),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
//SND_SOC_DAPM_ADC("ADC", "HiFi Capture", 0x06, 2, 1),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &jzdlv_input_mux_controls),
SND_SOC_DAPM_PGA("Line Input", 0x06, 0, 1, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", 0x06, 1, 1),
SND_SOC_DAPM_INPUT("MICIN"),
SND_SOC_DAPM_INPUT("RLINEIN"),
SND_SOC_DAPM_INPUT("LLINEIN"),
};
static const struct snd_soc_dapm_route intercon[] = {
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
/* outputs */
{"RHPOUT", NULL, "Output Mixer"},
{"ROUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
{"LOUT", NULL, "Output Mixer"},
/* input mux */
{"Input Mux", "Line In", "Line Input"},
{"Input Mux", "Mic", "Mic Bias"},
{"ADC", NULL, "Input Mux"},
/* inputs */
{"Line Input", NULL, "LLINEIN"},
{"Line Input", NULL, "RLINEIN"},
{"Mic Bias", NULL, "MICIN"},
};
static void init_codec(void)
{
/* reset DLV codec. from hibernate mode to sleep mode */
write_codec_file(0, 0xf);
write_codec_file_bit(6, 0, 0);
write_codec_file_bit(6, 0, 1);
mdelay(200);
//write_codec_file(0, 0xf);
write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
write_codec_file_bit(5, 0, 4);//PMR1.SB_ADC->0
mdelay(10);//wait for stability
}
static int jzdlv_add_widgets(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(codec, jzdlv_dapm_widgets,
ARRAY_SIZE(jzdlv_dapm_widgets));
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
}
static int jzdlv_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_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
int speed = 0;
int val = 0;
/* sample channel */
switch (params_channels(params)) {
case 1:
write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono
break;
case 2:
write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo
break;
}
/* sample rate */
switch (params_rate(params)) {
case 8000:
speed = 10;
break;
case 9600:
speed = 9;
break;
case 11025:
speed = 8;
break;
case 12000:
speed = 7;
break;
case 16000:
speed = 6;
break;
case 22050:
speed = 5;
break;
case 24000:
speed = 4;
break;
case 32000:
speed = 3;
break;
case 44100:
speed = 2;
break;
case 48000:
speed = 1;
break;
case 96000:
speed = 0;
break;
default:
printk(" invalid rate :0x%08x\n",params_rate(params));
}
val = (speed << 4) | speed;
jzdlv_write(codec, DLV_CCR2, val);
return 0;
}
static int jzdlv_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
//struct snd_soc_pcm_runtime *rtd = substream->private_data;
//struct snd_soc_device *socdev = rtd->socdev;
//struct snd_soc_codec *codec = socdev->codec;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
//case SNDRV_PCM_TRIGGER_RESUME:
//case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
init_codec();
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
REG_AIC_I2SCR = 0x10;
mdelay(1);
set_audio_data_replay();
mdelay(5);
write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
__aic_flush_fifo();
} else {
set_record_mic_input_audio_without_playback();
mdelay(10);
REG_AIC_I2SCR = 0x10;
mdelay(20);
__aic_flush_fifo();
write_codec_file_bit(5, 1, 7);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
//case SNDRV_PCM_TRIGGER_SUSPEND:
//case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
unset_audio_data_replay();
} else {
unset_record_mic_input_audio_without_playback();
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static int jzdlv_pcm_prepare(struct snd_pcm_substream *substream)
{
/*struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec; */
return 0;
}
static void jzdlv_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
/* deactivate */
if (!codec->active) {
udelay(50);
}
}
static int jzdlv_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 reg_val = jzdlv_read_reg_cache(codec, 2/*DLV_1_LOW*/);
if (mute != 0)
mute = 1;
if (mute)
reg_val = reg_val | (0x1 << 14);
else
reg_val = reg_val & ~(0x1 << 14);
//jzdlv_write(codec, DLV_1_LOW, reg_val);
return 0;
}
static int jzdlv_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct jzdlv_priv *jzdlv = codec->private_data;
jzdlv->sysclk = freq;
return 0;
}
/*
* Set's ADC and Voice DAC format. called by apus_hw_params() in apus.c
*/
static int jzdlv_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
/* struct snd_soc_codec *codec = codec_dai->codec; */
/* set master/slave audio interface. codec side */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
/* set master mode for codec */
break;
case SND_SOC_DAIFMT_CBS_CFS:
/* set slave mode for codec */
break;
default:
return -EINVAL;
}
/* interface format . set some parameter for codec side */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* set I2S mode for codec */
break;
case SND_SOC_DAIFMT_RIGHT_J:
/* set right J mode */
break;
case SND_SOC_DAIFMT_LEFT_J:
/* set left J mode */
break;
case SND_SOC_DAIFMT_DSP_A:
/* set dsp A mode */
break;
case SND_SOC_DAIFMT_DSP_B:
/* set dsp B mode */
break;
default:
return -EINVAL;
}
/* clock inversion. codec side */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_IF:
break;
case SND_SOC_DAIFMT_IB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
break;
default:
return -EINVAL;
}
/* jzcodec_write(codec, 0, val); */
return 0;
}
static int jzdlv_dapm_event(struct snd_soc_codec *codec, int event)
{
/* u16 reg_val; */
switch (event) {
case SNDRV_CTL_POWER_D0: /* full On */
/* vref/mid, osc on, dac unmute */
/* u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW); */
/* jzcodec_write(codec, 0, val); */
break;
case SNDRV_CTL_POWER_D1: /* partial On */
case SNDRV_CTL_POWER_D2: /* partial On */
break;
case SNDRV_CTL_POWER_D3hot: /* Off, with power */
/* everything off except vref/vmid, */
/*reg_val = 0x0800;
jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x0017;
jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
REG_ICDC_CDCCR1 = jzcodec_reg[0];
mdelay(2);
reg_val = 0x2102;
jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x001f;
jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
REG_ICDC_CDCCR1 = jzcodec_reg[0];
mdelay(2);
reg_val = 0x3302;
jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x0003;
jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
REG_ICDC_CDCCR1 = jzcodec_reg[0];*/
break;
case SNDRV_CTL_POWER_D3cold: /* Off, without power */
/* everything off, dac mute, inactive */
/*reg_val = 0x2302;
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x001b;
jzcodec_write(codec, ICODEC_1_HIGH, reg_val);
mdelay(1);
reg_val = 0x2102;
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x001b;
jzcodec_write(codec, ICODEC_1_HIGH, reg_val);*/
break;
}
//codec->dapm_state = event;
return 0;
}
#define JZDLV_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_12000 |\
SNDRV_PCM_RATE_24000)
#define JZDLV_FORMATS (SNDRV_PCM_FORMAT_S8 | SNDRV_PCM_FMTBIT_S16_LE)
struct snd_soc_dai jzdlv_dai = {
.name = "JZDLV",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = JZDLV_RATES,
.formats = JZDLV_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = JZDLV_RATES,
.formats = JZDLV_FORMATS,},
.ops = {
.trigger = jzdlv_pcm_trigger,
.prepare = jzdlv_pcm_prepare,
.hw_params = jzdlv_hw_params,
.shutdown = jzdlv_shutdown,
},
.dai_ops = {
.digital_mute = jzdlv_mute,
.set_sysclk = jzdlv_set_dai_sysclk,
.set_fmt = jzdlv_set_dai_fmt,
}
};
EXPORT_SYMBOL_GPL(jzdlv_dai);
#ifdef CONFIG_PM
static int jzdlv_suspend(struct platform_device *pdev, pm_message_t state)
{
#if 0
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
jzcodec_reg_pm[ICODEC_1_LOW] = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
jzcodec_reg_pm[ICODEC_1_HIGH] = jzcodec_read_reg_cache(codec, ICODEC_1_HIGH);
jzcodec_reg_pm[ICODEC_2_LOW] = jzcodec_read_reg_cache(codec, ICODEC_2_LOW);
jzcodec_reg_pm[ICODEC_2_HIGH] = jzcodec_read_reg_cache(codec, ICODEC_2_HIGH);
jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
#endif
return 0;
}
static int jzdlv_resume(struct platform_device *pdev)
{
#if 0
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
u16 reg_val;
jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
reg_val = jzcodec_reg_pm[ICODEC_1_LOW];
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
reg_val = jzcodec_reg_pm[ICODEC_1_HIGH];
jzcodec_write(codec, ICODEC_1_HIGH, reg_val);
reg_val = jzcodec_reg_pm[ICODEC_2_LOW];
jzcodec_write(codec, ICODEC_2_LOW, reg_val);
reg_val = jzcodec_reg_pm[ICODEC_2_HIGH];
jzcodec_write(codec, ICODEC_2_HIGH, reg_val);
jzcodec_dapm_event(codec, codec->suspend_dapm_state);
#endif
return 0;
}
#else
#define jzdlv_suspend NULL
#define jzdlv_resume NULL
#endif
/*
* initialise the JZDLV driver
* register the mixer and dsp interfaces with the kernel
*/
static int jzdlv_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
int ret = 0, retval;
/*REG_CPM_CPCCR &= ~(1 << 31);
REG_CPM_CPCCR &= ~(1 << 30);*/
write_codec_file(0, 0xf);
REG_AIC_I2SCR = 0x10;
__i2s_internal_codec();
__i2s_as_slave();
__i2s_select_i2s();
__aic_select_i2s();
__aic_reset();
mdelay(10);
REG_AIC_I2SCR = 0x10;
mdelay(20);
/* power on DLV */
write_codec_file(8, 0x3f);
write_codec_file(9, 0xff);
mdelay(10);
__cpm_start_idct();
__cpm_start_db();
__cpm_start_me();
__cpm_start_mc();
__cpm_start_ipu();
codec->name = "JZDLV";
codec->owner = THIS_MODULE;
codec->read = jzdlv_read_reg_cache;
codec->write = jzdlv_write;
//codec->dapm_event = jzdlv_dapm_event;
codec->dai = &jzdlv_dai;
codec->num_dai = 1;
codec->reg_cache_size = sizeof(jzdlv_reg);
codec->reg_cache = kmemdup(jzdlv_reg, sizeof(jzdlv_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
jzdlv_reset(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "jzdlv: failed to create pcms\n");
goto pcm_err;
}
/* power on device */
jzdlv_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
jzdlv_add_controls(codec);
jzdlv_add_widgets(codec);
ret = snd_soc_register_card(socdev);
if (ret < 0) {
printk(KERN_ERR "jzcodec: failed to register card\n");
goto card_err;
}
mdelay(10);
REG_AIC_I2SCR = 0x10;
mdelay(20);
/* power on DLV */
write_codec_file(9, 0xff);
write_codec_file(8, 0x3f);
retval = request_irq(IRQ_AIC, aic_codec_irq, IRQF_DISABLED, "aic_codec_irq", NULL);
if (retval) {
printk("Could not get aic codec irq %d\n", IRQ_AIC);
return retval;
}
printk_codec_files();
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;
}
static struct snd_soc_device *jzdlv_socdev;
static int write_codec_reg(u16 * add, char * name, int reg)
{
write_codec_file(reg, *add);
return 0;
}
static int jzdlv_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
struct jzdlv_priv *jzdlv;
int ret = 0;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
jzdlv = kzalloc(sizeof(struct jzdlv_priv), GFP_KERNEL);
if (jzdlv == NULL) {
kfree(codec);
return -ENOMEM;
}
codec->private_data = jzdlv;
socdev->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
jzdlv_socdev = socdev;
/* Add other interfaces here ,no I2C connection */
codec->hw_write = (hw_write_t)write_codec_reg;
//codec->hw_read = (hw_read_t)read_codec_reg;
ret = jzdlv_init(jzdlv_socdev);
if (ret < 0) {
codec = jzdlv_socdev->codec;
err("failed to initialise jzdlv\n");
kfree(codec);
}
return ret;
}
/* power down chip */
static int jzdlv_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
jzdlv_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
kfree(codec->private_data);
kfree(codec);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_jzdlv = {
.probe = jzdlv_probe,
.remove = jzdlv_remove,
.suspend = jzdlv_suspend,
.resume = jzdlv_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_jzdlv);
MODULE_DESCRIPTION("ASoC JZDLV driver");
MODULE_AUTHOR("Richard");
MODULE_LICENSE("GPL");