/* * Convert to ADV7511 HDMI hardware format. * ADV7511 HDMI chip need parity bit replaced by block start bit and * with the preamble bits left out. * ALSA IEC958 subframe format: * bit 0-3 = preamble (0x8 = block start) * 4-7 = AUX (=0) * 8-27 = audio data (without AUX if 24bit sample) * 28 = validity * 29 = user data * 30 = channel status * 31 = parity * * ADV7511 IEC958 subframe format: * bit 0-23 = audio data * 24 = validity * 25 = user data * 26 = channel status * 27 = block start * 28-31 = 0 * MSB to LSB bit reverse by software as hardware not supporting it.
*/ staticvoid hdmi_reformat_iec958(struct snd_pcm_runtime *runtime, struct kmb_i2s_info *kmb_i2s, unsignedint tx_ptr)
{
u32(*buf)[2] = (void *)runtime->dma_area; unsignedlong temp;
u32 i, j, sample;
for (i = 0; i < kmb_i2s->fifo_th; i++) {
j = 0; do {
temp = buf[tx_ptr][j]; /* Replace parity with block start*/
assign_bit(31, &temp, (BIT(3) & temp));
sample = bitrev32(temp);
buf[tx_ptr][j] = sample << 4;
j++;
} while (j < 2);
tx_ptr++;
}
}
/* Disable all channels regardless of configuration*/ if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < MAX_ISR; i++)
writel(0, kmb_i2s->i2s_base + TER(i));
} else { for (i = 0; i < MAX_ISR; i++)
writel(0, kmb_i2s->i2s_base + RER(i));
}
}
for (i = 0; i < config->chan_nr / 2; i++)
isr[i] = readl(kmb_i2s->i2s_base + ISR(i));
kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE); /* Only check TX interrupt if TX is active */
tx_enabled = readl(kmb_i2s->i2s_base + ITER);
/* * Data available. Retrieve samples from FIFO
*/
/* * 8 channel audio will have isr[0..2] triggered, * reading the specific isr based on the audio configuration, * to avoid reading the buffers too early.
*/ switch (config->chan_nr) { case2: if (isr[0] & ISR_RXDA)
kmb_pcm_operation(kmb_i2s, false);
ret = IRQ_HANDLED; break; case4: if (isr[1] & ISR_RXDA)
kmb_pcm_operation(kmb_i2s, false);
ret = IRQ_HANDLED; break; case8: if (isr[3] & ISR_RXDA)
kmb_pcm_operation(kmb_i2s, false);
ret = IRQ_HANDLED; break;
}
for (i = 0; i < config->chan_nr / 2; i++) { /* * Check if TX fifo is empty. If empty fill FIFO with samples
*/ if ((isr[i] & ISR_TXFE) && tx_enabled) {
kmb_pcm_operation(kmb_i2s, true);
ret = IRQ_HANDLED;
}
/* Error Handling: TX */ if (isr[i] & ISR_TXFO) {
dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i);
ret = IRQ_HANDLED;
} /* Error Handling: RX */ if (isr[i] & ISR_RXFO) {
dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i);
ret = IRQ_HANDLED;
}
}
return ret;
}
staticint kmb_platform_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *soc_runtime)
{
size_t size = kmb_pcm_hardware.buffer_bytes_max; /* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */
snd_pcm_set_managed_buffer_all(soc_runtime->pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
NULL, size, size); return0;
}
switch (cmd) { case SNDRV_PCM_TRIGGER_START: /* Keep track of i2s activity before turn off * the i2s interface
*/
kmb_i2s->active++;
kmb_i2s_start(kmb_i2s, substream); break; case SNDRV_PCM_TRIGGER_STOP:
kmb_i2s->active--; if (kmb_i2s->use_pio)
kmb_i2s_stop(kmb_i2s, substream); break; default: return -EINVAL;
}
switch (config->chan_nr) { case8: case4: /* * Platform is not capable of providing clocks for * multi channel audio
*/ if (kmb_i2s->clock_provider) return -EINVAL;
writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); break; case2: /* * Platform is only capable of providing clocks need for * 2 channel master mode
*/ if (!(kmb_i2s->clock_provider)) return -EINVAL;
/* Prepare the related clocks */
kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk"); if (IS_ERR(kmb_i2s->clk_apb)) {
dev_err(dev, "Failed to get apb clock\n"); return PTR_ERR(kmb_i2s->clk_apb);
}
ret = clk_prepare_enable(kmb_i2s->clk_apb); if (ret < 0) return ret;
ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb); if (ret) {
dev_err(dev, "Failed to add clk_apb reset action\n"); return ret;
}
kmb_i2s->clk_i2s = devm_clk_get(dev, "osc"); if (IS_ERR(kmb_i2s->clk_i2s)) {
dev_err(dev, "Failed to get osc clock\n"); return PTR_ERR(kmb_i2s->clk_i2s);
}
kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(kmb_i2s->i2s_base)) return PTR_ERR(kmb_i2s->i2s_base);
kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(kmb_i2s->pss_base)) return PTR_ERR(kmb_i2s->pss_base);
if (kmb_i2s->use_pio) {
irq = platform_get_irq_optional(pdev, 0); if (irq > 0) {
ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0,
pdev->name, kmb_i2s); if (ret < 0) {
dev_err(dev, "failed to request irq\n"); return ret;
}
}
ret = devm_snd_soc_register_component(dev, &kmb_component,
kmb_i2s_dai, 1);
} else {
kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA;
kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA;
ret = snd_dmaengine_pcm_register(&pdev->dev,
NULL, 0); if (ret) {
dev_err(&pdev->dev, "could not register dmaengine: %d\n",
ret); return ret;
}
ret = devm_snd_soc_register_component(dev, &kmb_component_dma,
kmb_i2s_dai, 1);
}
if (ret) {
dev_err(dev, "not able to register dai\n"); return ret;
}
/* To ensure none of the channels are enabled at boot up */
kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver");
MODULE_AUTHOR("Sia Jee Heng <jee.heng.sia@intel.com>");
MODULE_AUTHOR("Sit, Michael Wei Hong <michael.wei.hong.sit@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:kmb_platform");
Messung V0.5 in Prozent
¤ 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.0.5Bemerkung:
(vorverarbeitet am 2026-06-07)
¤
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.