summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorRahul Mittal <rmittal@nvidia.com>2013-01-03 13:39:14 +0530
committerRiham Haidar <rhaidar@nvidia.com>2013-01-15 17:17:53 -0800
commit5215115032cd98ee4c0396b7b994a4b1e4cfc806 (patch)
treeac36675822d7005bd89c3b066e08fb1bba21d87d /sound
parent7a4ef61fa5258b3e31084165313c2784124f2748 (diff)
asoc: codecs: rt5640: Implement i2c shutdown
i2c shutdown implementation for rt5640 No i2c transaction should happen after shutdown Bug 1202582 Change-Id: I1f4981d1a33a22b84f652926eed5478432c8a637 Signed-off-by: Rahul Mittal <rmittal@nvidia.com> Reviewed-on: http://git-master/r/188216 (cherry picked from commit 67925f41526dc4f1791ffaeca7322b11cc712ef7) Reviewed-on: http://git-master/r/191112 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Scott Peterson <speterson@nvidia.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/rt5640.c130
-rw-r--r--sound/soc/codecs/rt5640.h2
2 files changed, 127 insertions, 5 deletions
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 71f6467bca03..2c76a96b57ca 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -34,6 +34,10 @@
#define RT5640_DET_EXT_MIC 0
#define RT5639_RESET_ID 0x0008
+#define CHECK_I2C_SHUTDOWN(r, c) { if (r && r->shutdown_complete) { \
+dev_err(c->dev, "error: i2c state is 'shutdown'\n"); \
+mutex_unlock(&r->lock); return -ENODEV; } }
+
#ifdef RT5640_DEMO
struct rt5640_init_reg {
u8 reg;
@@ -436,6 +440,9 @@ int rt5640_headset_detect(struct snd_soc_codec *codec, int jack_insert)
{
int jack_type;
int sclk_src;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
if (jack_insert) {
if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
@@ -475,6 +482,7 @@ int rt5640_headset_detect(struct snd_soc_codec *codec, int jack_insert)
jack_type = RT5640_NO_JACK;
}
+ mutex_unlock(&rt5640->lock);
return jack_type;
}
EXPORT_SYMBOL(rt5640_headset_detect);
@@ -513,9 +521,13 @@ static int rt5640_dmic_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
- if (rt5640->dmic_en == ucontrol->value.integer.value[0])
+ if (rt5640->dmic_en == ucontrol->value.integer.value[0]) {
+ mutex_unlock(&rt5640->lock);
return 0;
+ }
rt5640->dmic_en = ucontrol->value.integer.value[0];
switch (rt5640->dmic_en) {
@@ -560,9 +572,11 @@ static int rt5640_dmic_put(struct snd_kcontrol *kcontrol,
break;
default:
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -632,8 +646,13 @@ static int rt5640_regctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
+
ucontrol->value.integer.value[0] = regctl_addr;
ucontrol->value.integer.value[1] = snd_soc_read(codec, regctl_addr);
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -641,10 +660,15 @@ static int rt5640_regctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
+
regctl_addr = ucontrol->value.integer.value[0];
if (ucontrol->value.integer.value[1] <= REGVAL_MAX)
snd_soc_write(codec, regctl_addr,
ucontrol->value.integer.value[1]);
+ mutex_unlock(&rt5640->lock);
return 0;
}
#endif
@@ -659,13 +683,18 @@ static int rt5640_vol_rescale_get(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int val = snd_soc_read(codec, mc->reg);
+ unsigned int val;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
+ val = snd_soc_read(codec, mc->reg);
ucontrol->value.integer.value[0] = VOL_RESCALE_MAX_VOL -
((val & RT5640_L_VOL_MASK) >> mc->shift);
ucontrol->value.integer.value[1] = VOL_RESCALE_MAX_VOL -
(val & RT5640_R_VOL_MASK);
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -676,11 +705,17 @@ static int rt5640_vol_rescale_put(struct snd_kcontrol *kcontrol,
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned int val, val2;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
val = VOL_RESCALE_MAX_VOL - ucontrol->value.integer.value[0];
val2 = VOL_RESCALE_MAX_VOL - ucontrol->value.integer.value[1];
- return snd_soc_update_bits_locked(codec, mc->reg, RT5640_L_VOL_MASK |
+ ret = snd_soc_update_bits_locked(codec, mc->reg, RT5640_L_VOL_MASK |
RT5640_R_VOL_MASK, val << mc->shift | val2);
+ mutex_unlock(&rt5640->lock);
+ return ret;
}
@@ -774,6 +809,8 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_soc_codec *codec = w->codec;
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
int div[] = {2, 3, 4, 6, 12}, idx = -EINVAL, i, rate, red, bound, temp;
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
rate = rt5640->lrck[rt5640->aif_pu] << 8;
red = 3000000 * 12;
@@ -792,6 +829,7 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
else
snd_soc_update_bits(codec, RT5640_DMIC, RT5640_DMIC_CLK_MASK,
idx << RT5640_DMIC_CLK_SFT);
+ mutex_unlock(&rt5640->lock);
return idx;
}
@@ -799,9 +837,13 @@ static int check_sysclk1_source(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
unsigned int val;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(source->codec);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, source->codec)
val = snd_soc_read(source->codec, RT5640_GLB_CLK);
val &= RT5640_SCLK_SRC_MASK;
+ mutex_unlock(&rt5640->lock);
if (val == RT5640_SCLK_SRC_PLL1 || val == RT5640_SCLK_SRC_PLL1T)
return 1;
else
@@ -1192,6 +1234,9 @@ static int spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -1209,8 +1254,10 @@ static int spk_event(struct snd_soc_dapm_widget *w,
break;
default:
+ mutex_unlock(&rt5640->lock);
return 0;
}
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -1237,6 +1284,9 @@ static int rt5640_set_dmic1_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = w->codec;
unsigned int val, mask;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1251,9 +1301,11 @@ static int rt5640_set_dmic1_event(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec, RT5640_DMIC,
RT5640_DMIC_1_EN_MASK, RT5640_DMIC_1_EN);
default:
+ mutex_unlock(&rt5640->lock);
return 0;
}
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -1262,6 +1314,9 @@ static int rt5640_set_dmic2_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = w->codec;
unsigned int val, mask;
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1276,9 +1331,11 @@ static int rt5640_set_dmic2_event(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec, RT5640_DMIC,
RT5640_DMIC_2_EN_MASK, RT5640_DMIC_2_EN);
default:
+ mutex_unlock(&rt5640->lock);
return 0;
}
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -1931,16 +1988,20 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
unsigned int val_len = 0, val_clk, mask_clk, dai_sel;
int pre_div, bclk_ms, frame_size;
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
rt5640->lrck[dai->id] = params_rate(params);
pre_div = get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]);
if (pre_div < 0) {
dev_err(codec->dev, "Unsupported clock setting\n");
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
frame_size = snd_soc_params_to_frame_size(params);
if (frame_size < 0) {
dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
bclk_ms = frame_size > 32 ? 1 : 0;
@@ -1964,12 +2025,14 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
val_len |= RT5640_I2S_DL_8;
break;
default:
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
dai_sel = get_sdp_info(codec, dai->id);
if (dai_sel < 0) {
dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
if (dai_sel & RT5640_U_IF1) {
@@ -1999,6 +2062,7 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
}
#endif
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -2018,6 +2082,8 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct snd_soc_codec *codec = dai->codec;
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
unsigned int reg_val = 0, dai_sel;
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
@@ -2028,6 +2094,7 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
rt5640->master[dai->id] = 0;
break;
default:
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
@@ -2038,6 +2105,7 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
reg_val |= RT5640_I2S_BP_INV;
break;
default:
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
@@ -2054,12 +2122,14 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
reg_val |= RT5640_I2S_DF_PCM_B;
break;
default:
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
dai_sel = get_sdp_info(codec, dai->id);
if (dai_sel < 0) {
dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
if (dai_sel & RT5640_U_IF1) {
@@ -2080,6 +2150,7 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
RT5640_I2S_DF_MASK, reg_val);
}
#endif
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -2089,9 +2160,13 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
struct snd_soc_codec *codec = dai->codec;
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
unsigned int reg_val = 0;
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
- if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src)
+ if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src) {
+ mutex_unlock(&rt5640->lock);
return 0;
+ }
switch (clk_id) {
case RT5640_SCLK_S_MCLK:
@@ -2108,6 +2183,7 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
break;
default:
dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
snd_soc_update_bits(codec, RT5640_GLB_CLK,
@@ -2116,6 +2192,7 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
rt5640->sysclk_src = clk_id;
dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -2179,10 +2256,14 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
struct rt5640_pll_code pll_code;
int ret, dai_sel;
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
if (source == rt5640->pll_src && freq_in == rt5640->pll_in &&
- freq_out == rt5640->pll_out)
+ freq_out == rt5640->pll_out) {
+ mutex_unlock(&rt5640->lock);
return 0;
+ }
if (!freq_in || !freq_out) {
dev_dbg(codec->dev, "PLL disabled\n");
@@ -2191,6 +2272,7 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
rt5640->pll_out = 0;
snd_soc_update_bits(codec, RT5640_GLB_CLK,
RT5640_SCLK_SRC_MASK, RT5640_SCLK_SRC_MCLK);
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -2210,6 +2292,7 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
if (dai_sel < 0) {
dev_err(codec->dev,
"Failed to get sdp info: %d\n", dai_sel);
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
if (dai_sel & RT5640_U_IF1) {
@@ -2227,12 +2310,14 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
break;
default:
dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ mutex_unlock(&rt5640->lock);
return -EINVAL;
}
ret = rt5640_pll_calc(freq_in, freq_out, &pll_code);
if (ret < 0) {
dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ mutex_unlock(&rt5640->lock);
return ret;
}
@@ -2249,6 +2334,7 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
rt5640->pll_out = freq_out;
rt5640->pll_src = source;
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -2270,6 +2356,8 @@ static ssize_t rt5640_index_show(struct device *dev,
struct snd_soc_codec *codec = rt5640->codec;
unsigned int val;
int cnt = 0, i;
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
cnt += sprintf(buf, "RT5640 index register\n");
for (i = 0; i < 0xb4; i++) {
@@ -2284,6 +2372,7 @@ static ssize_t rt5640_index_show(struct device *dev,
if (cnt >= PAGE_SIZE)
cnt = PAGE_SIZE - 1;
+ mutex_unlock(&rt5640->lock);
return cnt;
}
static DEVICE_ATTR(index_reg, 0444, rt5640_index_show, NULL);
@@ -2291,6 +2380,10 @@ static DEVICE_ATTR(index_reg, 0444, rt5640_index_show, NULL);
static int rt5640_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
+
switch (level) {
case SND_SOC_BIAS_ON:
#ifdef RT5640_DEMO
@@ -2371,6 +2464,7 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
}
codec->dapm.bias_level = level;
+ mutex_unlock(&rt5640->lock);
return 0;
}
@@ -2379,12 +2473,15 @@ static int rt5640_probe(struct snd_soc_codec *codec)
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
int ret;
u16 val;
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
codec->dapm.idle_bias_off = 1;
ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ mutex_unlock(&rt5640->lock);
return ret;
}
@@ -2392,6 +2489,7 @@ static int rt5640_probe(struct snd_soc_codec *codec)
if ((val != rt5640_reg[RT5640_RESET]) && (val != RT5639_RESET_ID)) {
dev_err(codec->dev,
"Device with ID register %x is not rt5640/39\n", val);
+ mutex_unlock(&rt5640->lock);
return -ENODEV;
}
@@ -2438,18 +2536,25 @@ static int rt5640_probe(struct snd_soc_codec *codec)
if (ret != 0) {
dev_err(codec->dev,
"Failed to create index_reg sysfs files: %d\n", ret);
+ mutex_unlock(&rt5640->lock);
return ret;
}
+ mutex_unlock(&rt5640->lock);
return 0;
}
static int rt5640_remove(struct snd_soc_codec *codec)
{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+
rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ mutex_lock(&rt5640->lock);
+ CHECK_I2C_SHUTDOWN(rt5640, codec)
rt5640_reset(codec);
snd_soc_write(codec, RT5640_PWR_ANLG1, 0);
+ mutex_unlock(&rt5640->lock);
return 0;
}
#ifdef CONFIG_PM
@@ -2591,6 +2696,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
i2c_set_clientdata(i2c, rt5640);
+ mutex_init(&rt5640->lock);
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
rt5640_dai, ARRAY_SIZE(rt5640_dai));
@@ -2607,6 +2713,19 @@ static __devexit int rt5640_i2c_remove(struct i2c_client *i2c)
return 0;
}
+static void rt5640_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct rt5640_priv *rt5640 = i2c_get_clientdata(i2c);
+
+ mutex_lock(&rt5640->lock);
+
+ if (i2c->irq)
+ disable_irq(i2c->irq);
+ rt5640->shutdown_complete = 1;
+
+ mutex_unlock(&rt5640->lock);
+}
+
struct i2c_driver rt5640_i2c_driver = {
.driver = {
.name = "rt5640",
@@ -2615,6 +2734,7 @@ struct i2c_driver rt5640_i2c_driver = {
.probe = rt5640_i2c_probe,
.remove = __devexit_p(rt5640_i2c_remove),
.id_table = rt5640_i2c_id,
+ .shutdown = rt5640_i2c_shutdown,
};
static int __init rt5640_modinit(void)
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index d6920f00e08e..af48091e57b5 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -2092,6 +2092,8 @@ struct rt5640_priv {
int dmic_en;
int dsp_sw;
+ struct mutex lock;
+ int shutdown_complete;
};