Merge: ac108 & ac101 as single codec module
This commit is contained in:
parent
fa4b566d9a
commit
18eec1abfa
8 changed files with 715 additions and 647 deletions
5
Makefile
5
Makefile
|
@ -1,12 +1,10 @@
|
|||
snd-soc-wm8960-objs := wm8960.o
|
||||
snd-soc-ac108-objs := ac108.o
|
||||
snd-soc-ac101-objs := ac101.o
|
||||
snd-soc-ac108-objs := ac108.o ac101.o
|
||||
snd-soc-simple-card-objs := simple-card.o
|
||||
|
||||
|
||||
obj-m += snd-soc-wm8960.o
|
||||
obj-m += snd-soc-ac108.o
|
||||
obj-m += snd-soc-ac101.o
|
||||
obj-m += snd-soc-simple-card.o
|
||||
|
||||
|
||||
|
@ -17,7 +15,6 @@ clean:
|
|||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||
|
||||
install:
|
||||
sudo cp snd-soc-ac101.ko /lib/modules/$(shell uname -r)/kernel/sound/soc/codecs/
|
||||
sudo cp snd-soc-ac108.ko /lib/modules/$(shell uname -r)/kernel/sound/soc/codecs/
|
||||
sudo cp snd-soc-wm8960.ko /lib/modules/$(shell uname -r)/kernel/sound/soc/codecs/
|
||||
sudo cp snd-soc-simple-card.ko /lib/modules/$(shell uname -r)/kernel/sound/soc/generic/
|
||||
|
|
317
ac108.c
317
ac108.c
|
@ -25,8 +25,9 @@
|
|||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "ac108.h"
|
||||
#include "ac10x.h"
|
||||
|
||||
#define _USE_CAPTURE 0
|
||||
#define _USE_CAPTURE 1
|
||||
#define _MASTER_INDEX 0
|
||||
|
||||
/**
|
||||
|
@ -46,18 +47,6 @@ struct pll_div {
|
|||
unsigned int k2;
|
||||
};
|
||||
|
||||
struct ac10x_priv {
|
||||
struct i2c_client *i2c[4];
|
||||
int codec_index;
|
||||
int sysclk;
|
||||
unsigned mclk; /* master clock or aif_clock/aclk */
|
||||
int clk_id;
|
||||
unsigned char i2s_mode;
|
||||
unsigned char data_protocol;
|
||||
struct delayed_work dlywork;
|
||||
int trgr_cnt;
|
||||
int tdm_chips_cnt;
|
||||
};
|
||||
static struct ac10x_priv *ac10x;
|
||||
|
||||
struct real_val_to_reg_val {
|
||||
|
@ -165,7 +154,7 @@ static const struct pll_div ac108_pll_div_list[] = {
|
|||
|
||||
|
||||
/* AC108 definition */
|
||||
#define AC108_CHANNELS_MAX 16 /* range[1, 16] */
|
||||
#define AC108_CHANNELS_MAX 8 /* range[1, 16] */
|
||||
#define AC108_RATES (SNDRV_PCM_RATE_8000_96000 & \
|
||||
~(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000 | \
|
||||
SNDRV_PCM_RATE_88200))
|
||||
|
@ -177,46 +166,79 @@ static const struct pll_div ac108_pll_div_list[] = {
|
|||
static const DECLARE_TLV_DB_SCALE(tlv_adc_pga_gain, 0, 100, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(tlv_ch_digital_vol, -11925,75,0);
|
||||
|
||||
static int ac10x_read(u8 reg, u8 *rt_value, struct i2c_client *client) {
|
||||
int ret;
|
||||
int ac10x_read(u8 reg, u8 *rt_value, struct i2c_client *client) {
|
||||
int i = 0, ret;
|
||||
u8 read_cmd[3] = { reg, 0, 0 };
|
||||
u8 cmd_len = 1;
|
||||
|
||||
#if _I2C_MUTEX_EN
|
||||
mutex_lock(&ac10x->i2c_mutex);
|
||||
#endif
|
||||
//__retry:
|
||||
ret = i2c_master_send(client, read_cmd, cmd_len);
|
||||
if (ret != cmd_len) {
|
||||
pr_err("ac10x_read error1\n");
|
||||
return -1;
|
||||
/*
|
||||
if (ret == -EAGAIN && i++ < 5) {
|
||||
usleep_range(1, 100);
|
||||
goto __retry;
|
||||
}
|
||||
*/
|
||||
pr_err("ac10x_read error1 %d tried %d\n", ret, i);
|
||||
ret = -1;goto __ret;
|
||||
}
|
||||
//__retry2:
|
||||
ret = i2c_master_recv(client, rt_value, 1);
|
||||
if (ret != 1) {
|
||||
pr_err("ac10x_read error2, ret = %d.\n", ret);
|
||||
return -1;
|
||||
/*
|
||||
if (ret == -EAGAIN && i++ < 5) {
|
||||
usleep_range(1, 100);
|
||||
goto __retry2;
|
||||
}
|
||||
*/
|
||||
pr_err("ac10x_read error2 %d tried %d.\n", ret, i);
|
||||
ret = -2;goto __ret;
|
||||
}
|
||||
ret = 0;
|
||||
__ret:
|
||||
#if _I2C_MUTEX_EN
|
||||
mutex_unlock(&ac10x->i2c_mutex);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ac10x_write(u8 reg, unsigned char val, struct i2c_client *client) {
|
||||
int ac10x_write(u8 reg, unsigned char val, struct i2c_client *client) {
|
||||
int ret = 0;
|
||||
u8 write_cmd[2] = { reg, val };
|
||||
|
||||
#if _I2C_MUTEX_EN
|
||||
mutex_lock(&ac10x->i2c_mutex);
|
||||
#endif
|
||||
ret = i2c_master_send(client, write_cmd, 2);
|
||||
if (ret != 2) {
|
||||
pr_err("ac10x_write error->[REG-0x%02x,val-0x%02x]\n", reg, val);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
static int ac10x_update_bits(u8 reg, u8 mask, u8 val, struct i2c_client *client) {
|
||||
#if _I2C_MUTEX_EN
|
||||
mutex_unlock(&ac10x->i2c_mutex);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ac10x_update_bits(u8 reg, u8 mask, u8 val, struct i2c_client *client) {
|
||||
u8 val_old, val_new;
|
||||
int ret;
|
||||
|
||||
if ((ret = ac10x_read(reg, &val_old, client)) < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ac10x_read(reg, &val_old, client);
|
||||
val_new = (val_old & ~mask) | (val & mask);
|
||||
if (val_new != val_old) {
|
||||
ac10x_write(reg, val_new, client);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -237,16 +259,13 @@ static int snd_ac108_get_volsw(struct snd_kcontrol *kcontrol,
|
|||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
unsigned int mask = (1 << fls(mc->max)) - 1;
|
||||
unsigned int invert = mc->invert;
|
||||
int chip = mc->autodisable;
|
||||
int ret, chip = mc->autodisable;
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = ac10x_read(mc->reg, &val, ac10x->i2c[chip]);
|
||||
if (ret < 0)
|
||||
if ((ret = ac10x_read(mc->reg, &val, ac10x->i2c[chip])) < 0)
|
||||
return ret;
|
||||
|
||||
val = (val >> mc->shift) & mask;
|
||||
|
||||
ucontrol->value.integer.value[0] = val - mc->min;
|
||||
if (invert) {
|
||||
ucontrol->value.integer.value[0] =
|
||||
|
@ -271,11 +290,9 @@ static int snd_ac108_put_volsw(struct snd_kcontrol *kcontrol,
|
|||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
unsigned int sign_bit = mc->sign_bit;
|
||||
unsigned int mask = (1 << fls(mc->max)) - 1;
|
||||
unsigned int val, mask = (1 << fls(mc->max)) - 1;
|
||||
unsigned int invert = mc->invert;
|
||||
unsigned int val, val_mask;
|
||||
int chip = mc->autodisable;
|
||||
int err;
|
||||
int ret, chip = mc->autodisable;
|
||||
|
||||
if (sign_bit)
|
||||
mask = BIT(sign_bit + 1) - 1;
|
||||
|
@ -284,14 +301,12 @@ static int snd_ac108_put_volsw(struct snd_kcontrol *kcontrol,
|
|||
if (invert) {
|
||||
val = mc->max - val;
|
||||
}
|
||||
val_mask = mask << mc->shift;
|
||||
|
||||
mask = mask << mc->shift;
|
||||
val = val << mc->shift;
|
||||
|
||||
err = ac10x_update_bits(mc->reg, val_mask, val, ac10x->i2c[chip]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return err;
|
||||
ret = ac10x_update_bits(mc->reg, mask, val, ac10x->i2c[chip]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define SOC_AC108_SINGLE_TLV(xname, reg, shift, max, invert, chip, tlv_array) \
|
||||
|
@ -451,17 +466,6 @@ static const struct snd_soc_dapm_route ac108_dapm_routes[] = {
|
|||
|
||||
};
|
||||
|
||||
#if 0
|
||||
static int ac108_multi_chips_read(u8 reg, u8 *rt_value, struct ac10x_priv *ac10x) {
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < ac10x->codec_index; i++) {
|
||||
ac10x_read(reg, rt_value++, ac10x->i2c[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ac108_multi_chips_write(u8 reg, u8 val, struct ac10x_priv *ac10x) {
|
||||
u8 i;
|
||||
for (i = 0; i < ac10x->codec_index; i++) {
|
||||
|
@ -561,7 +565,7 @@ static int ac108_configure_clocking(struct ac10x_priv *ac10x, unsigned int rate)
|
|||
ac108_multi_chips_update_bits(PLL_LOCK_CTRL, 0x1 << PLL_LOCK_EN, 0x1 << PLL_LOCK_EN, ac10x);
|
||||
/*0x10: PLL Common voltage Enable, PLL Enable,PLL loop divider factor detection enable*/
|
||||
ac108_multi_chips_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN | 0x01 << PLL_NDET,
|
||||
0x1 << PLL_EN | 0x1 << PLL_COM_EN | 0x01 << PLL_NDET, ac10x);
|
||||
0x01 << PLL_EN | 0x01 << PLL_COM_EN | 0x01 << PLL_NDET, ac10x);
|
||||
|
||||
/**
|
||||
* 0x20: enable pll,pll source from mclk, sysclk source from
|
||||
|
@ -657,20 +661,26 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h
|
|||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned bclkdiv;
|
||||
u8 r;
|
||||
int ret = 0;
|
||||
u8 v;
|
||||
|
||||
dev_dbg(dai->dev, "%s() stream=%d\n", __func__, substream->stream);
|
||||
dev_dbg(dai->dev, "%s() stream=%d play:%d capt:%d +++\n", __func__,
|
||||
substream->stream, dai->playback_active, dai->capture_active);
|
||||
|
||||
if (ac10x->i2c101) {
|
||||
ret = ac101_hw_params(substream, params, dai);
|
||||
}
|
||||
|
||||
/* nothing should be done when it isn't capturing stream. */
|
||||
if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) {
|
||||
return 0;
|
||||
// TODO:
|
||||
}
|
||||
|
||||
channels = params_channels(params);
|
||||
|
||||
/* Master mode, to clear cpu_dai fifos, output bclk without lrck */
|
||||
ac10x_read(I2S_CTRL, &r, ac10x->i2c[_MASTER_INDEX]);
|
||||
if (r & (0x02 << LRCK_IOEN)) {
|
||||
ac10x_read(I2S_CTRL, &v, ac10x->i2c[_MASTER_INDEX]);
|
||||
if (v & (0x02 << LRCK_IOEN)) {
|
||||
ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x02 << LRCK_IOEN, ac10x->i2c[_MASTER_INDEX]);
|
||||
}
|
||||
|
||||
|
@ -780,7 +790,7 @@ static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_h
|
|||
*/
|
||||
ac108_multi_chips_slots(ac10x, channels);
|
||||
|
||||
ac10x->trgr_cnt = 0;
|
||||
dev_dbg(dai->dev, "%s() stream=%d ---\n", __func__, substream->stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -830,7 +840,7 @@ static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) {
|
|||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM: /*AC108 Master*/
|
||||
if (ac10x->tdm_chips_cnt < 2) {
|
||||
if (ac10x->tdm_chips_cnt < 2 || _MASTER_MULTI_CODEC == _MASTER_AC108) {
|
||||
dev_dbg(dai->dev, "AC108 set to work as Master\n");
|
||||
/**
|
||||
* 0x30:chip is master mode ,BCLK & LRCK output
|
||||
|
@ -965,16 +975,21 @@ static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) {
|
|||
|
||||
ac108_multi_chips_write(HPF_EN, 0x00, ac10x);
|
||||
|
||||
if (ac10x->i2c101) {
|
||||
return ac101_set_dai_fmt(dai, fmt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* due to miss channels order in cpu_dai, we meed defer the clock starting.
|
||||
*/
|
||||
static void ac108_work_start_clock(struct work_struct *work) {
|
||||
struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, dlywork.work);
|
||||
static int ac108_set_clock(int y_start_n_stop) {
|
||||
u8 r;
|
||||
|
||||
dev_dbg(ac10x->codec->dev, "%s() L%d start:%d\n", __func__, __LINE__, y_start_n_stop);
|
||||
|
||||
if (y_start_n_stop) {
|
||||
/* enable lrck clock */
|
||||
ac10x_read(I2S_CTRL, &r, ac10x->i2c[_MASTER_INDEX]);
|
||||
if (r & (0x02 << LRCK_IOEN)) {
|
||||
|
@ -983,8 +998,17 @@ static void ac108_work_start_clock(struct work_struct *work) {
|
|||
|
||||
/* enable global clock */
|
||||
ac108_multi_chips_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x);
|
||||
} else {
|
||||
/* disable lrck clock if it's enabled */
|
||||
ac10x_read(I2S_CTRL, &r, ac10x->i2c[_MASTER_INDEX]);
|
||||
if (r & (0x01 << LRCK_IOEN)) {
|
||||
ac108_multi_chips_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x0 << GEN, ac10x);
|
||||
ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, 0x02 << LRCK_IOEN, ac10x->i2c[_MASTER_INDEX]);
|
||||
ac108_multi_chips_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x1 << GEN, ac10x);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ac108_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
|
@ -993,6 +1017,7 @@ static int ac108_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
int ret = 0;
|
||||
u8 r;
|
||||
|
||||
dev_dbg(dai->dev, "%s() stream=%d cmd=%d\n",
|
||||
__FUNCTION__, substream->stream, cmd);
|
||||
|
@ -1001,9 +1026,9 @@ static int ac108_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (ac10x->trgr_cnt++ > 0) {
|
||||
break;
|
||||
}
|
||||
/* disable global clock if lrck disabled */
|
||||
ac10x_read(I2S_CTRL, &r, ac10x->i2c[_MASTER_INDEX]);
|
||||
if ((r & (0x01 << LRCK_IOEN)) == 0) {
|
||||
/* disable global clock */
|
||||
ac108_multi_chips_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x0 << GEN, ac10x);
|
||||
|
||||
|
@ -1013,8 +1038,8 @@ static int ac108_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
/*0x22: Module reset de-asserted<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
||||
ac108_multi_chips_write(MOD_RST_CTRL, 1 << I2S | 1 << ADC_DIGITAL | 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x);
|
||||
|
||||
/* delayed clock starting */
|
||||
schedule_delayed_work(&ac10x->dlywork, msecs_to_jiffies(30));
|
||||
/* delayed clock starting, move to simple_card_trigger() */
|
||||
}
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
|
@ -1027,19 +1052,55 @@ static int ac108_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int ac108_audio_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai
|
||||
) {
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (ac10x->i2c101) {
|
||||
return ac101_audio_startup(substream, dai);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ac108_aif_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai
|
||||
) {
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (ac10x->i2c101) {
|
||||
ac101_aif_shutdown(substream, dai);
|
||||
}
|
||||
}
|
||||
|
||||
int ac108_aif_mute(struct snd_soc_dai *dai, int mute) {
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (ac10x->i2c101) {
|
||||
return ac101_aif_mute(dai, mute);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops ac108_dai_ops = {
|
||||
.startup = ac108_audio_startup,
|
||||
.shutdown = ac108_aif_shutdown,
|
||||
|
||||
/*DAI clocking configuration*/
|
||||
.set_sysclk = ac108_set_sysclk,
|
||||
|
||||
/*ALSA PCM audio operations*/
|
||||
.hw_params = ac108_hw_params,
|
||||
.trigger = ac108_trigger,
|
||||
|
||||
// .hw_free = ac108_hw_free,
|
||||
.digital_mute = ac108_aif_mute,
|
||||
|
||||
/*DAI format configuration*/
|
||||
.set_fmt = ac108_set_fmt,
|
||||
|
||||
// .hw_free = ac108_hw_free,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver ac108_dai0 = {
|
||||
|
@ -1150,14 +1211,17 @@ static int ac108_add_widgets(struct snd_soc_codec *codec) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ac108_probe(struct snd_soc_codec *codec) {
|
||||
ac10x->codec = codec;
|
||||
dev_set_drvdata(codec->dev, ac10x);
|
||||
ac108_add_widgets(codec);
|
||||
|
||||
if (ac10x->i2c101) {
|
||||
ac101_codec_probe(codec);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ac108_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) {
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
|
@ -1186,18 +1250,49 @@ static int ac108_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_l
|
|||
break;
|
||||
}
|
||||
|
||||
if (ac10x->i2c101) {
|
||||
ac101_set_bias_level(codec, level);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ac108_codec_remove(struct snd_soc_codec *codec) {
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
static const struct snd_soc_codec_driver ac108_soc_codec_driver = {
|
||||
if (! ac10x->i2c101) {
|
||||
return 0;
|
||||
}
|
||||
return ac101_codec_remove(codec);
|
||||
}
|
||||
|
||||
int ac108_codec_suspend(struct snd_soc_codec *codec) {
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (! ac10x->i2c101) {
|
||||
return 0;
|
||||
}
|
||||
return ac101_codec_suspend(codec);
|
||||
}
|
||||
|
||||
int ac108_codec_resume(struct snd_soc_codec *codec) {
|
||||
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (! ac10x->i2c101) {
|
||||
return 0;
|
||||
}
|
||||
return ac101_codec_resume(codec);
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver ac10x_soc_codec_driver = {
|
||||
.probe = ac108_probe,
|
||||
.remove = ac108_codec_remove,
|
||||
.suspend = ac108_codec_suspend,
|
||||
.resume = ac108_codec_resume,
|
||||
.set_bias_level = ac108_set_bias_level,
|
||||
.read = ac108_codec_read,
|
||||
.write = ac108_codec_write,
|
||||
};
|
||||
|
||||
|
||||
static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {
|
||||
int val = 0, flag = 0;
|
||||
u8 i = 0, reg, num, value_w, value_r[4];
|
||||
|
@ -1241,25 +1336,22 @@ static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, co
|
|||
|
||||
static ssize_t ac108_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
||||
#if 1
|
||||
printk("echo flag|reg|val > ac10x\n");
|
||||
printk("eg read star addres=0x06,count 0x10:echo 0610 >ac10x\n");
|
||||
printk("eg write value:0xfe to address:0x06 :echo 106fe > ac10x\n");
|
||||
printk("echo flag|reg|val > ac108\n");
|
||||
printk("eg read star addres=0x06,count 0x10:echo 0610 >ac108\n");
|
||||
printk("eg write value:0xfe to address:0x06 :echo 106fe > ac108\n");
|
||||
return 0;
|
||||
#else
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"echo flag|reg|val > ac10x\n"
|
||||
"eg read star addres=0x06,count 0x10:echo 0610 >ac10x\n"
|
||||
"eg write value:0xfe to address:0x06 :echo 106fe > ac10x\n");
|
||||
return snprintf(buf, PAGE_SIZE,"echo flag|reg|val > ac108\n"
|
||||
"eg read star addres=0x06,count 0x10:echo 0610 >ac108\n"
|
||||
"eg write value:0xfe to address:0x06 :echo 106fe > ac108\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(ac108, 0644, ac108_show, ac108_store);
|
||||
|
||||
static struct attribute *ac108_debug_attrs[] = {
|
||||
&dev_attr_ac108.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ac108_debug_attr_group = {
|
||||
.name = "ac108_debug",
|
||||
.attrs = ac108_debug_attrs,
|
||||
|
@ -1267,9 +1359,9 @@ static struct attribute_group ac108_debug_attr_group = {
|
|||
|
||||
|
||||
static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i2c_id) {
|
||||
int ret = 0;
|
||||
struct device_node *np = i2c->dev.of_node;
|
||||
unsigned int val = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (ac10x == NULL) {
|
||||
ac10x = devm_kzalloc(&i2c->dev, sizeof(struct ac10x_priv), GFP_KERNEL);
|
||||
|
@ -1277,7 +1369,16 @@ static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i
|
|||
dev_err(&i2c->dev, "Unable to allocate ac10x private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_DELAYED_WORK(&ac10x->dlywork, ac108_work_start_clock);
|
||||
#if _I2C_MUTEX_EN
|
||||
mutex_init(&ac10x->i2c_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((int)i2c_id->driver_data == AC101_I2C_ID) {
|
||||
ac10x->i2c101 = i2c;
|
||||
i2c_set_clientdata(i2c, ac10x);
|
||||
ret = ac101_probe(i2c, i2c_id);
|
||||
goto __ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "data-protocol", &val);
|
||||
|
@ -1304,25 +1405,39 @@ static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i
|
|||
ac10x->i2c[i2c_id->driver_data] = i2c;
|
||||
ac10x->codec_index++;
|
||||
|
||||
/* when all i2c prepared, we bind codec to i2c[_MASTER_INDEX] */
|
||||
if (ac10x->codec_index == ac10x->tdm_chips_cnt) {
|
||||
ret = snd_soc_register_codec(&ac10x->i2c[_MASTER_INDEX]->dev, &ac108_soc_codec_driver,
|
||||
ac108_dai[_MASTER_INDEX], 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "Failed to register ac10x codec: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&i2c->dev.kobj, &ac108_debug_attr_group);
|
||||
if (ret) {
|
||||
pr_err("failed to create attr group\n");
|
||||
}
|
||||
|
||||
__ret:
|
||||
/* when all i2c prepared, we bind codec to i2c[_MASTER_INDEX] */
|
||||
if ((ac10x->codec_index != 0 && ac10x->tdm_chips_cnt < 2)
|
||||
|| (ac10x->codec_index == ac10x->tdm_chips_cnt && ac10x->i2c101)) {
|
||||
#if _MASTER_MULTI_CODEC == _MASTER_AC108
|
||||
asoc_simple_card_register_set_clock(ac108_set_clock);
|
||||
#endif
|
||||
/* no playback stream */
|
||||
if (! ac10x->i2c101) {
|
||||
memset(&ac108_dai[_MASTER_INDEX]->playback, '\0', sizeof ac108_dai[_MASTER_INDEX]->playback);
|
||||
}
|
||||
ret = snd_soc_register_codec(&ac10x->i2c[_MASTER_INDEX]->dev, &ac10x_soc_codec_driver,
|
||||
ac108_dai[_MASTER_INDEX], 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "Failed to register ac10x codec: %d\n", ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ac108_i2c_remove(struct i2c_client *client) {
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
static int ac108_i2c_remove(struct i2c_client *i2c) {
|
||||
if (ac10x->i2c101) {
|
||||
ac101_remove(ac10x->i2c101);
|
||||
}
|
||||
if (ac10x->codec != NULL) {
|
||||
snd_soc_unregister_codec(&ac10x->i2c[_MASTER_INDEX]->dev);
|
||||
ac10x->codec = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1331,6 +1446,7 @@ static const struct i2c_device_id ac108_i2c_id[] = {
|
|||
{ "ac108_1", 1 },
|
||||
{ "ac108_2", 2 },
|
||||
{ "ac108_3", 3 },
|
||||
{ "ac101", AC101_I2C_ID },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ac108_i2c_id);
|
||||
|
@ -1340,6 +1456,7 @@ static const struct of_device_id ac108_of_match[] = {
|
|||
{ .compatible = "x-power,ac108_1", },
|
||||
{ .compatible = "x-power,ac108_2", },
|
||||
{ .compatible = "x-power,ac108_3", },
|
||||
{ .compatible = "x-power,ac101", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ac108_of_match);
|
||||
|
|
13
ac10x.h
13
ac10x.h
|
@ -16,6 +16,9 @@
|
|||
#define __AC10X_H__
|
||||
|
||||
#define AC101_I2C_ID 4
|
||||
#define _MASTER_AC108 0
|
||||
#define _MASTER_AC101 1
|
||||
#define _MASTER_MULTI_CODEC _MASTER_AC108
|
||||
|
||||
#ifdef AC101_DEBG
|
||||
#define AC101_DBG(format,args...) printk("[AC101] "format,##args)
|
||||
|
@ -23,8 +26,13 @@
|
|||
#define AC101_DBG(...)
|
||||
#endif
|
||||
|
||||
#define _I2C_MUTEX_EN 1
|
||||
|
||||
struct ac10x_priv {
|
||||
struct i2c_client *i2c[4];
|
||||
#if _I2C_MUTEX_EN
|
||||
struct mutex i2c_mutex;
|
||||
#endif
|
||||
int codec_index;
|
||||
unsigned sysclk;
|
||||
unsigned mclk; /* master clock or aif_clock/aclk */
|
||||
|
@ -32,7 +40,6 @@ struct ac10x_priv {
|
|||
unsigned char i2s_mode;
|
||||
unsigned char data_protocol;
|
||||
struct delayed_work dlywork;
|
||||
int trgr_cnt;
|
||||
int tdm_chips_cnt;
|
||||
|
||||
/* struct for ac101 .begin */
|
||||
|
@ -48,6 +55,7 @@ struct ac10x_priv {
|
|||
u8 aif2_clken;
|
||||
|
||||
struct work_struct codec_resume;
|
||||
struct delayed_work dlywork101;
|
||||
struct gpio_desc* gpiod_spk_amp_gate;
|
||||
/* struct for ac101 .end */
|
||||
};
|
||||
|
@ -78,4 +86,7 @@ int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id);
|
|||
void ac101_shutdown(struct i2c_client *i2c);
|
||||
int ac101_remove(struct i2c_client *i2c);
|
||||
|
||||
/* simple card export */
|
||||
int asoc_simple_card_register_set_clock(int (*set_clock)(int));
|
||||
|
||||
#endif//__AC10X_H__
|
||||
|
|
|
@ -103,28 +103,6 @@
|
|||
system-clock-id = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
simple-audio-card,dai-link@1 {
|
||||
format = "dsp_a";
|
||||
bitclock-master = <&codec1_dai>;
|
||||
frame-master = <&codec1_dai>;
|
||||
/* bitclock-inversion; */
|
||||
/* frame-inversion; */
|
||||
|
||||
cpu {
|
||||
sound-dai = <&i2s>;
|
||||
dai-tdm-slot-num = <2>;
|
||||
dai-tdm-slot-width = <32>;
|
||||
dai-tdm-slot-tx-mask = <1 1 0 0>;
|
||||
dai-tdm-slot-rx-mask = <1 1 0 0>;
|
||||
};
|
||||
|
||||
codec1_dai: codec {
|
||||
sound-dai = <&ac101>;
|
||||
clocks = <&ac10x_mclk>;
|
||||
system-clock-id = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Binary file not shown.
|
@ -202,32 +202,35 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int (* _start_clock)(void);
|
||||
static int (* _set_clock)(int y_start_n_stop);
|
||||
|
||||
int register_start_clock(int (*start_clock)(void)) {
|
||||
_start_clock = start_clock;
|
||||
int asoc_simple_card_register_set_clock(int (*set_clock)(int)) {
|
||||
_set_clock = set_clock;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(register_start_clock);
|
||||
EXPORT_SYMBOL(asoc_simple_card_register_set_clock);
|
||||
|
||||
static int asoc_simple_card_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(rtd->card->dev, "%s() stream=%d cmd=%d\n",
|
||||
__FUNCTION__, substream->stream, cmd);
|
||||
dev_dbg(rtd->card->dev, "%s() stream=%d cmd=%d play:%d, capt:%d\n",
|
||||
__FUNCTION__, substream->stream, cmd,
|
||||
dai->playback_active, dai->capture_active);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (_start_clock) _start_clock();
|
||||
if (_set_clock) _set_clock(1);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (_set_clock) _set_clock(0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/vermagic.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
MODULE_INFO(vermagic, VERMAGIC_STRING);
|
||||
|
||||
__visible struct module __this_module
|
||||
__attribute__((section(".gnu.linkonce.this_module"))) = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.init = init_module,
|
||||
#ifdef CONFIG_MODULE_UNLOAD
|
||||
.exit = cleanup_module,
|
||||
#endif
|
||||
.arch = MODULE_ARCH_INIT,
|
||||
};
|
||||
|
||||
static const struct modversion_info ____versions[]
|
||||
__used
|
||||
__attribute__((section("__versions"))) = {
|
||||
{ 0xdb0d8ebc, __VMLINUX_SYMBOL_STR(module_layout) },
|
||||
{ 0xb077e70a, __VMLINUX_SYMBOL_STR(clk_unprepare) },
|
||||
{ 0xe56a9336, __VMLINUX_SYMBOL_STR(snd_pcm_format_width) },
|
||||
{ 0xf9a482f9, __VMLINUX_SYMBOL_STR(msleep) },
|
||||
{ 0xeb711ae7, __VMLINUX_SYMBOL_STR(snd_soc_params_to_bclk) },
|
||||
{ 0x815588a6, __VMLINUX_SYMBOL_STR(clk_enable) },
|
||||
{ 0x2e5810c6, __VMLINUX_SYMBOL_STR(__aeabi_unwind_cpp_pr1) },
|
||||
{ 0xd9ead207, __VMLINUX_SYMBOL_STR(i2c_del_driver) },
|
||||
{ 0xaa3b6018, __VMLINUX_SYMBOL_STR(snd_soc_dapm_get_volsw) },
|
||||
{ 0x52eb2dc5, __VMLINUX_SYMBOL_STR(regmap_update_bits_base) },
|
||||
{ 0xb6e6d99d, __VMLINUX_SYMBOL_STR(clk_disable) },
|
||||
{ 0xf7802486, __VMLINUX_SYMBOL_STR(__aeabi_uidivmod) },
|
||||
{ 0x70139129, __VMLINUX_SYMBOL_STR(snd_soc_add_codec_controls) },
|
||||
{ 0xb1ad28e0, __VMLINUX_SYMBOL_STR(__gnu_mcount_nc) },
|
||||
{ 0xb7051343, __VMLINUX_SYMBOL_STR(snd_soc_dapm_new_controls) },
|
||||
{ 0xcb5daba2, __VMLINUX_SYMBOL_STR(snd_soc_put_volsw) },
|
||||
{ 0x66da0eca, __VMLINUX_SYMBOL_STR(snd_soc_get_volsw) },
|
||||
{ 0xe2d5255a, __VMLINUX_SYMBOL_STR(strcmp) },
|
||||
{ 0x37dc1ecd, __VMLINUX_SYMBOL_STR(snd_soc_info_enum_double) },
|
||||
{ 0x82108f90, __VMLINUX_SYMBOL_STR(snd_soc_dapm_add_routes) },
|
||||
{ 0xe707d823, __VMLINUX_SYMBOL_STR(__aeabi_uidiv) },
|
||||
{ 0xe872d1ce, __VMLINUX_SYMBOL_STR(snd_soc_read) },
|
||||
{ 0x1b55d4fa, __VMLINUX_SYMBOL_STR(dev_err) },
|
||||
{ 0x27e1a049, __VMLINUX_SYMBOL_STR(printk) },
|
||||
{ 0xac92dc30, __VMLINUX_SYMBOL_STR(snd_soc_update_bits) },
|
||||
{ 0x3e9d3734, __VMLINUX_SYMBOL_STR(of_find_property) },
|
||||
{ 0x8b4dfb4c, __VMLINUX_SYMBOL_STR(snd_soc_dapm_put_volsw) },
|
||||
{ 0xa0195a8c, __VMLINUX_SYMBOL_STR(snd_ctl_boolean_mono_info) },
|
||||
{ 0x2196324, __VMLINUX_SYMBOL_STR(__aeabi_idiv) },
|
||||
{ 0x59e5070d, __VMLINUX_SYMBOL_STR(__do_div64) },
|
||||
{ 0x94680ad3, __VMLINUX_SYMBOL_STR(snd_soc_info_volsw) },
|
||||
{ 0xf204b815, __VMLINUX_SYMBOL_STR(i2c_register_driver) },
|
||||
{ 0x34cffba9, __VMLINUX_SYMBOL_STR(snd_soc_get_enum_double) },
|
||||
{ 0xff227eee, __VMLINUX_SYMBOL_STR(__devm_regmap_init_i2c) },
|
||||
{ 0x7c9a7371, __VMLINUX_SYMBOL_STR(clk_prepare) },
|
||||
{ 0x8c211eeb, __VMLINUX_SYMBOL_STR(devm_clk_get) },
|
||||
{ 0xee9ce628, __VMLINUX_SYMBOL_STR(snd_soc_unregister_codec) },
|
||||
{ 0xc98126c9, __VMLINUX_SYMBOL_STR(snd_soc_put_enum_double) },
|
||||
{ 0x103b85f7, __VMLINUX_SYMBOL_STR(snd_soc_register_codec) },
|
||||
{ 0x40c57909, __VMLINUX_SYMBOL_STR(devm_kmalloc) },
|
||||
{ 0xd79caef6, __VMLINUX_SYMBOL_STR(regmap_write) },
|
||||
{ 0xad83053c, __VMLINUX_SYMBOL_STR(regcache_sync) },
|
||||
{ 0x44c19f83, __VMLINUX_SYMBOL_STR(snd_soc_write) },
|
||||
};
|
||||
|
||||
static const char __module_depends[]
|
||||
__used
|
||||
__attribute__((section(".modinfo"))) =
|
||||
"depends=snd-pcm,snd-soc-core,snd";
|
||||
|
||||
MODULE_ALIAS("i2c:wm8960");
|
||||
MODULE_ALIAS("of:N*T*Cwlf,wm8960");
|
||||
MODULE_ALIAS("of:N*T*Cwlf,wm8960C*");
|
||||
|
||||
MODULE_INFO(srcversion, "2690F666DC1933DEF3E5373");
|
Loading…
Reference in a new issue