Skip to content

Commit

Permalink
Allwinner: WIP audio passthrough
Browse files Browse the repository at this point in the history
  • Loading branch information
jernejsk committed Jul 7, 2020
1 parent 74156da commit 87cbddb
Show file tree
Hide file tree
Showing 6 changed files with 515 additions and 24 deletions.
150 changes: 150 additions & 0 deletions packages/audio/alsa-lib/patches/alsa-lib-001-iec958-hbr.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
From 04226851079cf2a76937583f7debbdcda56d6f08 Mon Sep 17 00:00:00 2001
From: Matthias Reichl <hias@horus.com>
Date: Tue, 30 Jun 2020 12:45:54 +0200
Subject: [PATCH] pcm_iec958: implement HDMI HBR audio compliant formatting

HBR compressed audio data like DTS HD MA is usually packed into
8-channel PCM frames (typically 192kHz). The HDMI specs state this has
to be formatted as a single IEC958 stream, compared to normal multi-
channel PCM data which has to be formatted as parallel IEC958 streams.

As this single-stream formatting mode may break existing setups that
expect non-PCM multichannel data to be formatted as parallel IEC958
streams it needs to be explicitly selected by setting the hbr_mode
option to true.

If hbr_mode is true and the non-audio channel status bit is set then
the data is formatted accordingly, as contiguous IEC958 frames of
a single IEC958 stream.

Signed-off-by: Matthias Reichl <hias@horus.com>
---
src/pcm/pcm_iec958.c | 37 +++++++++++++++++++++++++++++++++----
1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/src/pcm/pcm_iec958.c b/src/pcm/pcm_iec958.c
index 76d3ca7b..dba41e1e 100644
--- a/src/pcm/pcm_iec958.c
+++ b/src/pcm/pcm_iec958.c
@@ -63,6 +63,7 @@ struct snd_pcm_iec958 {
unsigned int byteswap;
unsigned char preamble[3]; /* B/M/W or Z/X/Y */
snd_pcm_fast_ops_t fops;
+ int hbr_mode;
};

enum { PREAMBLE_Z, PREAMBLE_X, PREAMBLE_Y };
@@ -193,6 +194,8 @@ static void snd_pcm_iec958_encode(snd_pcm_iec958_t *iec,
unsigned int channel;
int32_t sample = 0;
int counter = iec->counter;
+ int single_stream = iec->hbr_mode && (iec->status[0] & IEC958_AES0_NONAUDIO);
+ int counter_step = single_stream ? ((channels + 1) >> 1) : 1;
for (channel = 0; channel < channels; ++channel) {
const char *src;
uint32_t *dst;
@@ -205,7 +208,12 @@ static void snd_pcm_iec958_encode(snd_pcm_iec958_t *iec,
src_step = snd_pcm_channel_area_step(src_area);
dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(uint32_t);
frames1 = frames;
- iec->counter = counter;
+
+ if (single_stream)
+ iec->counter = (counter + (channel >> 1)) % 192;
+ else
+ iec->counter = counter;
+
while (frames1-- > 0) {
goto *get;
#define GET32_END after
@@ -217,9 +225,12 @@ static void snd_pcm_iec958_encode(snd_pcm_iec958_t *iec,
*dst = sample;
src += src_step;
dst += dst_step;
- iec->counter++;
+ iec->counter += counter_step;
iec->counter %= 192;
}
+ if (single_stream) {
+ iec->counter = (counter + frames * counter_step) % 192;
+ }
}
}
#endif /* DOC_HIDDEN */
@@ -473,6 +484,7 @@ static const snd_pcm_ops_t snd_pcm_iec958_ops = {
* \param close_slave When set, the slave PCM handle is closed with copy PCM
* \param status_bits The IEC958 status bits
* \param preamble_vals The preamble byte values
+ * \param hbr_mode When set, enable HDMI HBR compliant formatting
* \retval zero on success otherwise a negative error code
* \warning Using of this function might be dangerous in the sense
* of compatibility reasons. The prototype might be freely
@@ -481,7 +493,8 @@ static const snd_pcm_ops_t snd_pcm_iec958_ops = {
int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat,
snd_pcm_t *slave, int close_slave,
const unsigned char *status_bits,
- const unsigned char *preamble_vals)
+ const unsigned char *preamble_vals,
+ int hbr_mode)
{
snd_pcm_t *pcm;
snd_pcm_iec958_t *iec;
@@ -519,6 +532,8 @@ int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sfo

memcpy(iec->preamble, preamble_vals, 3);

+ iec->hbr_mode = hbr_mode;
+
err = snd_pcm_new(&pcm, SND_PCM_TYPE_IEC958, name, slave->stream, slave->mode);
if (err < 0) {
free(iec);
@@ -566,9 +581,15 @@ pcm.name {
[preamble.z or preamble.b val]
[preamble.x or preamble.m val]
[preamble.y or preamble.w val]
+ [hbr_mode true]
}
\endcode

+When <code>hbr_mode</code> is set true, multichannel compressed
+data is formatted as contiguous frames of a single IEC958 stream.
+Eg 8-channel PCM as typically used for HDMI HBR audio is formatted
+as 4 contiguous iec958 frames.
+
\subsection pcm_plugins_iec958_funcref Function reference

<UL>
@@ -605,6 +626,7 @@ int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name,
unsigned char preamble_vals[3] = {
0x08, 0x02, 0x04 /* Z, X, Y */
};
+ int hbr_mode = 0;

snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
@@ -633,6 +655,13 @@ int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name,
preamble = n;
continue;
}
+ if (strcmp(id, "hbr_mode") == 0) {
+ err = snd_config_get_bool(n);
+ if (err < 0)
+ continue;
+ hbr_mode = err;
+ continue;
+ }
SNDERR("Unknown field %s", id);
return -EINVAL;
}
@@ -707,7 +736,7 @@ int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name,
return err;
err = snd_pcm_iec958_open(pcmp, name, sformat, spcm, 1,
status ? status_bits : NULL,
- preamble_vals);
+ preamble_vals, hbr_mode);
if (err < 0)
snd_pcm_close(spcm);
return err;
--
2.20.1

112 changes: 112 additions & 0 deletions packages/audio/alsa-lib/patches/alsa-lib-002-iec958-format.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
From dec023cd0d5fb6e2adb36de3f3ae2d4a4677950f Mon Sep 17 00:00:00 2001
From: Matthias Reichl <hias@horus.com>
Date: Tue, 30 Jun 2020 17:34:52 +0200
Subject: [PATCH] pcm_iec958: set status bits according to rate and format

This mimics snd_pcm_create_iec958_consumer in the kernel.

The rate and wordlength bits will only be changed if they were
previously set to "not indicated", which is now the default if no
status option is used.

This allows application to override the parameters determined from
the stream which is eg needed for HDMI HBR passthrough (8ch 192kHz
data but status bits need to be set to 768kHz).

Signed-off-by: Matthias Reichl <hias@horus.com>
---
src/pcm/pcm_iec958.c | 71 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 67 insertions(+), 4 deletions(-)

diff --git a/src/pcm/pcm_iec958.c b/src/pcm/pcm_iec958.c
index dba41e1e..dd64aac6 100644
--- a/src/pcm/pcm_iec958.c
+++ b/src/pcm/pcm_iec958.c
@@ -364,9 +364,71 @@ static int snd_pcm_iec958_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params
iec->byteswap = format != SND_PCM_FORMAT_IEC958_SUBFRAME;
}
}
- /* FIXME: needs to adjust status_bits according to the format
- * and sample rate
- */
+
+ if ((iec->status[0] & IEC958_AES0_PROFESSIONAL) == 0) {
+ if ((iec->status[3] & IEC958_AES3_CON_FS) == IEC958_AES3_CON_FS_NOTID) {
+ unsigned int rate = 0;
+ unsigned char fs;
+
+ err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &rate, 0);
+ if (err < 0)
+ rate = 0;
+
+ switch (rate) {
+ case 32000:
+ fs = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ fs = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ fs = IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ fs = IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ fs = IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ fs = IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ fs = IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ fs = IEC958_AES3_CON_FS_NOTID;
+ break;
+ }
+
+ iec->status[3] &= ~IEC958_AES3_CON_FS;
+ iec->status[3] |= fs;
+ }
+
+ if ((iec->status[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) {
+ unsigned char ws;
+ switch (snd_pcm_format_width(format)) {
+ case 16:
+ ws = IEC958_AES4_CON_WORDLEN_20_16;
+ break;
+ case 18:
+ ws = IEC958_AES4_CON_WORDLEN_22_18;
+ break;
+ case 20:
+ ws = IEC958_AES4_CON_WORDLEN_20_16 | IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ case 24:
+ case 32: /* Assume 24-bit width for 32-bit samples. */
+ ws = IEC958_AES4_CON_WORDLEN_24_20 | IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ default:
+ ws = IEC958_AES4_CON_WORDLEN_NOTID;
+ break;
+ }
+ iec->status[4] &= ~(IEC958_AES4_CON_MAX_WORDLEN_24 | IEC958_AES4_CON_WORDLEN);
+ iec->status[4] |= ws;
+ }
+ }
return 0;
}

@@ -503,7 +565,8 @@ int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sfo
IEC958_AES0_CON_EMPHASIS_NONE,
IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
0,
- IEC958_AES3_CON_FS_48000
+ IEC958_AES3_CON_FS_NOTID, /* will be set in hwparams */
+ IEC958_AES4_CON_WORDLEN_NOTID /* will be set in hwparams */
};

assert(pcmp && slave);
--
2.20.1

105 changes: 105 additions & 0 deletions packages/audio/alsa-lib/patches/test.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c
index 2028790eba78..115db2c2cdae 100644
--- a/src/pcm/pcm_hw.c
+++ b/src/pcm/pcm_hw.c
@@ -41,8 +41,8 @@
#include "../control/control_local.h"
#include "../timer/timer_local.h"

-//#define DEBUG_RW /* use to debug readi/writei/readn/writen */
-//#define DEBUG_MMAP /* debug mmap_commit */
+#define DEBUG_RW /* use to debug readi/writei/readn/writen */
+#define DEBUG_MMAP /* debug mmap_commit */

#ifndef PIC
/* entry for static linking */
diff --git a/src/pcm/pcm_iec958.c b/src/pcm/pcm_iec958.c
index dd64aac65d8f..087393b29681 100644
--- a/src/pcm/pcm_iec958.c
+++ b/src/pcm/pcm_iec958.c
@@ -71,8 +71,8 @@ enum { PREAMBLE_Z, PREAMBLE_X, PREAMBLE_Y };
#endif /* DOC_HIDDEN */

/*
- * Determine parity for time slots 4 upto 30
- * to be sure that bit 4 upt 31 will carry
+ * Determine parity for time slots 0 upto 18
+ * to be sure that bit 0 upt 19 will carry
* an even number of ones and zeros.
*/
static unsigned int iec958_parity(unsigned int data)
@@ -80,9 +80,8 @@ static unsigned int iec958_parity(unsigned int data)
unsigned int parity;
int bit;

- data >>= 4; /* start from bit 4 */
parity = 0;
- for (bit = 4; bit <= 30; bit++) {
+ for (bit = 0; bit <= 18; bit++) {
if (data & 1)
parity++;
data >>= 1;
@@ -94,13 +93,12 @@ static unsigned int iec958_parity(unsigned int data)
* Compose 32bit IEC958 subframe, two sub frames
* build one frame with two channels.
*
- * bit 0-3 = preamble
- * 4-7 = AUX (=0)
- * 8-27 = data (12-27 for 16bit, 8-27 for 20bit, and 24bit without AUX)
- * 28 = validity (0 for valid data, else 'in error')
- * 29 = user data (0)
- * 30 = channel status (24 bytes for 192 frames)
- * 31 = parity
+ * bit 0-15 = data
+ * 16 = validity (0 for valid data, else 'in error')
+ * 17 = user data (0)
+ * 18 = channel status (24 bytes for 192 frames)
+ * 19 = parity
+ * 20 = frame start
*/

static inline uint32_t iec958_subframe(snd_pcm_iec958_t *iec, uint32_t data, int channel)
@@ -108,24 +106,20 @@ static inline uint32_t iec958_subframe(snd_pcm_iec958_t *iec, uint32_t data, int
unsigned int byte = iec->counter >> 3;
unsigned int mask = 1 << (iec->counter - (byte << 3));

- /* bit 4-27 */
- data >>= 4;
- data &= ~0xf;
+ data >>= 16;

/* set IEC status bits (up to 192 bits) */
if (iec->status[byte] & mask)
- data |= 0x40000000;
+ data |= 0x40000;

- if (iec958_parity(data)) /* parity bit 4-30 */
- data |= 0x80000000;
+ if (iec958_parity(data))
+ data |= 0x80000;

- /* Preamble */
- if (channel)
- data |= iec->preamble[PREAMBLE_Y]; /* odd sub frame, 'Y' */
- else if (! iec->counter)
- data |= iec->preamble[PREAMBLE_Z]; /* Block start, 'Z' */
- else
- data |= iec->preamble[PREAMBLE_X]; /* even sub frame, 'X' */
+ /* block start */
+ if (!iec->counter)
+ data |= 0x100000;
+
+ data <<= 11;

if (iec->byteswap)
data = bswap_32(data);
@@ -137,8 +131,7 @@ static inline int32_t iec958_to_s32(snd_pcm_iec958_t *iec, uint32_t data)
{
if (iec->byteswap)
data = bswap_32(data);
- data &= ~0xf;
- data <<= 4;
+ data <<= 16;
return (int32_t)data;
}

Loading

0 comments on commit 87cbddb

Please sign in to comment.