Commit 1c5f6e07 authored by Sia Jee Heng's avatar Sia Jee Heng Committed by Mark Brown

ASoC: Intel: KMB: Support IEC958 encoded PCM format

Support ALSA IEC958 plugin for KeemBay I2S driver.
Bit manipulation needed as IEC958 format supported by ADV7511 HDMI chip
is not compatible with the ALSA IEC958 plugin format.
Signed-off-by: default avatarSia Jee Heng <jee.heng.sia@intel.com>
Link: https://lore.kernel.org/r/20210204014258.10197-5-jee.heng.sia@intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 28785f54
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
// Intel KeemBay Platform driver. // Intel KeemBay Platform driver.
// //
#include <linux/bitrev.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/io.h> #include <linux/io.h>
...@@ -39,7 +40,8 @@ static const struct snd_pcm_hardware kmb_pcm_hardware = { ...@@ -39,7 +40,8 @@ static const struct snd_pcm_hardware kmb_pcm_hardware = {
.rate_max = 48000, .rate_max = 48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | .formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE, SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
.buffer_bytes_max = BUFFER_BYTES_MAX, .buffer_bytes_max = BUFFER_BYTES_MAX,
...@@ -50,6 +52,50 @@ static const struct snd_pcm_hardware kmb_pcm_hardware = { ...@@ -50,6 +52,50 @@ static const struct snd_pcm_hardware kmb_pcm_hardware = {
.fifo_size = 16, .fifo_size = 16,
}; };
/*
* 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.
*/
static void hdmi_reformat_iec958(struct snd_pcm_runtime *runtime,
struct kmb_i2s_info *kmb_i2s,
unsigned int tx_ptr)
{
u32(*buf)[2] = (void *)runtime->dma_area;
unsigned long 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++;
}
}
static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s, static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s,
struct snd_pcm_runtime *runtime, struct snd_pcm_runtime *runtime,
unsigned int tx_ptr, bool *period_elapsed) unsigned int tx_ptr, bool *period_elapsed)
...@@ -65,6 +111,8 @@ static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s, ...@@ -65,6 +111,8 @@ static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s,
writel(((u16(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0)); writel(((u16(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
writel(((u16(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0)); writel(((u16(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
} else { } else {
if (kmb_i2s->iec958_fmt)
hdmi_reformat_iec958(runtime, kmb_i2s, tx_ptr);
writel(((u32(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0)); writel(((u32(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
writel(((u32(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0)); writel(((u32(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
} }
...@@ -235,6 +283,7 @@ static int kmb_pcm_trigger(struct snd_soc_component *component, ...@@ -235,6 +283,7 @@ static int kmb_pcm_trigger(struct snd_soc_component *component,
kmb_i2s->tx_substream = NULL; kmb_i2s->tx_substream = NULL;
else else
kmb_i2s->rx_substream = NULL; kmb_i2s->rx_substream = NULL;
kmb_i2s->iec958_fmt = false;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -549,6 +598,9 @@ static int kmb_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -549,6 +598,9 @@ static int kmb_dai_hw_params(struct snd_pcm_substream *substream,
kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break; break;
case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
kmb_i2s->iec958_fmt = true;
fallthrough;
case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S32_LE:
config->data_width = 32; config->data_width = 32;
kmb_i2s->ccr = 0x10; kmb_i2s->ccr = 0x10;
...@@ -686,6 +738,24 @@ static struct snd_soc_dai_ops kmb_dai_ops = { ...@@ -686,6 +738,24 @@ static struct snd_soc_dai_ops kmb_dai_ops = {
.set_fmt = kmb_set_dai_fmt, .set_fmt = kmb_set_dai_fmt,
}; };
static struct snd_soc_dai_driver intel_kmb_hdmi_dai[] = {
{
.name = "intel_kmb_hdmi_i2s",
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE),
},
.ops = &kmb_dai_ops,
.probe = kmb_probe,
},
};
static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = { static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = {
{ {
.name = "intel_kmb_i2s", .name = "intel_kmb_i2s",
...@@ -740,6 +810,7 @@ static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = { ...@@ -740,6 +810,7 @@ static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = {
static const struct of_device_id kmb_plat_of_match[] = { static const struct of_device_id kmb_plat_of_match[] = {
{ .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai}, { .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai},
{ .compatible = "intel,keembay-hdmi-i2s", .data = &intel_kmb_hdmi_dai},
{ .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai}, { .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai},
{} {}
}; };
......
...@@ -150,6 +150,7 @@ struct kmb_i2s_info { ...@@ -150,6 +150,7 @@ struct kmb_i2s_info {
struct snd_pcm_substream *rx_substream; struct snd_pcm_substream *rx_substream;
unsigned int tx_ptr; unsigned int tx_ptr;
unsigned int rx_ptr; unsigned int rx_ptr;
bool iec958_fmt;
}; };
#endif /* KMB_PLATFORM_H_ */ #endif /* KMB_PLATFORM_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment