diff options
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.patch | 473 |
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 + |