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: Ryder Lee <ryder.lee@mediatek.com> Reviewed-by: Garlic Tseng <garlic.tseng@mediatek.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Родитель
a74d51ba0e
Коммит
cf87027360
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (c) 2016 MediaTek Inc.
|
||||
* 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
|
||||
* 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)
|
|||
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;
|
||||
|
||||
ret = clk_prepare_enable(i2s_path->asrco_ck);
|
||||
|
@ -128,11 +129,10 @@ err_hop_ck:
|
|||
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->asrco_ck);
|
||||
}
|
||||
|
@ -272,27 +272,32 @@ int mt2701_afe_disable_clock(struct mtk_base_afe *afe)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
|
||||
int mclk)
|
||||
int mt2701_mclk_configuration(struct mtk_base_afe *afe, int id)
|
||||
|
||||
{
|
||||
struct mt2701_afe_private *priv = afe->platform_priv;
|
||||
struct mt2701_i2s_path *i2s_path = &priv->i2s_path[id];
|
||||
int ret;
|
||||
int ret = -EINVAL;
|
||||
|
||||
/* Set mclk source */
|
||||
if (domain == 0)
|
||||
if (!(MT2701_PLL_DOMAIN_0_RATE % i2s_path->mclk_rate))
|
||||
ret = clk_set_parent(i2s_path->sel_ck,
|
||||
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,
|
||||
priv->base_ck[MT2701_TOP_AUD_MCLK_SRC1]);
|
||||
|
||||
if (ret)
|
||||
dev_err(afe->dev, "failed to set domain%d mclk source %d\n",
|
||||
domain, ret);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "failed to set mclk source\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set mclk divider */
|
||||
ret = clk_set_rate(i2s_path->div_ck, mclk);
|
||||
if (ret)
|
||||
ret = clk_set_rate(i2s_path->div_ck, i2s_path->mclk_rate);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "failed to set mclk divider %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (c) 2016 MediaTek Inc.
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -18,20 +19,24 @@
|
|||
#define _MT2701_AFE_CLOCK_CTRL_H_
|
||||
|
||||
struct mtk_base_afe;
|
||||
struct mt2701_i2s_path;
|
||||
|
||||
int mt2701_init_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_enable_i2s(struct mtk_base_afe *afe, int id, int dir);
|
||||
void mt2701_afe_disable_i2s(struct mtk_base_afe *afe, int id, int dir);
|
||||
int mt2701_afe_enable_i2s(struct mtk_base_afe *afe,
|
||||
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);
|
||||
void mt2701_afe_disable_mclk(struct mtk_base_afe *afe, int id);
|
||||
|
||||
int mt2701_enable_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 mclk);
|
||||
int mt2701_mclk_configuration(struct mtk_base_afe *afe, int id);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
*
|
||||
* Copyright (c) 2016 MediaTek Inc.
|
||||
* 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
|
||||
* 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,
|
|||
return mt2701_afe_enable_mclk(afe, i2s_num);
|
||||
}
|
||||
|
||||
static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai,
|
||||
int i2s_num,
|
||||
int dir_invert)
|
||||
static int mt2701_afe_i2s_path_disable(struct mtk_base_afe *afe,
|
||||
struct mt2701_i2s_path *i2s_path,
|
||||
int stream_dir)
|
||||
{
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
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;
|
||||
const struct mt2701_i2s_data *i2s_data = i2s_path->i2s_data[stream_dir];
|
||||
|
||||
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) {
|
||||
dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n",
|
||||
i2s_path->on[stream_dir], stream_dir);
|
||||
if (--i2s_path->on[stream_dir] < 0)
|
||||
i2s_path->on[stream_dir] = 0;
|
||||
}
|
||||
|
||||
if (i2s_path->on[stream_dir])
|
||||
return 0;
|
||||
|
||||
|
@ -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,
|
||||
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;
|
||||
}
|
||||
|
@ -154,48 +139,32 @@ static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream,
|
|||
if (i2s_path->occupied[substream->stream])
|
||||
i2s_path->occupied[substream->stream] = 0;
|
||||
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 */
|
||||
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 */
|
||||
mt2701_afe_disable_mclk(afe, i2s_num);
|
||||
}
|
||||
|
||||
static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai,
|
||||
int i2s_num,
|
||||
int dir_invert)
|
||||
static int mt2701_i2s_path_enable(struct mtk_base_afe *afe,
|
||||
struct mt2701_i2s_path *i2s_path,
|
||||
int stream_dir, int rate)
|
||||
{
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
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;
|
||||
const struct mt2701_i2s_data *i2s_data = i2s_path->i2s_data[stream_dir];
|
||||
int reg, fs, w_len = 1; /* now we support bck 64bits only */
|
||||
int stream_dir = substream->stream;
|
||||
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 */
|
||||
i2s_path->on[stream_dir]++;
|
||||
|
||||
if (i2s_path->on[stream_dir] != 1)
|
||||
if (++i2s_path->on[stream_dir] != 1)
|
||||
return 0;
|
||||
|
||||
fs = mt2701_afe_i2s_fs(runtime->rate);
|
||||
fs = mt2701_afe_i2s_fs(rate);
|
||||
|
||||
mask = ASYS_I2S_CON_FS |
|
||||
ASYS_I2S_CON_I2S_COUPLE_MODE | /* 0 */
|
||||
|
@ -209,22 +178,20 @@ static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
|
|||
if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
mask |= 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);
|
||||
|
||||
if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
reg = ASMO_TIMING_CON1;
|
||||
else
|
||||
reg = ASMI_TIMING_CON1;
|
||||
|
||||
regmap_update_bits(afe->regmap, reg,
|
||||
i2s_data->i2s_asrc_fs_mask
|
||||
<< i2s_data->i2s_asrc_fs_shift,
|
||||
fs << i2s_data->i2s_asrc_fs_shift);
|
||||
|
||||
/* 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 */
|
||||
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,
|
|||
static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int clk_domain;
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
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;
|
||||
int mclk_rate;
|
||||
|
||||
if (i2s_num < 0)
|
||||
return i2s_num;
|
||||
|
||||
i2s_path = &afe_priv->i2s_path[i2s_num];
|
||||
mclk_rate = i2s_path->mclk_rate;
|
||||
|
||||
if (i2s_path->occupied[substream->stream])
|
||||
return -EBUSY;
|
||||
|
||||
ret = mt2701_mclk_configuration(afe, i2s_num);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2s_path->occupied[substream->stream] = 1;
|
||||
|
||||
if (MT2701_PLL_DOMAIN_0_RATE % mclk_rate == 0) {
|
||||
clk_domain = 0;
|
||||
} else if (MT2701_PLL_DOMAIN_1_RATE % mclk_rate == 0) {
|
||||
clk_domain = 1;
|
||||
} 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);
|
||||
/* need to enable i2s-out path when enable i2s-in */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
mt2701_i2s_path_enable(afe, i2s_path, !substream->stream,
|
||||
substream->runtime->rate);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
mt2701_i2s_path_prepare_enable(substream, dai, i2s_num, 0);
|
||||
} 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);
|
||||
}
|
||||
mt2701_i2s_path_enable(afe, i2s_path, substream->stream,
|
||||
substream->runtime->rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче