ASoC: rockchip: i2s_tdm: add support handle 'io-multiplex' property

Some i2s bus GPIOs maybe designed multiplex, the default
is input, we need to configure IN/OUT dynamically.

Change-Id: I2e0f0f972d6f9fa3fc8e8fc9f5dfd5d4e6deaee1
Signed-off-by: Xing Zheng <zhengxing@rock-chips.com>
This commit is contained in:
Xing Zheng
2019-06-20 01:08:18 +08:00
committed by Tao Huang
parent fe27c204eb
commit 6578e192ef
2 changed files with 124 additions and 4 deletions

View File

@@ -31,6 +31,7 @@
#define DEFAULT_MCLK_FS 256
#define CH_GRP_MAX 4 /* The max channel 8 / 2 */
#define MULTIPLEX_CH_MAX 10
struct txrx_config {
u32 addr;
@@ -41,6 +42,8 @@ struct txrx_config {
struct rk_i2s_soc_data {
u32 softrst_offset;
u32 grf_reg_offset;
u32 grf_shift;
int tx_reset_id;
int rx_reset_id;
int config_count;
@@ -76,6 +79,7 @@ struct rk_i2s_tdm_dev {
struct rk_i2s_soc_data *soc_data;
void __iomem *cru_base;
bool is_master_mode;
bool io_multiplex;
bool mclk_calibrate;
bool tdm_mode;
unsigned int mclk_rx_freq;
@@ -88,6 +92,28 @@ struct rk_i2s_tdm_dev {
spinlock_t lock; /* xfer lock */
};
static int to_ch_num(unsigned int val)
{
int chs;
switch (val) {
case I2S_CHN_4:
chs = 4;
break;
case I2S_CHN_6:
chs = 6;
break;
case I2S_CHN_8:
chs = 8;
break;
default:
chs = 2;
break;
}
return chs;
}
static int i2s_tdm_runtime_suspend(struct device *dev)
{
struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
@@ -849,6 +875,86 @@ static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream,
I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
val);
if (i2s_tdm->io_multiplex) {
int usable_chs = MULTIPLEX_CH_MAX;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
struct snd_pcm_str *playback_str =
&substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (playback_str->substream_opened) {
regmap_read(i2s_tdm->regmap, I2S_TXCR, &val);
val &= I2S_TXCR_CSR_MASK;
usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val);
}
regmap_read(i2s_tdm->regmap, I2S_RXCR, &val);
val &= I2S_RXCR_CSR_MASK;
if (to_ch_num(val) > usable_chs) {
dev_err(i2s_tdm->dev,
"Capture chs(%d) > usable chs(%d)\n",
to_ch_num(val), usable_chs);
ret = -EINVAL;
goto err;
}
switch (val) {
case I2S_CHN_4:
val = I2S_IO_6CH_OUT_4CH_IN;
break;
case I2S_CHN_6:
val = I2S_IO_4CH_OUT_6CH_IN;
break;
case I2S_CHN_8:
val = I2S_IO_2CH_OUT_8CH_IN;
break;
default:
val = I2S_IO_8CH_OUT_2CH_IN;
break;
}
} else {
struct snd_pcm_str *capture_str =
&substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE];
if (capture_str->substream_opened) {
regmap_read(i2s_tdm->regmap, I2S_RXCR, &val);
val &= I2S_RXCR_CSR_MASK;
usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val);
}
regmap_read(i2s_tdm->regmap, I2S_TXCR, &val);
val &= I2S_TXCR_CSR_MASK;
if (to_ch_num(val) > usable_chs) {
dev_err(i2s_tdm->dev,
"Playback chs(%d) > usable chs(%d)\n",
to_ch_num(val), usable_chs);
ret = -EINVAL;
goto err;
}
switch (val) {
case I2S_CHN_4:
val = I2S_IO_4CH_OUT_6CH_IN;
break;
case I2S_CHN_6:
val = I2S_IO_6CH_OUT_4CH_IN;
break;
case I2S_CHN_8:
val = I2S_IO_8CH_OUT_2CH_IN;
break;
default:
val = I2S_IO_2CH_OUT_8CH_IN;
break;
}
}
val <<= i2s_tdm->soc_data->grf_shift;
val |= (I2S_IO_DIRECTION_MASK << i2s_tdm->soc_data->grf_shift) << 16;
regmap_write(i2s_tdm->grf, i2s_tdm->soc_data->grf_reg_offset, val);
}
regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
I2S_DMACR_TDL(16));
regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
@@ -1130,6 +1236,8 @@ static struct rk_i2s_soc_data rk1808_i2s_soc_data = {
static struct rk_i2s_soc_data rk3308_i2s_soc_data = {
.softrst_offset = 0x0400,
.grf_reg_offset = 0x0308,
.grf_shift = 5,
.configs = rk3308_txrx_config,
.config_count = ARRAY_SIZE(rk3308_txrx_config),
.init = common_soc_init,
@@ -1381,7 +1489,13 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
i2s_tdm->dev = &pdev->dev;
of_id = of_match_device(rockchip_i2s_tdm_match, &pdev->dev);
if (!of_id || !of_id->data)
return -EINVAL;
spin_lock_init(&i2s_tdm->lock);
i2s_tdm->soc_data = (struct rk_i2s_soc_data *)of_id->data;
i2s_tdm->bclk_fs = 64;
if (!of_property_read_u32(node, "rockchip,bclk-fs", &val)) {
if ((val >= 32) && (val % 2 == 0))
@@ -1411,11 +1525,7 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
i2s_tdm->cru_base = of_iomap(cru_node, 0);
if (!i2s_tdm->cru_base)
return -ENOENT;
of_id = of_match_device(rockchip_i2s_tdm_match, &pdev->dev);
if (!of_id || !of_id->data)
return -EINVAL;
i2s_tdm->soc_data = (struct rk_i2s_soc_data *)of_id->data;
i2s_tdm->soc_data->tx_reset_id = of_i2s_resetid_get(node, "tx-m");
i2s_tdm->soc_data->rx_reset_id = of_i2s_resetid_get(node, "rx-m");
}
@@ -1450,6 +1560,9 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
if (IS_ERR(i2s_tdm->mclk_rx))
return PTR_ERR(i2s_tdm->mclk_rx);
i2s_tdm->io_multiplex =
of_property_read_bool(node, "rockchip,io-multiplex");
i2s_tdm->mclk_calibrate =
of_property_read_bool(node, "rockchip,mclk-calibrate");
if (i2s_tdm->mclk_calibrate) {

View File

@@ -263,6 +263,13 @@ enum {
#define I2S_CHN_6 (2 << I2S_CSR_SHIFT)
#define I2S_CHN_8 (3 << I2S_CSR_SHIFT)
/* io direction cfg register */
#define I2S_IO_DIRECTION_MASK (7)
#define I2S_IO_8CH_OUT_2CH_IN (7)
#define I2S_IO_6CH_OUT_4CH_IN (3)
#define I2S_IO_4CH_OUT_6CH_IN (1)
#define I2S_IO_2CH_OUT_8CH_IN (0)
/* I2S REGS */
#define I2S_TXCR (0x0000)
#define I2S_RXCR (0x0004)