Commit cf870273 authored by Ryder Lee's avatar Ryder Lee Committed by Mark Brown

ASoC: mediatek: simplify the control logic of MT2701 I2S

This patch adjusts the mt2701_afe_i2s_ops to simplify the control
logic of the I2S path.
Signed-off-by: default avatarRyder Lee <ryder.lee@mediatek.com>
Reviewed-by: default avatarGarlic Tseng <garlic.tseng@mediatek.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent a74d51ba
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Copyright (c) 2016 MediaTek Inc. * Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.com> * Author: Garlic Tseng <garlic.tseng@mediatek.com>
* Ryder Lee <ryder.lee@mediatek.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and
...@@ -102,10 +103,10 @@ int mt2701_init_clock(struct mtk_base_afe *afe) ...@@ -102,10 +103,10 @@ int mt2701_init_clock(struct mtk_base_afe *afe)
return 0; return 0;
} }
int mt2701_afe_enable_i2s(struct mtk_base_afe *afe, int id, int dir) int mt2701_afe_enable_i2s(struct mtk_base_afe *afe,
struct mt2701_i2s_path *i2s_path,
int dir)
{ {
struct mt2701_afe_private *afe_priv = afe->platform_priv;
struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id];
int ret; int ret;
ret = clk_prepare_enable(i2s_path->asrco_ck); ret = clk_prepare_enable(i2s_path->asrco_ck);
...@@ -128,11 +129,10 @@ int mt2701_afe_enable_i2s(struct mtk_base_afe *afe, int id, int dir) ...@@ -128,11 +129,10 @@ int mt2701_afe_enable_i2s(struct mtk_base_afe *afe, int id, int dir)
return ret; return ret;
} }
void mt2701_afe_disable_i2s(struct mtk_base_afe *afe, int id, int dir) void mt2701_afe_disable_i2s(struct mtk_base_afe *afe,
struct mt2701_i2s_path *i2s_path,
int dir)
{ {
struct mt2701_afe_private *afe_priv = afe->platform_priv;
struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id];
clk_disable_unprepare(i2s_path->hop_ck[dir]); clk_disable_unprepare(i2s_path->hop_ck[dir]);
clk_disable_unprepare(i2s_path->asrco_ck); clk_disable_unprepare(i2s_path->asrco_ck);
} }
...@@ -272,27 +272,32 @@ int mt2701_afe_disable_clock(struct mtk_base_afe *afe) ...@@ -272,27 +272,32 @@ int mt2701_afe_disable_clock(struct mtk_base_afe *afe)
return 0; return 0;
} }
void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain, int mt2701_mclk_configuration(struct mtk_base_afe *afe, int id)
int mclk)
{ {
struct mt2701_afe_private *priv = afe->platform_priv; struct mt2701_afe_private *priv = afe->platform_priv;
struct mt2701_i2s_path *i2s_path = &priv->i2s_path[id]; struct mt2701_i2s_path *i2s_path = &priv->i2s_path[id];
int ret; int ret = -EINVAL;
/* Set mclk source */ /* Set mclk source */
if (domain == 0) if (!(MT2701_PLL_DOMAIN_0_RATE % i2s_path->mclk_rate))
ret = clk_set_parent(i2s_path->sel_ck, ret = clk_set_parent(i2s_path->sel_ck,
priv->base_ck[MT2701_TOP_AUD_MCLK_SRC0]); priv->base_ck[MT2701_TOP_AUD_MCLK_SRC0]);
else else if (!(MT2701_PLL_DOMAIN_1_RATE % i2s_path->mclk_rate))
ret = clk_set_parent(i2s_path->sel_ck, ret = clk_set_parent(i2s_path->sel_ck,
priv->base_ck[MT2701_TOP_AUD_MCLK_SRC1]); priv->base_ck[MT2701_TOP_AUD_MCLK_SRC1]);
if (ret) if (ret) {
dev_err(afe->dev, "failed to set domain%d mclk source %d\n", dev_err(afe->dev, "failed to set mclk source\n");
domain, ret); return ret;
}
/* Set mclk divider */ /* Set mclk divider */
ret = clk_set_rate(i2s_path->div_ck, mclk); ret = clk_set_rate(i2s_path->div_ck, i2s_path->mclk_rate);
if (ret) if (ret) {
dev_err(afe->dev, "failed to set mclk divider %d\n", ret); dev_err(afe->dev, "failed to set mclk divider %d\n", ret);
return ret;
}
return 0;
} }
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Copyright (c) 2016 MediaTek Inc. * Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.com> * Author: Garlic Tseng <garlic.tseng@mediatek.com>
* Ryder Lee <ryder.lee@mediatek.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and
...@@ -18,20 +19,24 @@ ...@@ -18,20 +19,24 @@
#define _MT2701_AFE_CLOCK_CTRL_H_ #define _MT2701_AFE_CLOCK_CTRL_H_
struct mtk_base_afe; struct mtk_base_afe;
struct mt2701_i2s_path;
int mt2701_init_clock(struct mtk_base_afe *afe); int mt2701_init_clock(struct mtk_base_afe *afe);
int mt2701_afe_enable_clock(struct mtk_base_afe *afe); int mt2701_afe_enable_clock(struct mtk_base_afe *afe);
int mt2701_afe_disable_clock(struct mtk_base_afe *afe); int mt2701_afe_disable_clock(struct mtk_base_afe *afe);
int mt2701_afe_enable_i2s(struct mtk_base_afe *afe, int id, int dir); int mt2701_afe_enable_i2s(struct mtk_base_afe *afe,
void mt2701_afe_disable_i2s(struct mtk_base_afe *afe, int id, int dir); struct mt2701_i2s_path *path,
int dir);
void mt2701_afe_disable_i2s(struct mtk_base_afe *afe,
struct mt2701_i2s_path *path,
int dir);
int mt2701_afe_enable_mclk(struct mtk_base_afe *afe, int id); int mt2701_afe_enable_mclk(struct mtk_base_afe *afe, int id);
void mt2701_afe_disable_mclk(struct mtk_base_afe *afe, int id); void mt2701_afe_disable_mclk(struct mtk_base_afe *afe, int id);
int mt2701_enable_btmrg_clk(struct mtk_base_afe *afe); int mt2701_enable_btmrg_clk(struct mtk_base_afe *afe);
void mt2701_disable_btmrg_clk(struct mtk_base_afe *afe); void mt2701_disable_btmrg_clk(struct mtk_base_afe *afe);
void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain, int mt2701_mclk_configuration(struct mtk_base_afe *afe, int id);
int mclk);
#endif #endif
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
* *
* Copyright (c) 2016 MediaTek Inc. * Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.com> * Author: Garlic Tseng <garlic.tseng@mediatek.com>
* Ir Lian <ir.lian@mediatek.com> * Ir Lian <ir.lian@mediatek.com>
* Ryder Lee <ryder.lee@mediatek.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and
...@@ -101,31 +102,15 @@ static int mt2701_afe_i2s_startup(struct snd_pcm_substream *substream, ...@@ -101,31 +102,15 @@ static int mt2701_afe_i2s_startup(struct snd_pcm_substream *substream,
return mt2701_afe_enable_mclk(afe, i2s_num); return mt2701_afe_enable_mclk(afe, i2s_num);
} }
static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream, static int mt2701_afe_i2s_path_disable(struct mtk_base_afe *afe,
struct snd_soc_dai *dai, struct mt2701_i2s_path *i2s_path,
int i2s_num, int stream_dir)
int dir_invert)
{ {
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); const struct mt2701_i2s_data *i2s_data = i2s_path->i2s_data[stream_dir];
struct mt2701_afe_private *afe_priv = afe->platform_priv;
struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i2s_num];
const struct mt2701_i2s_data *i2s_data;
int stream_dir = substream->stream;
if (dir_invert) {
if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
stream_dir = SNDRV_PCM_STREAM_CAPTURE;
else
stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
}
i2s_data = i2s_path->i2s_data[stream_dir];
i2s_path->on[stream_dir]--; if (--i2s_path->on[stream_dir] < 0)
if (i2s_path->on[stream_dir] < 0) {
dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n",
i2s_path->on[stream_dir], stream_dir);
i2s_path->on[stream_dir] = 0; i2s_path->on[stream_dir] = 0;
}
if (i2s_path->on[stream_dir]) if (i2s_path->on[stream_dir])
return 0; return 0;
...@@ -133,7 +118,7 @@ static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream, ...@@ -133,7 +118,7 @@ static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream,
regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
ASYS_I2S_CON_I2S_EN, 0); ASYS_I2S_CON_I2S_EN, 0);
mt2701_afe_disable_i2s(afe, i2s_num, stream_dir); mt2701_afe_disable_i2s(afe, i2s_path, stream_dir);
return 0; return 0;
} }
...@@ -154,48 +139,32 @@ static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream, ...@@ -154,48 +139,32 @@ static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream,
if (i2s_path->occupied[substream->stream]) if (i2s_path->occupied[substream->stream])
i2s_path->occupied[substream->stream] = 0; i2s_path->occupied[substream->stream] = 0;
else else
goto I2S_UNSTART; goto exit;
mt2701_afe_i2s_path_shutdown(substream, dai, i2s_num, 0); mt2701_afe_i2s_path_disable(afe, i2s_path, substream->stream);
/* need to disable i2s-out path when disable i2s-in */ /* need to disable i2s-out path when disable i2s-in */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
mt2701_afe_i2s_path_shutdown(substream, dai, i2s_num, 1); mt2701_afe_i2s_path_disable(afe, i2s_path, !substream->stream);
I2S_UNSTART: exit:
/* disable mclk */ /* disable mclk */
mt2701_afe_disable_mclk(afe, i2s_num); mt2701_afe_disable_mclk(afe, i2s_num);
} }
static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream, static int mt2701_i2s_path_enable(struct mtk_base_afe *afe,
struct snd_soc_dai *dai, struct mt2701_i2s_path *i2s_path,
int i2s_num, int stream_dir, int rate)
int dir_invert)
{ {
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); const struct mt2701_i2s_data *i2s_data = i2s_path->i2s_data[stream_dir];
struct mt2701_afe_private *afe_priv = afe->platform_priv;
struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i2s_num];
const struct mt2701_i2s_data *i2s_data;
struct snd_pcm_runtime * const runtime = substream->runtime;
int reg, fs, w_len = 1; /* now we support bck 64bits only */ int reg, fs, w_len = 1; /* now we support bck 64bits only */
int stream_dir = substream->stream;
unsigned int mask, val; unsigned int mask, val;
if (dir_invert) {
if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
stream_dir = SNDRV_PCM_STREAM_CAPTURE;
else
stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
}
i2s_data = i2s_path->i2s_data[stream_dir];
/* no need to enable if already done */ /* no need to enable if already done */
i2s_path->on[stream_dir]++; if (++i2s_path->on[stream_dir] != 1)
if (i2s_path->on[stream_dir] != 1)
return 0; return 0;
fs = mt2701_afe_i2s_fs(runtime->rate); fs = mt2701_afe_i2s_fs(rate);
mask = ASYS_I2S_CON_FS | mask = ASYS_I2S_CON_FS |
ASYS_I2S_CON_I2S_COUPLE_MODE | /* 0 */ ASYS_I2S_CON_I2S_COUPLE_MODE | /* 0 */
...@@ -209,22 +178,20 @@ static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream, ...@@ -209,22 +178,20 @@ static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) { if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) {
mask |= ASYS_I2S_IN_PHASE_FIX; mask |= ASYS_I2S_IN_PHASE_FIX;
val |= ASYS_I2S_IN_PHASE_FIX; val |= ASYS_I2S_IN_PHASE_FIX;
reg = ASMI_TIMING_CON1;
} else {
reg = ASMO_TIMING_CON1;
} }
regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val); regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val);
if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
reg = ASMO_TIMING_CON1;
else
reg = ASMI_TIMING_CON1;
regmap_update_bits(afe->regmap, reg, regmap_update_bits(afe->regmap, reg,
i2s_data->i2s_asrc_fs_mask i2s_data->i2s_asrc_fs_mask
<< i2s_data->i2s_asrc_fs_shift, << i2s_data->i2s_asrc_fs_shift,
fs << i2s_data->i2s_asrc_fs_shift); fs << i2s_data->i2s_asrc_fs_shift);
/* enable i2s */ /* enable i2s */
mt2701_afe_enable_i2s(afe, i2s_num, stream_dir); mt2701_afe_enable_i2s(afe, i2s_path, stream_dir);
/* reset i2s hw status before enable */ /* reset i2s hw status before enable */
regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
...@@ -241,43 +208,32 @@ static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream, ...@@ -241,43 +208,32 @@ static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream, static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
int clk_domain;
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
struct mt2701_afe_private *afe_priv = afe->platform_priv; struct mt2701_afe_private *afe_priv = afe->platform_priv;
int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id); int ret, i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
struct mt2701_i2s_path *i2s_path; struct mt2701_i2s_path *i2s_path;
int mclk_rate;
if (i2s_num < 0) if (i2s_num < 0)
return i2s_num; return i2s_num;
i2s_path = &afe_priv->i2s_path[i2s_num]; i2s_path = &afe_priv->i2s_path[i2s_num];
mclk_rate = i2s_path->mclk_rate;
if (i2s_path->occupied[substream->stream]) if (i2s_path->occupied[substream->stream])
return -EBUSY; return -EBUSY;
ret = mt2701_mclk_configuration(afe, i2s_num);
if (ret)
return ret;
i2s_path->occupied[substream->stream] = 1; i2s_path->occupied[substream->stream] = 1;
if (MT2701_PLL_DOMAIN_0_RATE % mclk_rate == 0) { /* need to enable i2s-out path when enable i2s-in */
clk_domain = 0; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
} else if (MT2701_PLL_DOMAIN_1_RATE % mclk_rate == 0) { mt2701_i2s_path_enable(afe, i2s_path, !substream->stream,
clk_domain = 1; substream->runtime->rate);
} else {
dev_err(dai->dev, "%s() bad mclk rate %d\n",
__func__, mclk_rate);
return -EINVAL;
}
mt2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { mt2701_i2s_path_enable(afe, i2s_path, substream->stream,
mt2701_i2s_path_prepare_enable(substream, dai, i2s_num, 0); substream->runtime->rate);
} else {
/* need to enable i2s-out path when enable i2s-in */
/* prepare for another direction "out" */
mt2701_i2s_path_prepare_enable(substream, dai, i2s_num, 1);
/* prepare for "in" */
mt2701_i2s_path_prepare_enable(substream, dai, i2s_num, 0);
}
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment