/* * ALSA SoC Synopsys I2S Audio Layer * * sound/soc/dwc/designware_i2s.c * * Copyright (C) 2010 ST Microelectronics * Rajeev Kumar <rajeevkumar.linux@gmail.com> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied.
*/
if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < 4; i++)
i2s_write_reg(dev->i2s_base, TER(i), 0);
} else { for (i = 0; i < 4; i++)
i2s_write_reg(dev->i2s_base, RER(i), 0);
}
}
if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < 4; i++)
i2s_read_reg(dev->i2s_base, TOR(i));
} else { for (i = 0; i < 4; i++)
i2s_read_reg(dev->i2s_base, ROR(i));
}
}
staticinlinevoid i2s_disable_irqs(struct dw_i2s_dev *dev, u32 stream, int chan_nr)
{
u32 i, irq;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < (chan_nr / 2); i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
}
} else { for (i = 0; i < (chan_nr / 2); i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
}
}
}
staticinlinevoid i2s_enable_irqs(struct dw_i2s_dev *dev, u32 stream, int chan_nr)
{
u32 i, irq;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < (chan_nr / 2); i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
}
} else { for (i = 0; i < (chan_nr / 2); i++) {
irq = i2s_read_reg(dev->i2s_base, IMR(i));
i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
}
}
}
for (i = 0; i < 4; i++) { /* * Check if TX fifo is empty. If empty fill FIFO with samples * NOTE: Only two channels supported
*/ if ((isr[i] & ISR_TXFE) && (i == 0) && dev->use_pio) {
dw_pcm_push_tx(dev);
irq_valid = true;
}
/* * Data available. Retrieve samples from FIFO * NOTE: Only two channels supported
*/ if ((isr[i] & ISR_RXDA) && (i == 0) && dev->use_pio) {
dw_pcm_pop_rx(dev);
irq_valid = true;
}
switch (config->chan_nr) { case EIGHT_CHANNEL_SUPPORT: case SIX_CHANNEL_SUPPORT: case FOUR_CHANNEL_SUPPORT: case TWO_CHANNEL_SUPPORT: break; default:
dev_err(dev->dev, "channel not supported\n"); return -EINVAL;
}
dw_i2s_config(dev, substream->stream);
i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
config->sample_rate = params_rate(params);
if (dev->capability & DW_I2S_MASTER) { if (dev->i2s_clk_cfg) {
ret = dev->i2s_clk_cfg(config); if (ret < 0) {
dev_err(dev->dev, "runtime audio clk config fail\n"); return ret;
}
} else {
u32 bitclk = config->sample_rate *
config->data_width * 2;
ret = clk_set_rate(dev->clk, bitclk); if (ret) {
dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
ret); return ret;
}
}
} return 0;
}
staticint dw_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); int ret = 0;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
dev->active++;
i2s_start(dev, substream); break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dev->active--;
i2s_stop(dev, substream); break; default:
ret = -EINVAL; break;
} return ret;
}
staticint dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsignedint fmt)
{ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); int ret = 0;
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BC_FC: if (dev->capability & DW_I2S_SLAVE)
ret = 0; else
ret = -EINVAL; break; case SND_SOC_DAIFMT_BP_FP: if (dev->capability & DW_I2S_MASTER)
ret = 0; else
ret = -EINVAL; break; case SND_SOC_DAIFMT_BC_FP: case SND_SOC_DAIFMT_BP_FC:
ret = -EINVAL; break; default:
dev_dbg(dev->dev, "dwc : Invalid clock provider format\n");
ret = -EINVAL; break;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_RIGHT_J: break; case SND_SOC_DAIFMT_DSP_A:
dev->frame_offset = 1; break; case SND_SOC_DAIFMT_DSP_B:
dev->frame_offset = 0; break; default:
dev_err(dev->dev, "DAI format unsupported"); return -EINVAL;
}
return ret;
}
staticint dw_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, unsignedint tx_mask, unsignedint rx_mask, int slots, int slot_width)
{ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
/* * The following tables allow a direct lookup of various parameters * defined in the I2S block's configuration in terms of sound system * parameters. Each table is sized to the number of entries possible * according to the number of configuration bits describing an I2S * block parameter.
*/
/* Maximum bit resolution of a channel - not uniformly spaced */ staticconst u32 fifo_width[COMP_MAX_WORDSIZE] = {
12, 16, 20, 24, 32, 0, 0, 0
};
/* Width of (DMA) bus */ staticconst u32 bus_widths[COMP_MAX_DATA_WIDTH] = {
DMA_SLAVE_BUSWIDTH_1_BYTE,
DMA_SLAVE_BUSWIDTH_2_BYTES,
DMA_SLAVE_BUSWIDTH_4_BYTES,
DMA_SLAVE_BUSWIDTH_UNDEFINED
};
/* PCM format to support channel resolution */ staticconst u32 formats[COMP_MAX_WORDSIZE] = {
SNDRV_PCM_FMTBIT_S16_LE,
SNDRV_PCM_FMTBIT_S16_LE,
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
0,
0,
0
};
if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) return -EINVAL;
ret = dw_configure_dai(dev, dw_i2s_dai, pdata->snd_rates); if (ret < 0) return ret;
if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
idx = 1;
if (dev->is_jh7110) { /* Use platform data and snd_dmaengine_dai_dma_data struct at the same time */
u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
u32 idx2;
ret = clk_prepare_enable(pclk); if (ret) gotoexit;
/* Use inner mclk first and avoid uninitialized gpio for external mclk */
ret = clk_set_parent(mclk, mclk_inner); if (ret) goto err_dis_pclk;
ret = clk_prepare_enable(bclk_mst); if (ret) goto err_dis_pclk;
/* deassert resets before set clock parent */
ret = reset_control_deassert(resets); if (ret) goto err_dis_all;
/* external clock (12.288MHz) for Audio */
ret = clk_set_parent(mclk, mclk_ext); if (ret) goto err_dis_all;
/* i2sclk will be got and enabled repeatedly later and should be disabled now. */
clk_disable_unprepare(bclk_mst);
clk_bulk_put(ARRAY_SIZE(clks), clks);
dev->is_jh7110 = true;
ret = clk_prepare_enable(pclk); if (ret) gotoexit;
ret = clk_set_parent(mclk, mclk_inner); if (ret) goto err_dis_pclk;
ret = clk_prepare_enable(bclk_mst); if (ret) goto err_dis_pclk;
ret = reset_control_deassert(resets); if (ret) goto err_dis_all;
/* The sources of BCLK and LRCK are the external codec. */
ret = clk_set_parent(bclk, bclk_ext); if (ret) goto err_dis_all;
ret = clk_set_parent(lrck, lrck_ext); if (ret) goto err_dis_all;
ret = clk_set_parent(mclk, mclk_ext); if (ret) goto err_dis_all;
/* The i2sclk will be got and enabled repeatedly later and should be disabled now. */
clk_disable_unprepare(bclk_mst);
clk_bulk_put(ARRAY_SIZE(clks), clks);
dev->is_jh7110 = true;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.