aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-3.14/0032-snd-bcm2835-Add-support-for-spdif-hdmi-passthrough.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-3.14/0032-snd-bcm2835-Add-support-for-spdif-hdmi-passthrough.patch')
-rw-r--r--target/linux/brcm2708/patches-3.14/0032-snd-bcm2835-Add-support-for-spdif-hdmi-passthrough.patch473
1 files changed, 473 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.14/0032-snd-bcm2835-Add-support-for-spdif-hdmi-passthrough.patch b/target/linux/brcm2708/patches-3.14/0032-snd-bcm2835-Add-support-for-spdif-hdmi-passthrough.patch
new file mode 100644
index 0000000000..4b6dcbc18e
--- /dev/null
+++ b/target/linux/brcm2708/patches-3.14/0032-snd-bcm2835-Add-support-for-spdif-hdmi-passthrough.patch
@@ -0,0 +1,473 @@
+From 2172c6578ef13acb8fcf5cac643cb1ee8e824117 Mon Sep 17 00:00:00 2001
+From: Julian Scheel <julian@jusst.de>
+Date: Wed, 19 Feb 2014 16:06:59 +0100
+Subject: [PATCH 32/54] snd-bcm2835: Add support for spdif/hdmi passthrough
+
+This adds a dedicated subdevice which can be used for passthrough of non-audio
+formats (ie encoded a52) through the hdmi audio link. In addition to this
+driver extension an appropriate card config is required to make alsa-lib
+support the AES parameters for this device.
+---
+ sound/arm/bcm2835-ctl.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++
+ sound/arm/bcm2835-pcm.c | 128 +++++++++++++++++++++++++++++++++++++++++-------
+ sound/arm/bcm2835.c | 9 +++-
+ sound/arm/bcm2835.h | 9 ++++
+ 4 files changed, 250 insertions(+), 19 deletions(-)
+
+diff --git a/sound/arm/bcm2835-ctl.c b/sound/arm/bcm2835-ctl.c
+index 8c5334a..aad905f 100755
+--- a/sound/arm/bcm2835-ctl.c
++++ b/sound/arm/bcm2835-ctl.c
+@@ -30,6 +30,7 @@
+ #include <sound/rawmidi.h>
+ #include <sound/initval.h>
+ #include <sound/tlv.h>
++#include <sound/asoundef.h>
+
+ #include "bcm2835.h"
+
+@@ -183,6 +184,122 @@ static struct snd_kcontrol_new snd_bcm2835_ctl[] = {
+ },
+ };
+
++static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
++ uinfo->count = 1;
++ return 0;
++}
++
++static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
++ int i;
++
++ for (i = 0; i < 4; i++)
++ ucontrol->value.iec958.status[i] =
++ (chip->spdif_status >> (i * 8)) && 0xff;
++
++ return 0;
++}
++
++static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
++ unsigned int val = 0;
++ int i, change;
++
++ for (i = 0; i < 4; i++)
++ val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
++
++ change = val != chip->spdif_status;
++ chip->spdif_status = val;
++
++ return change;
++}
++
++static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
++ uinfo->count = 1;
++ return 0;
++}
++
++static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ /* bcm2835 supports only consumer mode and sets all other format flags
++ * automatically. So the only thing left is signalling non-audio
++ * content */
++ ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO;
++ return 0;
++}
++
++static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
++ uinfo->count = 1;
++ return 0;
++}
++
++static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
++ int i;
++
++ for (i = 0; i < 4; i++)
++ ucontrol->value.iec958.status[i] =
++ (chip->spdif_status >> (i * 8)) & 0xff;
++ return 0;
++}
++
++static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
++ unsigned int val = 0;
++ int i, change;
++
++ for (i = 0; i < 4; i++)
++ val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
++ change = val != chip->spdif_status;
++ chip->spdif_status = val;
++
++ return change;
++}
++
++static struct snd_kcontrol_new snd_bcm2835_spdif[] = {
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
++ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
++ .info = snd_bcm2835_spdif_default_info,
++ .get = snd_bcm2835_spdif_default_get,
++ .put = snd_bcm2835_spdif_default_put
++ },
++ {
++ .access = SNDRV_CTL_ELEM_ACCESS_READ,
++ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
++ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
++ .info = snd_bcm2835_spdif_mask_info,
++ .get = snd_bcm2835_spdif_mask_get,
++ },
++ {
++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
++ SNDRV_CTL_ELEM_ACCESS_INACTIVE,
++ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
++ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
++ .info = snd_bcm2835_spdif_stream_info,
++ .get = snd_bcm2835_spdif_stream_get,
++ .put = snd_bcm2835_spdif_stream_put,
++ },
++};
++
+ int snd_bcm2835_new_ctl(bcm2835_chip_t * chip)
+ {
+ int err;
+@@ -196,5 +313,11 @@ int snd_bcm2835_new_ctl(bcm2835_chip_t * chip)
+ if (err < 0)
+ return err;
+ }
++ for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) {
++ err = snd_ctl_add(chip->card,
++ snd_ctl_new1(&snd_bcm2835_spdif[idx], chip));
++ if (err < 0)
++ return err;
++ }
+ return 0;
+ }
+diff --git a/sound/arm/bcm2835-pcm.c b/sound/arm/bcm2835-pcm.c
+index b4084bb..ebd3f62 100755
+--- a/sound/arm/bcm2835-pcm.c
++++ b/sound/arm/bcm2835-pcm.c
+@@ -15,6 +15,8 @@
+ #include <linux/interrupt.h>
+ #include <linux/slab.h>
+
++#include <sound/asoundef.h>
++
+ #include "bcm2835.h"
+
+ /* hardware definition */
+@@ -34,6 +36,23 @@ static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
+ .periods_max = 128,
+ };
+
++static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = {
++ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
++ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 |
++ SNDRV_PCM_RATE_48000,
++ .rate_min = 44100,
++ .rate_max = 48000,
++ .channels_min = 2,
++ .channels_max = 2,
++ .buffer_bytes_max = 128 * 1024,
++ .period_bytes_min = 1 * 1024,
++ .period_bytes_max = 128 * 1024,
++ .periods_min = 1,
++ .periods_max = 128,
++};
++
+ static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime)
+ {
+ audio_info("Freeing up alsa stream here ..\n");
+@@ -89,7 +108,8 @@ static irqreturn_t bcm2835_playback_fifo_irq(int irq, void *dev_id)
+ }
+
+ /* open callback */
+-static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
++static int snd_bcm2835_playback_open_generic(
++ struct snd_pcm_substream *substream, int spdif)
+ {
+ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+@@ -102,6 +122,11 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
+ audio_info("Alsa open (%d)\n", substream->number);
+ idx = substream->number;
+
++ if (spdif && chip->opened != 0)
++ return -EBUSY;
++ else if (!spdif && (chip->opened & (1 << idx)))
++ return -EBUSY;
++
+ if (idx > MAX_SUBSTREAMS) {
+ audio_error
+ ("substream(%d) device doesn't exist max(%d) substreams allowed\n",
+@@ -143,13 +168,20 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
+ }
+ runtime->private_data = alsa_stream;
+ runtime->private_free = snd_bcm2835_playback_free;
+- runtime->hw = snd_bcm2835_playback_hw;
++ if (spdif) {
++ runtime->hw = snd_bcm2835_playback_spdif_hw;
++ } else {
++ /* clear spdif status, as we are not in spdif mode */
++ chip->spdif_status = 0;
++ runtime->hw = snd_bcm2835_playback_hw;
++ }
+ /* minimum 16 bytes alignment (for vchiq bulk transfers) */
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ 16);
+
+ chip->alsa_stream[idx] = alsa_stream;
+
++ chip->opened |= (1 << idx);
+ alsa_stream->open = 1;
+ alsa_stream->draining = 1;
+
+@@ -159,6 +191,16 @@ out:
+ return err;
+ }
+
++static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
++{
++ return snd_bcm2835_playback_open_generic(substream, 0);
++}
++
++static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream)
++{
++ return snd_bcm2835_playback_open_generic(substream, 1);
++}
++
+ /* close callback */
+ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
+ {
+@@ -166,6 +208,7 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
++ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
+
+ audio_info(" .. IN\n");
+ audio_info("Alsa close\n");
+@@ -196,6 +239,8 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
+ * runtime->private_free callback we registered in *_open above
+ */
+
++ chip->opened &= ~(1 << substream->number);
++
+ audio_info(" .. OUT\n");
+
+ return 0;
+@@ -205,10 +250,9 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
+ static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+ {
+- int err;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+- bcm2835_alsa_stream_t *alsa_stream =
+- (bcm2835_alsa_stream_t *) runtime->private_data;
++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
++ int err;
+
+ audio_info(" .. IN\n");
+
+@@ -219,19 +263,9 @@ static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
+ return err;
+ }
+
+- err = bcm2835_audio_set_params(alsa_stream, params_channels(params),
+- params_rate(params),
+- snd_pcm_format_width(params_format
+- (params)));
+- if (err < 0) {
+- audio_error(" error setting hw params\n");
+- }
+-
+- bcm2835_audio_setup(alsa_stream);
+-
+- /* in preparation of the stream, set the controls (volume level) of the stream */
+- bcm2835_audio_set_ctls(alsa_stream->chip);
+-
++ alsa_stream->channels = params_channels(params);
++ alsa_stream->params_rate = params_rate(params);
++ alsa_stream->pcm_format_width = snd_pcm_format_width(params_format (params));
+ audio_info(" .. OUT\n");
+
+ return err;
+@@ -247,11 +281,35 @@ static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream)
+ /* prepare callback */
+ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
+ {
++ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
++ int channels;
++ int err;
+
+ audio_info(" .. IN\n");
+
++ /* notify the vchiq that it should enter spdif passthrough mode by
++ * setting channels=0 (see
++ * https://github.com/raspberrypi/linux/issues/528) */
++ if (chip->spdif_status & IEC958_AES0_NONAUDIO)
++ channels = 0;
++ else
++ channels = alsa_stream->channels;
++
++ err = bcm2835_audio_set_params(alsa_stream, channels,
++ alsa_stream->params_rate,
++ alsa_stream->pcm_format_width);
++ if (err < 0) {
++ audio_error(" error setting hw params\n");
++ }
++
++ bcm2835_audio_setup(alsa_stream);
++
++ /* in preparation of the stream, set the controls (volume level) of the stream */
++ bcm2835_audio_set_ctls(alsa_stream->chip);
++
++
+ memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect));
+
+ alsa_stream->pcm_indirect.hw_buffer_size =
+@@ -392,6 +450,18 @@ static struct snd_pcm_ops snd_bcm2835_playback_ops = {
+ .ack = snd_bcm2835_pcm_ack,
+ };
+
++static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = {
++ .open = snd_bcm2835_playback_spdif_open,
++ .close = snd_bcm2835_playback_close,
++ .ioctl = snd_bcm2835_pcm_lib_ioctl,
++ .hw_params = snd_bcm2835_pcm_hw_params,
++ .hw_free = snd_bcm2835_pcm_hw_free,
++ .prepare = snd_bcm2835_pcm_prepare,
++ .trigger = snd_bcm2835_pcm_trigger,
++ .pointer = snd_bcm2835_pcm_pointer,
++ .ack = snd_bcm2835_pcm_ack,
++};
++
+ /* create a pcm device */
+ int snd_bcm2835_new_pcm(bcm2835_chip_t * chip)
+ {
+@@ -424,3 +494,25 @@ int snd_bcm2835_new_pcm(bcm2835_chip_t * chip)
+
+ return 0;
+ }
++
++int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip)
++{
++ struct snd_pcm *pcm;
++ int err;
++
++ err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm);
++ if (err < 0)
++ return err;
++
++ pcm->private_data = chip;
++ strcpy(pcm->name, "bcm2835 IEC958/HDMI");
++ chip->pcm_spdif = pcm;
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
++ &snd_bcm2835_playback_spdif_ops);
++
++ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
++ snd_dma_continuous_data (GFP_KERNEL),
++ 64 * 1024, 64 * 1024);
++
++ return 0;
++}
+diff --git a/sound/arm/bcm2835.c b/sound/arm/bcm2835.c
+index e2047a7..4136760 100755
+--- a/sound/arm/bcm2835.c
++++ b/sound/arm/bcm2835.c
+@@ -104,7 +104,7 @@ static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
+ goto out;
+
+ snd_card_set_dev(g_card, &pdev->dev);
+- strcpy(g_card->driver, "BRCM bcm2835 ALSA Driver");
++ strcpy(g_card->driver, "bcm2835");
+ strcpy(g_card->shortname, "bcm2835 ALSA");
+ sprintf(g_card->longname, "%s", g_card->shortname);
+
+@@ -121,6 +121,12 @@ static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
+ goto out_bcm2835_new_pcm;
+ }
+
++ err = snd_bcm2835_new_spdif_pcm(chip);
++ if (err < 0) {
++ dev_err(&pdev->dev, "Failed to create new BCM2835 spdif pcm device\n");
++ goto out_bcm2835_new_spdif;
++ }
++
+ err = snd_bcm2835_new_ctl(chip);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Failed to create new BCM2835 ctl\n");
+@@ -156,6 +162,7 @@ add_register_map:
+
+ out_card_register:
+ out_bcm2835_new_ctl:
++out_bcm2835_new_spdif:
+ out_bcm2835_new_pcm:
+ out_bcm2835_create:
+ BUG_ON(!g_card);
+diff --git a/sound/arm/bcm2835.h b/sound/arm/bcm2835.h
+index 36afee3..8c2fe26 100755
+--- a/sound/arm/bcm2835.h
++++ b/sound/arm/bcm2835.h
+@@ -97,6 +97,7 @@ typedef enum {
+ typedef struct bcm2835_chip {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
++ struct snd_pcm *pcm_spdif;
+ /* Bitmat for valid reg_base and irq numbers */
+ uint32_t avail_substreams;
+ struct platform_device *pdev[MAX_SUBSTREAMS];
+@@ -106,6 +107,9 @@ typedef struct bcm2835_chip {
+ int old_volume; /* stores the volume value whist muted */
+ int dest;
+ int mute;
++
++ unsigned int opened;
++ unsigned int spdif_status;
+ } bcm2835_chip_t;
+
+ typedef struct bcm2835_alsa_stream {
+@@ -123,6 +127,10 @@ typedef struct bcm2835_alsa_stream {
+ int running;
+ int draining;
+
++ int channels;
++ int params_rate;
++ int pcm_format_width;
++
+ unsigned int pos;
+ unsigned int buffer_size;
+ unsigned int period_size;
+@@ -138,6 +146,7 @@ typedef struct bcm2835_alsa_stream {
+
+ int snd_bcm2835_new_ctl(bcm2835_chip_t * chip);
+ int snd_bcm2835_new_pcm(bcm2835_chip_t * chip);
++int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip);
+
+ int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream);
+ int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream);
+--
+1.9.1
+