/* * Clear the FIFOs * Requires at least 2 PCM clock cycles to take effect
*/
regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, clr, clr);
/* Wait for 2 PCM clock cycles */
/* * Toggle the SYNC flag. After 2 PCM clock cycles it can be read back * FIXME: This does not seem to work for slave mode!
*/
regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &syncval);
syncval &= BCM2835_I2S_SYNC;
/* Wait for the SYNC flag changing it's state */ while (--timeout) {
regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg); if ((csreg & BCM2835_I2S_SYNC) != syncval) break;
}
if (!timeout)
dev_err(dev->dev, "I2S SYNC error!\n");
/* Stop clock if it was not running before */ if (!clk_was_prepared)
bcm2835_i2s_stop_clock(dev);
staticint bcm2835_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai, unsignedint tx_mask, unsignedint rx_mask, int slots, int width)
{ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
if (slots) { if (slots < 0 || width < 0) return -EINVAL;
/* Limit masks to available slots */
rx_mask &= GENMASK(slots - 1, 0);
tx_mask &= GENMASK(slots - 1, 0);
/* * The driver is limited to 2-channel setups. * Check that exactly 2 bits are set in the masks.
*/ if (hweight_long((unsignedlong) rx_mask) != 2
|| hweight_long((unsignedlong) tx_mask) != 2) return -EINVAL;
if (slots * width > BCM2835_I2S_MAX_FRAME_LENGTH) return -EINVAL;
}
/* * Convert logical slot number into physical slot number. * * If odd_offset is 0 sequential number is identical to logical number. * This is used for DSP modes with slot numbering 0 1 2 3 ... * * Otherwise odd_offset defines the physical offset for odd numbered * slots. This is used for I2S and left/right justified modes to * translate from logical slot numbers 0 1 2 3 ... into physical slot * numbers 0 2 ... 3 4 ...
*/ staticint bcm2835_i2s_convert_slot(unsignedint slot, unsignedint odd_offset)
{ if (!odd_offset) return slot;
if (slot & 1) return (slot >> 1) + odd_offset;
return slot >> 1;
}
/* * Calculate channel position from mask and slot width. * * Mask must contain exactly 2 set bits. * Lowest set bit is channel 1 position, highest set bit channel 2. * The constant offset is added to both channel positions. * * If odd_offset is > 0 slot positions are translated to * I2S-style TDM slot numbering ( 0 2 ... 3 4 ...) with odd * logical slot numbers starting at physical slot odd_offset.
*/ staticvoid bcm2835_i2s_calc_channel_pos( unsignedint *ch1_pos, unsignedint *ch2_pos, unsignedint mask, unsignedint width, unsignedint bit_offset, unsignedint odd_offset)
{
*ch1_pos = bcm2835_i2s_convert_slot((ffs(mask) - 1), odd_offset)
* width + bit_offset;
*ch2_pos = bcm2835_i2s_convert_slot((fls(mask) - 1), odd_offset)
* width + bit_offset;
}
frame_length = snd_soc_params_to_frame_size(params); if (frame_length < 0) return frame_length;
bclk_rate = snd_soc_params_to_bclk(params); if (bclk_rate < 0) return bclk_rate;
}
/* Check if data fits into slots */ if (data_length > slot_width) return -EINVAL;
/* Check if CPU is bit clock provider */ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BP_FP: case SND_SOC_DAIFMT_BP_FC:
bit_clock_provider = true; break; case SND_SOC_DAIFMT_BC_FP: case SND_SOC_DAIFMT_BC_FC:
bit_clock_provider = false; break; default: return -EINVAL;
}
/* Check if CPU is frame sync provider */ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BP_FP: case SND_SOC_DAIFMT_BC_FP:
frame_sync_provider = true; break; case SND_SOC_DAIFMT_BP_FC: case SND_SOC_DAIFMT_BC_FC:
frame_sync_provider = false; break; default: return -EINVAL;
}
/* Clock should only be set up here if CPU is clock master */ if (bit_clock_provider &&
(!dev->clk_prepared || dev->clk_rate != bclk_rate)) { if (dev->clk_prepared)
bcm2835_i2s_stop_clock(dev);
if (dev->clk_rate != bclk_rate) {
ret = clk_set_rate(dev->clk, bclk_rate); if (ret) return ret;
dev->clk_rate = bclk_rate;
}
bcm2835_i2s_start_clock(dev);
}
/* Setup the frame format */
format = BCM2835_I2S_CHEN;
if (data_length >= 24)
format |= BCM2835_I2S_CHWEX;
format |= BCM2835_I2S_CHWID((data_length-8)&0xf);
/* CH2 format is the same as for CH1 */
format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format);
switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: /* I2S mode needs an even number of slots */ if (slots & 1) return -EINVAL;
/* * Use I2S-style logical slot numbering: even slots * are in first half of frame, odd slots in second half.
*/
odd_slot_offset = slots >> 1;
/* MSB starts one cycle after frame start */
data_delay = 1;
/* Setup frame sync signal for 50% duty cycle */
framesync_length = frame_length / 2;
frame_start_falling_edge = true; break; case SND_SOC_DAIFMT_LEFT_J: if (slots & 1) return -EINVAL;
/* * Transmitting data immediately after frame start, eg * in left-justified or DSP mode A, only works stable * if bcm2835 is the frame clock provider.
*/ if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_provider)
dev_warn(dev->dev, "Unstable consumer config detected, L/R may be swapped");
/* * Set format for both streams. * We cannot set another frame length * (and therefore word length) anyway, * so the format will be the same.
*/
regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG,
format
| BCM2835_I2S_CH1_POS(rx_ch1_pos)
| BCM2835_I2S_CH2_POS(rx_ch2_pos));
regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG,
format
| BCM2835_I2S_CH1_POS(tx_ch1_pos)
| BCM2835_I2S_CH2_POS(tx_ch2_pos));
/* Setup the I2S mode */
if (data_length <= 16) { /* * Use frame packed mode (2 channels per 32 bit word) * We cannot set another frame length in the second stream * (and therefore word length) anyway, * so the format will be the same.
*/
mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP;
}
/* * Clear both FIFOs if the one that should be started * is not empty at the moment. This should only happen * after overrun. Otherwise, hw_params would have cleared * the FIFO.
*/
regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &cs_reg);
/* Stop also the clock when not SND_SOC_DAIFMT_CONT */ if (!snd_soc_dai_active(dai) && !(dev->fmt & SND_SOC_DAIFMT_CONT))
bcm2835_i2s_stop_clock(dev);
}
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
bcm2835_i2s_stop(dev, substream, dai); break; default: return -EINVAL;
}
/* * Disable STBY. * Requires at least 4 PCM clock cycles to take effect.
*/
regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
BCM2835_I2S_STBY, BCM2835_I2S_STBY);
dev = devm_kzalloc(&pdev->dev, sizeof(*dev),
GFP_KERNEL); if (!dev) return -ENOMEM;
/* get the clock */
dev->clk_prepared = false;
dev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk), "could not get clk\n");
/* Request ioarea */
base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base);
dev->i2s_regmap = devm_regmap_init_mmio(&pdev->dev, base,
&bcm2835_regmap_config); if (IS_ERR(dev->i2s_regmap)) return PTR_ERR(dev->i2s_regmap);
/* Set the DMA address - we have to parse DT ourselves */
addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL); if (!addr) {
dev_err(&pdev->dev, "could not get DMA-register address\n"); return -EINVAL;
}
dma_base = be32_to_cpup(addr);
/* Set the bus width */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =
DMA_SLAVE_BUSWIDTH_4_BYTES;
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr_width =
DMA_SLAVE_BUSWIDTH_4_BYTES;
/* Set burst */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2;
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2;
/* * Set the PACK flag to enable S16_LE support (2 S16_LE values * packed into 32-bit transfers).
*/
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].flags =
SND_DMAENGINE_PCM_DAI_FLAG_PACK;
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags =
SND_DMAENGINE_PCM_DAI_FLAG_PACK;
/* Store the pdev */
dev->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, dev);
ret = devm_snd_soc_register_component(&pdev->dev,
&bcm2835_i2s_component, &bcm2835_i2s_dai, 1); if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); return ret;
}
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) {
dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); return ret;
}
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.