diff --git a/drivers/staging/vc04_services/bcm2835-audio/TODO b/drivers/staging/vc04_services/bcm2835-audio/TODO index 73d41fa631acf4..cb8ead3e91087a 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/TODO +++ b/drivers/staging/vc04_services/bcm2835-audio/TODO @@ -4,26 +4,7 @@ * * ***************************************************************************** +1) Revisit multi-cards options and PCM route mixer control (as per comment +https://lkml.org/lkml/2018/9/8/200) -1) Document the device tree node - -The downstream tree(the tree that the driver was imported from) at -http://www.github.com/raspberrypi/linux uses this node: - -audio: audio { - compatible = "brcm,bcm2835-audio"; - brcm,pwm-channels = <8>; -}; - -Since the driver requires the use of VCHIQ, it may be useful to have a link -in the device tree to the VCHIQ driver. - -2) Gracefully handle the case where VCHIQ is missing from the device tree or -it has not been initialized yet. - -3) Review error handling and remove duplicate code. - -4) Cleanup the logging mechanism. The driver should probably be using the -standard kernel logging mechanisms such as dev_info, dev_dbg, and friends. - -5) Fix the remaining checkpatch.pl errors and warnings. +2) Fix the remaining checkpatch.pl errors and warnings. diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c index ec468d5719b112..a6ec72a5f0bece 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c @@ -1,23 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright 2011 Broadcom Corporation. All rights reserved. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include -#include -#include -#include -#include #include #include @@ -27,6 +12,21 @@ #define CTRL_VOL_MAX 400 #define CTRL_VOL_MIN -10239 /* originally -10240 */ +static int bcm2835_audio_set_chip_ctls(struct bcm2835_chip *chip) +{ + int i, err = 0; + + /* change ctls for all substreams */ + for (i = 0; i < MAX_SUBSTREAMS; i++) { + if (chip->alsa_stream[i]) { + err = bcm2835_audio_set_ctls(chip->alsa_stream[i]); + if (err < 0) + break; + } + } + return err; +} + static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -49,41 +49,15 @@ static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol, return 0; } -/* toggles mute on or off depending on the value of nmute, and returns - * 1 if the mute value was changed, otherwise 0 - */ -static int toggle_mute(struct bcm2835_chip *chip, int nmute) -{ - /* if settings are ok, just return 0 */ - if (chip->mute == nmute) - return 0; - - /* if the sound is muted then we need to unmute */ - if (chip->mute == CTRL_VOL_MUTE) { - chip->volume = chip->old_volume; /* copy the old volume back */ - audio_info("Unmuting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); - } else /* otherwise we mute */ { - chip->old_volume = chip->volume; - chip->volume = 26214; /* set volume to minimum level AKA mute */ - audio_info("Muting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); - } - - chip->mute = nmute; - return 1; -} - static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); - if (mutex_lock_interruptible(&chip->audio_mutex)) - return -EINTR; - - BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK)); + mutex_lock(&chip->audio_mutex); if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) - ucontrol->value.integer.value[0] = chip2alsa(chip->volume); + ucontrol->value.integer.value[0] = chip->volume; else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) ucontrol->value.integer.value[0] = chip->mute; else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) @@ -97,79 +71,60 @@ static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + int val, *valp; int changed = 0; - if (mutex_lock_interruptible(&chip->audio_mutex)) - return -EINTR; - - if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { - audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int)ucontrol->value.integer.value[0]); - if (chip->mute == CTRL_VOL_MUTE) { - /* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */ - changed = 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */ - goto unlock; - } - if (changed || (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) { - chip->volume = alsa2chip(ucontrol->value.integer.value[0]); - changed = 1; - } - - } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { - /* Now implemented */ - audio_info(" Mute attempted\n"); - changed = toggle_mute(chip, ucontrol->value.integer.value[0]); - - } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { - if (ucontrol->value.integer.value[0] != chip->dest) { - chip->dest = ucontrol->value.integer.value[0]; - changed = 1; - } + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) + valp = &chip->volume; + else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) + valp = &chip->mute; + else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) + valp = &chip->dest; + else + return -EINVAL; + + val = ucontrol->value.integer.value[0]; + mutex_lock(&chip->audio_mutex); + if (val != *valp) { + *valp = val; + changed = 1; + if (bcm2835_audio_set_chip_ctls(chip)) + dev_err(chip->card->dev, "Failed to set ALSA controls..\n"); } - - if (changed && bcm2835_audio_set_ctls(chip)) - dev_err(chip->card->dev, "Failed to set ALSA controls..\n"); - -unlock: mutex_unlock(&chip->audio_mutex); return changed; } static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1); -static struct snd_kcontrol_new snd_bcm2835_ctl[] = { +static const struct snd_kcontrol_new snd_bcm2835_ctl[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", - .index = 0, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .private_value = PCM_PLAYBACK_VOLUME, .info = snd_bcm2835_ctl_info, .get = snd_bcm2835_ctl_get, .put = snd_bcm2835_ctl_put, - .count = 1, .tlv = {.p = snd_bcm2835_db_scale} }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Switch", - .index = 0, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .private_value = PCM_PLAYBACK_MUTE, .info = snd_bcm2835_ctl_info, .get = snd_bcm2835_ctl_get, .put = snd_bcm2835_ctl_put, - .count = 1, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Route", - .index = 0, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .private_value = PCM_PLAYBACK_DEVICE, .info = snd_bcm2835_ctl_info, .get = snd_bcm2835_ctl_get, .put = snd_bcm2835_ctl_put, - .count = 1, }, }; @@ -187,8 +142,7 @@ static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol, struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); int i; - if (mutex_lock_interruptible(&chip->audio_mutex)) - return -EINTR; + mutex_lock(&chip->audio_mutex); for (i = 0; i < 4; i++) ucontrol->value.iec958.status[i] = @@ -205,8 +159,7 @@ static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol, unsigned int val = 0; int i, change; - if (mutex_lock_interruptible(&chip->audio_mutex)) - return -EINTR; + mutex_lock(&chip->audio_mutex); for (i = 0; i < 4; i++) val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); @@ -237,51 +190,7 @@ static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol, 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; - - if (mutex_lock_interruptible(&chip->audio_mutex)) - return -EINTR; - - for (i = 0; i < 4; i++) - ucontrol->value.iec958.status[i] = - (chip->spdif_status >> (i * 8)) & 0xff; - - mutex_unlock(&chip->audio_mutex); - 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; - - if (mutex_lock_interruptible(&chip->audio_mutex)) - return -EINTR; - - 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; - - mutex_unlock(&chip->audio_mutex); - return change; -} - -static struct snd_kcontrol_new snd_bcm2835_spdif[] = { +static const struct snd_kcontrol_new snd_bcm2835_spdif[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), @@ -296,39 +205,34 @@ static struct snd_kcontrol_new snd_bcm2835_spdif[] = { .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(struct bcm2835_chip *chip) +static int create_ctls(struct bcm2835_chip *chip, size_t size, + const struct snd_kcontrol_new *kctls) { - int err; - unsigned int idx; + int i, err; - strcpy(chip->card->mixername, "Broadcom Mixer"); - for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_ctl); idx++) { - err = snd_ctl_add(chip->card, - snd_ctl_new1(&snd_bcm2835_ctl[idx], 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)); + for (i = 0; i < size; i++) { + err = snd_ctl_add(chip->card, snd_ctl_new1(&kctls[i], chip)); if (err < 0) return err; } return 0; } -static struct snd_kcontrol_new snd_bcm2835_headphones_ctl[] = { +int snd_bcm2835_new_ctl(struct bcm2835_chip *chip) +{ + int err; + + strcpy(chip->card->mixername, "Broadcom Mixer"); + err = create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl), snd_bcm2835_ctl); + if (err < 0) + return err; + return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_spdif), + snd_bcm2835_spdif); +} + +static const struct snd_kcontrol_new snd_bcm2835_headphones_ctl[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Playback Volume", @@ -357,21 +261,12 @@ static struct snd_kcontrol_new snd_bcm2835_headphones_ctl[] = { int snd_bcm2835_new_headphones_ctl(struct bcm2835_chip *chip) { - int err; - unsigned int idx; - strcpy(chip->card->mixername, "Broadcom Mixer"); - for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_headphones_ctl); idx++) { - err = snd_ctl_add(chip->card, - snd_ctl_new1(&snd_bcm2835_headphones_ctl[idx], - chip)); - if (err) - return err; - } - return 0; + return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_headphones_ctl), + snd_bcm2835_headphones_ctl); } -static struct snd_kcontrol_new snd_bcm2835_hdmi[] = { +static const struct snd_kcontrol_new snd_bcm2835_hdmi[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "HDMI Playback Volume", @@ -400,16 +295,8 @@ static struct snd_kcontrol_new snd_bcm2835_hdmi[] = { int snd_bcm2835_new_hdmi_ctl(struct bcm2835_chip *chip) { - int err; - unsigned int idx; - strcpy(chip->card->mixername, "Broadcom Mixer"); - for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_hdmi); idx++) { - err = snd_ctl_add(chip->card, - snd_ctl_new1(&snd_bcm2835_hdmi[idx], chip)); - if (err) - return err; - } - return 0; + return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_hdmi), + snd_bcm2835_hdmi); } diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c index 4532a5128560b2..bc1eaa3a077317 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c @@ -11,7 +11,8 @@ /* hardware definition */ static const struct snd_pcm_hardware snd_bcm2835_playback_hw = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH), + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_DRAIN_TRIGGER | SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 8000, @@ -27,7 +28,8 @@ static const struct snd_pcm_hardware snd_bcm2835_playback_hw = { static const 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), + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_DRAIN_TRIGGER | SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, @@ -44,50 +46,38 @@ static const struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = { static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime) { - audio_info("Freeing up alsa stream here ..\n"); kfree(runtime->private_data); - runtime->private_data = NULL; } -void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream) +void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream, + unsigned int bytes) { - unsigned int consumed = 0; - int new_period = 0; - - audio_info("alsa_stream=%p substream=%p\n", alsa_stream, - alsa_stream ? alsa_stream->substream : 0); - - if (alsa_stream->open) - consumed = bcm2835_audio_retrieve_buffers(alsa_stream); - - /* We get called only if playback was triggered, So, the number of buffers we retrieve in - * each iteration are the buffers that have been played out already - */ - - if (alsa_stream->period_size) { - if ((alsa_stream->pos / alsa_stream->period_size) != - ((alsa_stream->pos + consumed) / alsa_stream->period_size)) - new_period = 1; - } - audio_debug("updating pos cur: %d + %d max:%d period_bytes:%d, hw_ptr: %d new_period:%d\n", - alsa_stream->pos, - consumed, - alsa_stream->buffer_size, - (int) (alsa_stream->period_size * alsa_stream->substream->runtime->periods), - frames_to_bytes(alsa_stream->substream->runtime, alsa_stream->substream->runtime->status->hw_ptr), - new_period); - if (alsa_stream->buffer_size) { - alsa_stream->pos += consumed & ~(1 << 30); - alsa_stream->pos %= alsa_stream->buffer_size; + struct snd_pcm_substream *substream = alsa_stream->substream; + unsigned int pos; + + if (!alsa_stream->period_size) + return; + + if (bytes >= alsa_stream->buffer_size) { + snd_pcm_stream_lock(substream); + snd_pcm_stop(substream, + alsa_stream->draining ? + SNDRV_PCM_STATE_SETUP : + SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock(substream); + return; } - alsa_stream->interpolate_start = ktime_get_ns(); + pos = atomic_read(&alsa_stream->pos); + pos += bytes; + pos %= alsa_stream->buffer_size; + atomic_set(&alsa_stream->pos, pos); - if (alsa_stream->substream) { - if (new_period) - snd_pcm_period_elapsed(alsa_stream->substream); - } else { - audio_warning(" unexpected NULL substream\n"); + alsa_stream->period_offset += bytes; + alsa_stream->interpolate_start = ktime_get(); + if (alsa_stream->period_offset >= alsa_stream->period_size) { + alsa_stream->period_offset %= alsa_stream->period_size; + snd_pcm_period_elapsed(substream); } } @@ -101,11 +91,7 @@ static int snd_bcm2835_playback_open_generic( int idx; int err; - if (mutex_lock_interruptible(&chip->audio_mutex)) { - audio_error("Interrupted whilst waiting for lock\n"); - return -EINTR; - } - audio_info("Alsa open (%d)\n", substream->number); + mutex_lock(&chip->audio_mutex); idx = substream->number; if (spdif && chip->opened) { @@ -116,21 +102,13 @@ static int snd_bcm2835_playback_open_generic( goto out; } if (idx >= MAX_SUBSTREAMS) { - audio_error - ("substream(%d) device doesn't exist max(%d) substreams allowed\n", + dev_err(chip->dev, + "substream(%d) device doesn't exist max(%d) substreams allowed\n", idx, MAX_SUBSTREAMS); err = -ENODEV; goto out; } - /* Check if we are ready */ - if (!(chip->avail_substreams & (1 << idx))) { - /* We are not ready yet */ - audio_error("substream(%d) device is not ready yet\n", idx); - err = -EAGAIN; - goto out; - } - alsa_stream = kzalloc(sizeof(*alsa_stream), GFP_KERNEL); if (!alsa_stream) { err = -ENOMEM; @@ -142,8 +120,6 @@ static int snd_bcm2835_playback_open_generic( alsa_stream->substream = substream; alsa_stream->idx = idx; - spin_lock_init(&alsa_stream->lock); - err = bcm2835_audio_open(alsa_stream); if (err) { kfree(alsa_stream); @@ -164,11 +140,14 @@ static int snd_bcm2835_playback_open_generic( SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16); + /* position update is in 10ms order */ + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + 10 * 1000, UINT_MAX); + chip->alsa_stream[idx] = alsa_stream; chip->opened |= (1 << idx); - alsa_stream->open = 1; - alsa_stream->draining = 1; out: mutex_unlock(&chip->audio_mutex); @@ -186,47 +165,22 @@ 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) { - /* the hardware-specific codes will be here */ - - struct bcm2835_chip *chip; - struct snd_pcm_runtime *runtime; struct bcm2835_alsa_stream *alsa_stream; + struct snd_pcm_runtime *runtime; + struct bcm2835_chip *chip; chip = snd_pcm_substream_chip(substream); - if (mutex_lock_interruptible(&chip->audio_mutex)) { - audio_error("Interrupted whilst waiting for lock\n"); - return -EINTR; - } + mutex_lock(&chip->audio_mutex); runtime = substream->runtime; alsa_stream = runtime->private_data; - audio_info("Alsa close\n"); - - /* - * Call stop if it's still running. This happens when app - * is force killed and we don't get a stop trigger. - */ - if (alsa_stream->running) { - int err; - - err = bcm2835_audio_stop(alsa_stream); - alsa_stream->running = 0; - if (err) - audio_error(" Failed to STOP alsa device\n"); - } - alsa_stream->period_size = 0; alsa_stream->buffer_size = 0; - if (alsa_stream->open) { - alsa_stream->open = 0; - bcm2835_audio_close(alsa_stream); - } - if (alsa_stream->chip) - alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL; + bcm2835_audio_close(alsa_stream); + alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL; /* * Do not free up alsa_stream here, it will be freed up by * runtime->private_free callback we registered in *_open above @@ -239,35 +193,17 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream) return 0; } -/* hw_params callback */ static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; - int err; - - err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - if (err < 0) { - audio_error - (" pcm_lib_malloc failed to allocated pages for buffers\n"); - return err; - } - - 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)); - - return err; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); } -/* hw_free callback */ static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream) { return snd_pcm_lib_free_pages(substream); } -/* prepare callback */ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) { struct bcm2835_chip *chip = snd_pcm_substream_chip(substream); @@ -276,9 +212,6 @@ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) int channels; int err; - if (mutex_lock_interruptible(&chip->audio_mutex)) - return -EINTR; - /* notify the vchiq that it should enter spdif passthrough mode by * setting channels=0 (see * https://github.com/raspberrypi/linux/issues/528) @@ -286,18 +219,13 @@ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) if (chip->spdif_status & IEC958_AES0_NONAUDIO) channels = 0; else - channels = alsa_stream->channels; + channels = runtime->channels; err = bcm2835_audio_set_params(alsa_stream, channels, - alsa_stream->params_rate, - alsa_stream->pcm_format_width); + runtime->rate, + snd_pcm_format_width(runtime->format)); 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); + return err; memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect)); @@ -307,14 +235,11 @@ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); - alsa_stream->pos = 0; - alsa_stream->interpolate_start = ktime_get_ns(); + atomic_set(&alsa_stream->pos, 0); + alsa_stream->period_offset = 0; + alsa_stream->draining = false; + alsa_stream->interpolate_start = ktime_get(); - audio_debug("buffer_size=%d, period_size=%d pos=%d frame_bits=%d\n", - alsa_stream->buffer_size, alsa_stream->period_size, - alsa_stream->pos, runtime->frame_bits); - - mutex_unlock(&chip->audio_mutex); return 0; } @@ -324,12 +249,8 @@ static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; void *src = (void *) (substream->runtime->dma_area + rec->sw_data); - int err; - - err = bcm2835_audio_write(alsa_stream, bytes, src); - if (err) - audio_error(" Failed to transfer to alsa device (%d)\n", err); + bcm2835_audio_write(alsa_stream, bytes, src); } static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream) @@ -338,7 +259,6 @@ static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream) struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect; - pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max; return snd_pcm_indirect_playback_transfer(substream, pcm_indirect, snd_bcm2835_pcm_transfer); } @@ -348,50 +268,18 @@ static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; - int err = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - audio_debug("bcm2835_AUDIO_TRIGGER_START running=%d\n", - alsa_stream->running); - if (!alsa_stream->running) { - err = bcm2835_audio_start(alsa_stream); - if (!err) { - alsa_stream->pcm_indirect.hw_io = - alsa_stream->pcm_indirect.hw_data = - bytes_to_frames(runtime, - alsa_stream->pos); - substream->ops->ack(substream); - alsa_stream->running = 1; - alsa_stream->draining = 1; - } else { - audio_error(" Failed to START alsa device (%d)\n", err); - } - } - break; + return bcm2835_audio_start(alsa_stream); + case SNDRV_PCM_TRIGGER_DRAIN: + alsa_stream->draining = true; + return bcm2835_audio_drain(alsa_stream); case SNDRV_PCM_TRIGGER_STOP: - audio_debug - ("bcm2835_AUDIO_TRIGGER_STOP running=%d draining=%d\n", - alsa_stream->running, runtime->status->state == SNDRV_PCM_STATE_DRAINING); - if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { - audio_info("DRAINING\n"); - alsa_stream->draining = 1; - } else { - audio_info("DROPPING\n"); - alsa_stream->draining = 0; - } - if (alsa_stream->running) { - err = bcm2835_audio_stop(alsa_stream); - if (err != 0) - audio_error(" Failed to STOP alsa device (%d)\n", err); - alsa_stream->running = 0; - } - break; + return bcm2835_audio_stop(alsa_stream); default: - err = -EINVAL; + return -EINVAL; } - - return err; } /* pointer callback */ @@ -400,39 +288,35 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; - u64 now = ktime_get_ns(); - - audio_debug("pcm_pointer... (%d) hwptr=%d appl=%d pos=%d\n", 0, - frames_to_bytes(runtime, runtime->status->hw_ptr), - frames_to_bytes(runtime, runtime->control->appl_ptr), - alsa_stream->pos); + ktime_t now = ktime_get(); /* Give userspace better delay reporting by interpolating between GPU * notifications, assuming audio speed is close enough to the clock - * used for ktime */ - if (alsa_stream->interpolate_start && alsa_stream->interpolate_start < now) - runtime->delay = -(int)div_u64((now - alsa_stream->interpolate_start) * runtime->rate, 1000000000); + * used for ktime + */ + + if ((ktime_to_ns(alsa_stream->interpolate_start)) && + (ktime_compare(alsa_stream->interpolate_start, now) < 0)) { + u64 interval = + (ktime_to_ns(ktime_sub(now, + alsa_stream->interpolate_start))); + u64 frames_output_in_interval = + div_u64((interval * runtime->rate), 1000000000); + snd_pcm_sframes_t frames_output_in_interval_sized = + -frames_output_in_interval; + runtime->delay = frames_output_in_interval_sized; + } return snd_pcm_indirect_playback_pointer(substream, &alsa_stream->pcm_indirect, - alsa_stream->pos); -} - -static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream, - unsigned int cmd, void *arg) -{ - int ret = snd_pcm_lib_ioctl(substream, cmd, arg); - - audio_info(" .. substream=%p, cmd=%d, arg=%p (%x) ret=%d\n", substream, - cmd, arg, arg ? *(unsigned int *)arg : 0, ret); - return ret; + atomic_read(&alsa_stream->pos)); } /* operators */ static const struct snd_pcm_ops snd_bcm2835_playback_ops = { .open = snd_bcm2835_playback_open, .close = snd_bcm2835_playback_close, - .ioctl = snd_bcm2835_pcm_lib_ioctl, + .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_bcm2835_pcm_hw_params, .hw_free = snd_bcm2835_pcm_hw_free, .prepare = snd_bcm2835_pcm_prepare, @@ -444,7 +328,7 @@ static const struct snd_pcm_ops snd_bcm2835_playback_ops = { static const 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, + .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_bcm2835_pcm_hw_params, .hw_free = snd_bcm2835_pcm_hw_free, .prepare = snd_bcm2835_pcm_prepare, @@ -454,104 +338,36 @@ static const struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = { }; /* create a pcm device */ -int snd_bcm2835_new_pcm(struct bcm2835_chip *chip, u32 numchannels) -{ - struct snd_pcm *pcm; - int err; - - mutex_init(&chip->audio_mutex); - if (mutex_lock_interruptible(&chip->audio_mutex)) { - audio_error("Interrupted whilst waiting for lock\n"); - return -EINTR; - } - err = snd_pcm_new(chip->card, "bcm2835 ALSA", 0, numchannels, 0, &pcm); - if (err < 0) - goto out; - pcm->private_data = chip; - strcpy(pcm->name, "bcm2835 ALSA"); - chip->pcm = pcm; - chip->dest = AUDIO_DEST_AUTO; - chip->volume = alsa2chip(0); - chip->mute = CTRL_VOL_UNMUTE; /*disable mute on startup */ - /* set operators */ - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_bcm2835_playback_ops); - - /* pre-allocation of buffers */ - /* NOTE: this may fail */ - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - snd_bcm2835_playback_hw.buffer_bytes_max, - snd_bcm2835_playback_hw.buffer_bytes_max); - -out: - mutex_unlock(&chip->audio_mutex); - - return 0; -} - -int snd_bcm2835_new_spdif_pcm(struct bcm2835_chip *chip) +int snd_bcm2835_new_pcm(struct bcm2835_chip *chip, const char *name, + int idx, enum snd_bcm2835_route route, + u32 numchannels, bool spdif) { struct snd_pcm *pcm; int err; - if (mutex_lock_interruptible(&chip->audio_mutex)) { - audio_error("Interrupted whilst waiting for lock\n"); - return -EINTR; - } - err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm); - if (err < 0) - goto out; - - 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); - - /* pre-allocation of buffers */ - /* NOTE: this may fail */ - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - snd_bcm2835_playback_spdif_hw.buffer_bytes_max, snd_bcm2835_playback_spdif_hw.buffer_bytes_max); -out: - mutex_unlock(&chip->audio_mutex); - - return 0; -} - -int snd_bcm2835_new_simple_pcm(struct bcm2835_chip *chip, - const char *name, - enum snd_bcm2835_route route, - u32 numchannels) -{ - struct snd_pcm *pcm; - int err; - - mutex_init(&chip->audio_mutex); - - err = snd_pcm_new(chip->card, name, 0, numchannels, - 0, &pcm); + err = snd_pcm_new(chip->card, name, idx, numchannels, 0, &pcm); if (err) return err; pcm->private_data = chip; + pcm->nonatomic = true; strcpy(pcm->name, name); - chip->pcm = pcm; - chip->dest = route; - chip->volume = alsa2chip(0); - chip->mute = CTRL_VOL_UNMUTE; + if (!spdif) { + chip->dest = route; + chip->volume = 0; + chip->mute = CTRL_VOL_UNMUTE; + } snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + spdif ? &snd_bcm2835_playback_spdif_ops : &snd_bcm2835_playback_ops); - snd_pcm_lib_preallocate_pages_for_all( - pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - snd_bcm2835_playback_hw.buffer_bytes_max, - snd_bcm2835_playback_hw.buffer_bytes_max); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + chip->card->dev, 128 * 1024, 128 * 1024); + if (spdif) + chip->pcm_spdif = pcm; + else + chip->pcm = pcm; return 0; } - diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c index 868e2d6aaf1bc0..ea78e6aade7f71 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c @@ -1,188 +1,92 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright 2011 Broadcom Corporation. All rights reserved. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include #include - #include "bcm2835.h" - -/* ---- Include Files -------------------------------------------------------- */ - #include "vc_vchi_audioserv_defs.h" -/* ---- Private Constants and Types ------------------------------------------ */ - -#define BCM2835_AUDIO_STOP 0 -#define BCM2835_AUDIO_START 1 -#define BCM2835_AUDIO_WRITE 2 - -/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */ -#ifdef AUDIO_DEBUG_ENABLE -#define LOG_ERR(fmt, arg...) pr_err("%s:%d " fmt, __func__, __LINE__, ##arg) -#define LOG_WARN(fmt, arg...) pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) -#define LOG_INFO(fmt, arg...) pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) -#define LOG_DBG(fmt, arg...) pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) -#else -#define LOG_ERR(fmt, arg...) pr_err("%s:%d " fmt, __func__, __LINE__, ##arg) -#define LOG_WARN(fmt, arg...) no_printk(fmt, ##arg) -#define LOG_INFO(fmt, arg...) no_printk(fmt, ##arg) -#define LOG_DBG(fmt, arg...) no_printk(fmt, ##arg) -#endif - struct bcm2835_audio_instance { - unsigned int num_connections; - VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; + struct device *dev; + VCHI_SERVICE_HANDLE_T vchi_handle; struct completion msg_avail_comp; struct mutex vchi_mutex; struct bcm2835_alsa_stream *alsa_stream; int result; + unsigned int max_packet; short peer_version; }; static bool force_bulk; +module_param(force_bulk, bool, 0444); +MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio"); -/* ---- Private Variables ---------------------------------------------------- */ - -/* ---- Private Function Prototypes ------------------------------------------ */ - -/* ---- Private Functions ---------------------------------------------------- */ - -static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream); -static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream); -static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream, - unsigned int count, void *src); - -// Routine to send a message across a service - -static int -bcm2835_vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, - void *data, - unsigned int size) +static void bcm2835_audio_lock(struct bcm2835_audio_instance *instance) { - return vchi_queue_kernel_message(handle, - data, - size); + mutex_lock(&instance->vchi_mutex); + vchi_service_use(instance->vchi_handle); } -static const u32 BCM2835_AUDIO_WRITE_COOKIE1 = ('B' << 24 | 'C' << 16 | - 'M' << 8 | 'A'); -static const u32 BCM2835_AUDIO_WRITE_COOKIE2 = ('D' << 24 | 'A' << 16 | - 'T' << 8 | 'A'); - -struct bcm2835_audio_work { - struct work_struct my_work; - struct bcm2835_alsa_stream *alsa_stream; - int cmd; - void *src; - unsigned int count; -}; - -static void my_wq_function(struct work_struct *work) +static void bcm2835_audio_unlock(struct bcm2835_audio_instance *instance) { - struct bcm2835_audio_work *w = - container_of(work, struct bcm2835_audio_work, my_work); - int ret = -9; - - switch (w->cmd) { - case BCM2835_AUDIO_START: - ret = bcm2835_audio_start_worker(w->alsa_stream); - break; - case BCM2835_AUDIO_STOP: - ret = bcm2835_audio_stop_worker(w->alsa_stream); - break; - case BCM2835_AUDIO_WRITE: - ret = bcm2835_audio_write_worker(w->alsa_stream, w->count, - w->src); - break; - default: - LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd); - break; - } - kfree((void *)work); + vchi_service_release(instance->vchi_handle); + mutex_unlock(&instance->vchi_mutex); } -int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream) +static int bcm2835_audio_send_msg_locked(struct bcm2835_audio_instance *instance, + struct vc_audio_msg *m, bool wait) { - struct bcm2835_audio_work *work; + int status; - work = kmalloc(sizeof(*work), GFP_ATOMIC); - /*--- Queue some work (item 1) ---*/ - if (!work) { - LOG_ERR(" .. Error: NULL work kmalloc\n"); - return -ENOMEM; - } - INIT_WORK(&work->my_work, my_wq_function); - work->alsa_stream = alsa_stream; - work->cmd = BCM2835_AUDIO_START; - if (!queue_work(alsa_stream->my_wq, &work->my_work)) { - kfree(work); - return -EBUSY; + if (wait) { + instance->result = -1; + init_completion(&instance->msg_avail_comp); } - return 0; -} -int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream) -{ - struct bcm2835_audio_work *work; - - work = kmalloc(sizeof(*work), GFP_ATOMIC); - /*--- Queue some work (item 1) ---*/ - if (!work) { - LOG_ERR(" .. Error: NULL work kmalloc\n"); - return -ENOMEM; + status = vchi_queue_kernel_message(instance->vchi_handle, + m, sizeof(*m)); + if (status) { + dev_err(instance->dev, + "vchi message queue failed: %d, msg=%d\n", + status, m->type); + return -EIO; } - INIT_WORK(&work->my_work, my_wq_function); - work->alsa_stream = alsa_stream; - work->cmd = BCM2835_AUDIO_STOP; - if (!queue_work(alsa_stream->my_wq, &work->my_work)) { - kfree(work); - return -EBUSY; + + if (wait) { + if (!wait_for_completion_timeout(&instance->msg_avail_comp, + msecs_to_jiffies(10 * 1000))) { + dev_err(instance->dev, + "vchi message timeout, msg=%d\n", m->type); + return -ETIMEDOUT; + } else if (instance->result) { + dev_err(instance->dev, + "vchi message response error:%d, msg=%d\n", + instance->result, m->type); + return -EIO; + } } + return 0; } -int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream, - unsigned int count, void *src) +static int bcm2835_audio_send_msg(struct bcm2835_audio_instance *instance, + struct vc_audio_msg *m, bool wait) { - struct bcm2835_audio_work *work; + int err; - work = kmalloc(sizeof(*work), GFP_ATOMIC); - /*--- Queue some work (item 1) ---*/ - if (!work) { - LOG_ERR(" .. Error: NULL work kmalloc\n"); - return -ENOMEM; - } - INIT_WORK(&work->my_work, my_wq_function); - work->alsa_stream = alsa_stream; - work->cmd = BCM2835_AUDIO_WRITE; - work->src = src; - work->count = count; - if (!queue_work(alsa_stream->my_wq, &work->my_work)) { - kfree(work); - return -EBUSY; - } - return 0; + bcm2835_audio_lock(instance); + err = bcm2835_audio_send_msg_locked(instance, m, wait); + bcm2835_audio_unlock(instance); + return err; } -static void my_workqueue_quit(struct bcm2835_alsa_stream *alsa_stream) +static int bcm2835_audio_send_simple(struct bcm2835_audio_instance *instance, + int type, bool wait) { - flush_workqueue(alsa_stream->my_wq); - destroy_workqueue(alsa_stream->my_wq); - alsa_stream->my_wq = NULL; + struct vc_audio_msg m = { .type = type }; + + return bcm2835_audio_send_msg(instance, &m, wait); } static void audio_vchi_callback(void *param, @@ -190,179 +94,100 @@ static void audio_vchi_callback(void *param, void *msg_handle) { struct bcm2835_audio_instance *instance = param; - int status; - int msg_len; struct vc_audio_msg m; + int msg_len; + int status; if (reason != VCHI_CALLBACK_MSG_AVAILABLE) return; - if (!instance) { - LOG_ERR(" .. instance is null\n"); - BUG(); - return; - } - if (!instance->vchi_handle[0]) { - LOG_ERR(" .. instance->vchi_handle[0] is null\n"); - BUG(); - return; - } - status = vchi_msg_dequeue(instance->vchi_handle[0], + status = vchi_msg_dequeue(instance->vchi_handle, &m, sizeof(m), &msg_len, VCHI_FLAGS_NONE); if (m.type == VC_AUDIO_MSG_TYPE_RESULT) { - LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n", - instance, m.u.result.success); - instance->result = m.u.result.success; + instance->result = m.result.success; complete(&instance->msg_avail_comp); } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) { - struct bcm2835_alsa_stream *alsa_stream = instance->alsa_stream; - - LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n", - instance, m.u.complete.count); - if (m.u.complete.cookie1 != BCM2835_AUDIO_WRITE_COOKIE1 || - m.u.complete.cookie2 != BCM2835_AUDIO_WRITE_COOKIE2) - LOG_ERR(" .. response is corrupt\n"); - else if (alsa_stream) { - atomic_add(m.u.complete.count, - &alsa_stream->retrieved); - bcm2835_playback_fifo(alsa_stream); - } else { - LOG_ERR(" .. unexpected alsa_stream=%p\n", - alsa_stream); - } + if (m.complete.cookie1 != VC_AUDIO_WRITE_COOKIE1 || + m.complete.cookie2 != VC_AUDIO_WRITE_COOKIE2) + dev_err(instance->dev, "invalid cookie\n"); + else + bcm2835_playback_fifo(instance->alsa_stream, + m.complete.count); } else { - LOG_ERR(" .. unexpected m.type=%d\n", m.type); + dev_err(instance->dev, "unexpected callback type=%d\n", m.type); } } -static struct bcm2835_audio_instance * +static int vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance, - VCHI_CONNECTION_T **vchi_connections, - unsigned int num_connections) + VCHI_CONNECTION_T *vchi_connection, + struct bcm2835_audio_instance *instance) { - unsigned int i; - struct bcm2835_audio_instance *instance; + SERVICE_CREATION_T params = { + .version = VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER), + .service_id = VC_AUDIO_SERVER_NAME, + .connection = vchi_connection, + .rx_fifo_size = 0, + .tx_fifo_size = 0, + .callback = audio_vchi_callback, + .callback_param = instance, + .want_unaligned_bulk_rx = 1, //TODO: remove VCOS_FALSE + .want_unaligned_bulk_tx = 1, //TODO: remove VCOS_FALSE + .want_crc = 0 + }; int status; - int ret; - - LOG_DBG("%s: start", __func__); - - if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { - LOG_ERR("%s: unsupported number of connections %u (max=%u)\n", - __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); - - return ERR_PTR(-EINVAL); - } - /* Allocate memory for this instance */ - instance = kzalloc(sizeof(*instance), GFP_KERNEL); - if (!instance) - return ERR_PTR(-ENOMEM); - - instance->num_connections = num_connections; - /* Create a lock for exclusive, serialized VCHI connection access */ - mutex_init(&instance->vchi_mutex); /* Open the VCHI service connections */ - for (i = 0; i < num_connections; i++) { - SERVICE_CREATION_T params = { - .version = VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER), - .service_id = VC_AUDIO_SERVER_NAME, - .connection = vchi_connections[i], - .rx_fifo_size = 0, - .tx_fifo_size = 0, - .callback = audio_vchi_callback, - .callback_param = instance, - .want_unaligned_bulk_rx = 1, //TODO: remove VCOS_FALSE - .want_unaligned_bulk_tx = 1, //TODO: remove VCOS_FALSE - .want_crc = 0 - }; - - LOG_DBG("%s: about to open %i\n", __func__, i); - status = vchi_service_open(vchi_instance, ¶ms, - &instance->vchi_handle[i]); - - LOG_DBG("%s: opened %i: %p=%d\n", __func__, i, instance->vchi_handle[i], status); - if (status) { - LOG_ERR("%s: failed to open VCHI service connection (status=%d)\n", - __func__, status); - ret = -EPERM; - goto err_close_services; - } - /* Finished with the service for now */ - vchi_service_release(instance->vchi_handle[i]); - } - - LOG_DBG("%s: okay\n", __func__); - return instance; + status = vchi_service_open(vchi_instance, ¶ms, + &instance->vchi_handle); -err_close_services: - for (i = 0; i < instance->num_connections; i++) { - LOG_ERR("%s: closing %i: %p\n", __func__, i, instance->vchi_handle[i]); - if (instance->vchi_handle[i]) - vchi_service_close(instance->vchi_handle[i]); + if (status) { + dev_err(instance->dev, + "failed to open VCHI service connection (status=%d)\n", + status); + return -EPERM; } - kfree(instance); - LOG_ERR("%s: error\n", __func__); + /* Finished with the service for now */ + vchi_service_release(instance->vchi_handle); - return ERR_PTR(ret); + return 0; } -static int vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance) +static void vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance) { - unsigned int i; + int status; - if (!instance) { - LOG_ERR("%s: invalid handle %p\n", __func__, instance); - - return -1; - } - - LOG_DBG(" .. about to lock (%d)\n", instance->num_connections); - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } + mutex_lock(&instance->vchi_mutex); + vchi_service_use(instance->vchi_handle); /* Close all VCHI service connections */ - for (i = 0; i < instance->num_connections; i++) { - int status; - - LOG_DBG(" .. %i:closing %p\n", i, instance->vchi_handle[i]); - vchi_service_use(instance->vchi_handle[i]); - - status = vchi_service_close(instance->vchi_handle[i]); - if (status) { - LOG_DBG("%s: failed to close VCHI service connection (status=%d)\n", - __func__, status); - } + status = vchi_service_close(instance->vchi_handle); + if (status) { + dev_err(instance->dev, + "failed to close VCHI service connection (status=%d)\n", + status); } mutex_unlock(&instance->vchi_mutex); - - kfree(instance); - - return 0; } -int bcm2835_new_vchi_ctx(struct bcm2835_vchi_ctx *vchi_ctx) +int bcm2835_new_vchi_ctx(struct device *dev, struct bcm2835_vchi_ctx *vchi_ctx) { int ret; /* Initialize and create a VCHI connection */ ret = vchi_initialise(&vchi_ctx->vchi_instance); if (ret) { - LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)\n", - __func__, ret); - + dev_err(dev, "failed to initialise VCHI instance (ret=%d)\n", + ret); return -EIO; } ret = vchi_connect(NULL, 0, vchi_ctx->vchi_instance); if (ret) { - LOG_ERR("%s: failed to connect VCHI instance (ret=%d)\n", - __func__, ret); + dev_dbg(dev, "failed to connect VCHI instance (ret=%d)\n", + ret); kfree(vchi_ctx->vchi_instance); vchi_ctx->vchi_instance = NULL; @@ -381,473 +206,171 @@ void bcm2835_free_vchi_ctx(struct bcm2835_vchi_ctx *vchi_ctx) vchi_ctx->vchi_instance = NULL; } -static int bcm2835_audio_open_connection(struct bcm2835_alsa_stream *alsa_stream) -{ - struct bcm2835_audio_instance *instance = - (struct bcm2835_audio_instance *)alsa_stream->instance; - struct bcm2835_vchi_ctx *vhci_ctx = alsa_stream->chip->vchi_ctx; - - LOG_INFO("%s: start\n", __func__); - BUG_ON(instance); - if (instance) { - LOG_ERR("%s: VCHI instance already open (%p)\n", - __func__, instance); - instance->alsa_stream = alsa_stream; - alsa_stream->instance = instance; - return 0; - } - - /* Initialize an instance of the audio service */ - instance = vc_vchi_audio_init(vhci_ctx->vchi_instance, - &vhci_ctx->vchi_connection, 1); - - if (IS_ERR(instance)) { - LOG_ERR("%s: failed to initialize audio service\n", __func__); - - /* vchi_instance is retained for use the next time. */ - return PTR_ERR(instance); - } - - instance->alsa_stream = alsa_stream; - alsa_stream->instance = instance; - - LOG_DBG(" success !\n"); - - return 0; -} - int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream) { + struct bcm2835_vchi_ctx *vchi_ctx = alsa_stream->chip->vchi_ctx; struct bcm2835_audio_instance *instance; - struct vc_audio_msg m; - int status; - int ret; + int err; - alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1); - if (!alsa_stream->my_wq) + /* Allocate memory for this instance */ + instance = kzalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) return -ENOMEM; + mutex_init(&instance->vchi_mutex); + instance->dev = alsa_stream->chip->dev; + instance->alsa_stream = alsa_stream; + alsa_stream->instance = instance; - ret = bcm2835_audio_open_connection(alsa_stream); - if (ret) - goto free_wq; - - instance = alsa_stream->instance; - LOG_DBG(" instance (%p)\n", instance); - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", instance->num_connections); - ret = -EINTR; - goto free_wq; - } - vchi_service_use(instance->vchi_handle[0]); - - m.type = VC_AUDIO_MSG_TYPE_OPEN; - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); - - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); + err = vc_vchi_audio_init(vchi_ctx->vchi_instance, + vchi_ctx->vchi_connection, + instance); + if (err < 0) + goto free_instance; + + err = bcm2835_audio_send_simple(instance, VC_AUDIO_MSG_TYPE_OPEN, + false); + if (err < 0) + goto deinit; + + bcm2835_audio_lock(instance); + vchi_get_peer_version(instance->vchi_handle, &instance->peer_version); + bcm2835_audio_unlock(instance); + if (instance->peer_version < 2 || force_bulk) + instance->max_packet = 0; /* bulk transfer */ + else + instance->max_packet = 4000; -free_wq: - if (ret) - destroy_workqueue(alsa_stream->my_wq); + return 0; - return ret; + deinit: + vc_vchi_audio_deinit(instance); + free_instance: + alsa_stream->instance = NULL; + kfree(instance); + return err; } -static int bcm2835_audio_set_ctls_chan(struct bcm2835_alsa_stream *alsa_stream, - struct bcm2835_chip *chip) +int bcm2835_audio_set_ctls(struct bcm2835_alsa_stream *alsa_stream) { - struct vc_audio_msg m; - struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - LOG_INFO(" Setting ALSA dest(%d), volume(%d)\n", - chip->dest, chip->volume); - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - instance->result = -1; + struct bcm2835_chip *chip = alsa_stream->chip; + struct vc_audio_msg m = {}; m.type = VC_AUDIO_MSG_TYPE_CONTROL; - m.u.control.dest = chip->dest; - m.u.control.volume = chip->volume; - - /* Create the message available completion */ - init_completion(&instance->msg_avail_comp); - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); + m.control.dest = chip->dest; + if (!chip->mute) + m.control.volume = CHIP_MIN_VOLUME; + else + m.control.volume = alsa2chip(chip->volume); - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); - - ret = -1; - goto unlock; - } - - /* We are expecting a reply from the videocore */ - wait_for_completion(&instance->msg_avail_comp); - - if (instance->result) { - LOG_ERR("%s: result=%d\n", __func__, instance->result); - - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); - - return ret; -} - -int bcm2835_audio_set_ctls(struct bcm2835_chip *chip) -{ - int i; - int ret = 0; - - LOG_DBG(" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume); - - /* change ctls for all substreams */ - for (i = 0; i < MAX_SUBSTREAMS; i++) { - if (chip->avail_substreams & (1 << i)) { - if (!chip->alsa_stream[i]) { - LOG_DBG(" No ALSA stream available?! %i:%p (%x)\n", i, chip->alsa_stream[i], chip->avail_substreams); - ret = 0; - } else if (bcm2835_audio_set_ctls_chan(chip->alsa_stream[i], chip) != 0) { - LOG_ERR("Couldn't set the controls for stream %d\n", i); - ret = -1; - } else { - LOG_DBG(" Controls set for stream %d\n", i); - } - } - } - return ret; + return bcm2835_audio_send_msg(alsa_stream->instance, &m, true); } int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream, unsigned int channels, unsigned int samplerate, unsigned int bps) { - struct vc_audio_msg m; - struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - LOG_INFO(" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n", - channels, samplerate, bps); + struct vc_audio_msg m = { + .type = VC_AUDIO_MSG_TYPE_CONFIG, + .config.channels = channels, + .config.samplerate = samplerate, + .config.bps = bps, + }; + int err; /* resend ctls - alsa_stream may not have been open when first send */ - ret = bcm2835_audio_set_ctls_chan(alsa_stream, alsa_stream->chip); - if (ret) { - LOG_ERR(" Alsa controls not supported\n"); - return -EINVAL; - } - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - instance->result = -1; - - m.type = VC_AUDIO_MSG_TYPE_CONFIG; - m.u.config.channels = channels; - m.u.config.samplerate = samplerate; - m.u.config.bps = bps; - - /* Create the message available completion */ - init_completion(&instance->msg_avail_comp); - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); + err = bcm2835_audio_set_ctls(alsa_stream); + if (err) + return err; - ret = -1; - goto unlock; - } - - /* We are expecting a reply from the videocore */ - wait_for_completion(&instance->msg_avail_comp); - - if (instance->result) { - LOG_ERR("%s: result=%d", __func__, instance->result); - - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); - - return ret; + return bcm2835_audio_send_msg(alsa_stream->instance, &m, true); } -int bcm2835_audio_setup(struct bcm2835_alsa_stream *alsa_stream) +int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream) { - - return 0; + return bcm2835_audio_send_simple(alsa_stream->instance, + VC_AUDIO_MSG_TYPE_START, false); } -static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream) +int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream) { - struct vc_audio_msg m; - struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - m.type = VC_AUDIO_MSG_TYPE_START; - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); - - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); - return ret; + return bcm2835_audio_send_simple(alsa_stream->instance, + VC_AUDIO_MSG_TYPE_STOP, false); } -static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream) +int bcm2835_audio_drain(struct bcm2835_alsa_stream *alsa_stream) { - struct vc_audio_msg m; - struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - m.type = VC_AUDIO_MSG_TYPE_STOP; - m.u.stop.draining = alsa_stream->draining; + struct vc_audio_msg m = { + .type = VC_AUDIO_MSG_TYPE_STOP, + .stop.draining = 1, + }; - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); - - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); - return ret; + return bcm2835_audio_send_msg(alsa_stream->instance, &m, false); } int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream) { - struct vc_audio_msg m; struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - my_workqueue_quit(alsa_stream); + int err; - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - m.type = VC_AUDIO_MSG_TYPE_CLOSE; - - /* Create the message available completion */ - init_completion(&instance->msg_avail_comp); - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); - - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); - ret = -1; - goto unlock; - } - - /* We are expecting a reply from the videocore */ - wait_for_completion(&instance->msg_avail_comp); - - if (instance->result) { - LOG_ERR("%s: failed result (result=%d)\n", - __func__, instance->result); - - ret = -1; - goto unlock; - } - - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); + err = bcm2835_audio_send_simple(alsa_stream->instance, + VC_AUDIO_MSG_TYPE_CLOSE, true); /* Stop the audio service */ vc_vchi_audio_deinit(instance); alsa_stream->instance = NULL; + kfree(instance); - return ret; + return err; } -static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream, - unsigned int count, void *src) +int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream, + unsigned int size, void *src) { - struct vc_audio_msg m; struct bcm2835_audio_instance *instance = alsa_stream->instance; - int status; - int ret; - - LOG_INFO(" Writing %d bytes from %p\n", count, src); - - if (mutex_lock_interruptible(&instance->vchi_mutex)) { - LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", - instance->num_connections); - return -EINTR; - } - vchi_service_use(instance->vchi_handle[0]); - - if (instance->peer_version == 0 && - vchi_get_peer_version(instance->vchi_handle[0], &instance->peer_version) == 0) - LOG_DBG("%s: client version %d connected\n", __func__, instance->peer_version); - - m.type = VC_AUDIO_MSG_TYPE_WRITE; - m.u.write.count = count; - // old version uses bulk, new version uses control - m.u.write.max_packet = instance->peer_version < 2 || force_bulk ? 0 : 4000; - m.u.write.cookie1 = BCM2835_AUDIO_WRITE_COOKIE1; - m.u.write.cookie2 = BCM2835_AUDIO_WRITE_COOKIE2; - m.u.write.silence = src == NULL; - - /* Send the message to the videocore */ - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - &m, sizeof(m)); + struct vc_audio_msg m = { + .type = VC_AUDIO_MSG_TYPE_WRITE, + .write.count = size, + .write.max_packet = instance->max_packet, + .write.cookie1 = VC_AUDIO_WRITE_COOKIE1, + .write.cookie2 = VC_AUDIO_WRITE_COOKIE2, + }; + unsigned int count; + int err, status; - if (status) { - LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", - __func__, status); + if (!size) + return 0; - ret = -1; + bcm2835_audio_lock(instance); + err = bcm2835_audio_send_msg_locked(instance, &m, false); + if (err < 0) goto unlock; - } - if (!m.u.write.silence) { - if (!m.u.write.max_packet) { - /* Send the message to the videocore */ - status = vchi_bulk_queue_transmit(instance->vchi_handle[0], - src, count, - 0 * VCHI_FLAGS_BLOCK_UNTIL_QUEUED - + - 1 * VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, - NULL); - } else { - while (count > 0) { - int bytes = min_t(int, m.u.write.max_packet, count); - - status = bcm2835_vchi_msg_queue(instance->vchi_handle[0], - src, bytes); - src = (char *)src + bytes; - count -= bytes; - } - } - if (status) { - LOG_ERR("%s: failed on vchi_bulk_queue_transmit (status=%d)\n", - __func__, status); - ret = -1; - goto unlock; + count = size; + if (!instance->max_packet) { + /* Send the message to the videocore */ + status = vchi_bulk_queue_transmit(instance->vchi_handle, + src, count, + VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, + NULL); + } else { + while (count > 0) { + int bytes = min(instance->max_packet, count); + + status = vchi_queue_kernel_message(instance->vchi_handle, + src, bytes); + src += bytes; + count -= bytes; } } - ret = 0; - -unlock: - vchi_service_release(instance->vchi_handle[0]); - mutex_unlock(&instance->vchi_mutex); - return ret; -} - -/** - * Returns all buffers from arm->vc - */ -void bcm2835_audio_flush_buffers(struct bcm2835_alsa_stream *alsa_stream) -{ -} - -/** - * Forces VC to flush(drop) its filled playback buffers and - * return them the us. (VC->ARM) - */ -void bcm2835_audio_flush_playback_buffers(struct bcm2835_alsa_stream *alsa_stream) -{ -} -unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream) -{ - unsigned int count = atomic_read(&alsa_stream->retrieved); + if (status) { + dev_err(instance->dev, + "failed on %d bytes transfer (status=%d)\n", + size, status); + err = -EIO; + } - atomic_sub(count, &alsa_stream->retrieved); - return count; + unlock: + bcm2835_audio_unlock(instance); + return err; } - -module_param(force_bulk, bool, 0444); -MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio"); diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c index b8e148ade6f644..cf5f80f5ca6b03 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c @@ -6,13 +6,13 @@ #include #include #include -#include #include "bcm2835.h" static bool enable_hdmi; static bool enable_headphones; static bool enable_compat_alsa = true; +static int num_channels = MAX_SUBSTREAMS; module_param(enable_hdmi, bool, 0444); MODULE_PARM_DESC(enable_hdmi, "Enables HDMI virtual audio device"); @@ -21,38 +21,8 @@ MODULE_PARM_DESC(enable_headphones, "Enables Headphones virtual audio device"); module_param(enable_compat_alsa, bool, 0444); MODULE_PARM_DESC(enable_compat_alsa, "Enables ALSA compatibility virtual audio device"); - -static void snd_devm_unregister_child(struct device *dev, void *res) -{ - struct device *childdev = *(struct device **)res; - struct bcm2835_chip *chip = dev_get_drvdata(childdev); - struct snd_card *card = chip->card; - - snd_card_free(card); - - device_unregister(childdev); -} - -static int snd_devm_add_child(struct device *dev, struct device *child) -{ - struct device **dr; - int ret; - - dr = devres_alloc(snd_devm_unregister_child, sizeof(*dr), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - ret = device_add(child); - if (ret) { - devres_free(dr); - return ret; - } - - *dr = child; - devres_add(dev, dr); - - return 0; -} +module_param(num_channels, int, 0644); +MODULE_PARM_DESC(num_channels, "Number of audio channels (default: 8)"); static void bcm2835_devm_free_vchi_ctx(struct device *dev, void *res) { @@ -71,9 +41,7 @@ static int bcm2835_devm_add_vchi_ctx(struct device *dev) if (!vchi_ctx) return -ENOMEM; - memset(vchi_ctx, 0, sizeof(*vchi_ctx)); - - ret = bcm2835_new_vchi_ctx(vchi_ctx); + ret = bcm2835_new_vchi_ctx(dev, vchi_ctx); if (ret) { devres_free(vchi_ctx); return ret; @@ -84,101 +52,6 @@ static int bcm2835_devm_add_vchi_ctx(struct device *dev) return 0; } -static void snd_bcm2835_release(struct device *dev) -{ - struct bcm2835_chip *chip = dev_get_drvdata(dev); - - kfree(chip); -} - -static struct device * -snd_create_device(struct device *parent, - struct device_driver *driver, - const char *name) -{ - struct device *device; - int ret; - - device = devm_kzalloc(parent, sizeof(*device), GFP_KERNEL); - if (!device) - return ERR_PTR(-ENOMEM); - - device_initialize(device); - device->parent = parent; - device->driver = driver; - device->release = snd_bcm2835_release; - - dev_set_name(device, "%s", name); - - ret = snd_devm_add_child(parent, device); - if (ret) - return ERR_PTR(ret); - - return device; -} - -/* component-destructor - * (see "Management of Cards and Components") - */ -static int snd_bcm2835_dev_free(struct snd_device *device) -{ - struct bcm2835_chip *chip = device->device_data; - struct snd_card *card = chip->card; - - snd_device_free(card, chip); - - return 0; -} - -/* chip-specific constructor - * (see "Management of Cards and Components") - */ -static int snd_bcm2835_create(struct snd_card *card, - struct bcm2835_chip **rchip) -{ - struct bcm2835_chip *chip; - int err; - static struct snd_device_ops ops = { - .dev_free = snd_bcm2835_dev_free, - }; - - *rchip = NULL; - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->card = card; - - chip->vchi_ctx = devres_find(card->dev->parent, - bcm2835_devm_free_vchi_ctx, NULL, NULL); - if (!chip->vchi_ctx) { - kfree(chip); - return -ENODEV; - } - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); - if (err) { - kfree(chip); - return err; - } - - *rchip = chip; - return 0; -} - -static struct snd_card *snd_bcm2835_card_new(struct device *dev) -{ - struct snd_card *card; - int ret; - - ret = snd_card_new(dev, -1, NULL, THIS_MODULE, 0, &card); - if (ret) - return ERR_PTR(ret); - - return card; -} - typedef int (*bcm2835_audio_newpcm_func)(struct bcm2835_chip *chip, const char *name, enum snd_bcm2835_route route, @@ -203,17 +76,26 @@ static int bcm2835_audio_alsa_newpcm(struct bcm2835_chip *chip, { int err; - err = snd_bcm2835_new_pcm(chip, numchannels - 1); + err = snd_bcm2835_new_pcm(chip, "bcm2835 ALSA", 0, AUDIO_DEST_AUTO, + numchannels - 1, false); if (err) return err; - err = snd_bcm2835_new_spdif_pcm(chip); + err = snd_bcm2835_new_pcm(chip, "bcm2835 IEC958/HDMI", 1, 0, 1, true); if (err) return err; return 0; } +static int bcm2835_audio_simple_newpcm(struct bcm2835_chip *chip, + const char *name, + enum snd_bcm2835_route route, + u32 numchannels) +{ + return snd_bcm2835_new_pcm(chip, name, 0, route, numchannels, false); +} + static struct bcm2835_audio_driver bcm2835_audio_alsa = { .driver = { .name = "bcm2835_alsa", @@ -234,7 +116,7 @@ static struct bcm2835_audio_driver bcm2835_audio_hdmi = { .shortname = "bcm2835 HDMI", .longname = "bcm2835 HDMI", .minchannels = 1, - .newpcm = snd_bcm2835_new_simple_pcm, + .newpcm = bcm2835_audio_simple_newpcm, .newctl = snd_bcm2835_new_hdmi_ctl, .route = AUDIO_DEST_HDMI }; @@ -247,7 +129,7 @@ static struct bcm2835_audio_driver bcm2835_audio_headphones = { .shortname = "bcm2835 Headphones", .longname = "bcm2835 Headphones", .minchannels = 1, - .newpcm = snd_bcm2835_new_simple_pcm, + .newpcm = bcm2835_audio_simple_newpcm, .newctl = snd_bcm2835_new_headphones_ctl, .route = AUDIO_DEST_HEADPHONES }; @@ -272,81 +154,85 @@ static struct bcm2835_audio_drivers children_devices[] = { }, }; -static int snd_add_child_device(struct device *device, +static void bcm2835_card_free(void *data) +{ + snd_card_free(data); +} + +static int snd_add_child_device(struct device *dev, struct bcm2835_audio_driver *audio_driver, u32 numchans) { - struct snd_card *card; - struct device *child; struct bcm2835_chip *chip; - int err, i; - - child = snd_create_device(device, &audio_driver->driver, - audio_driver->driver.name); - if (IS_ERR(child)) { - dev_err(device, - "Unable to create child device %p, error %ld", - audio_driver->driver.name, - PTR_ERR(child)); - return PTR_ERR(child); + struct snd_card *card; + int err; + + err = snd_card_new(dev, -1, NULL, THIS_MODULE, sizeof(*chip), &card); + if (err < 0) { + dev_err(dev, "Failed to create card"); + return err; } - card = snd_bcm2835_card_new(child); - if (IS_ERR(card)) { - dev_err(child, "Failed to create card"); - return PTR_ERR(card); + chip = card->private_data; + chip->card = card; + chip->dev = dev; + mutex_init(&chip->audio_mutex); + + chip->vchi_ctx = devres_find(dev, + bcm2835_devm_free_vchi_ctx, NULL, NULL); + if (!chip->vchi_ctx) { + err = -ENODEV; + goto error; } - snd_card_set_dev(card, child); strcpy(card->driver, audio_driver->driver.name); strcpy(card->shortname, audio_driver->shortname); strcpy(card->longname, audio_driver->longname); - err = snd_bcm2835_create(card, &chip); - if (err) { - dev_err(child, "Failed to create chip, error %d\n", err); - return err; - } - - chip->dev = child; - err = audio_driver->newpcm(chip, audio_driver->shortname, audio_driver->route, numchans); if (err) { - dev_err(child, "Failed to create pcm, error %d\n", err); - return err; + dev_err(dev, "Failed to create pcm, error %d\n", err); + goto error; } err = audio_driver->newctl(chip); if (err) { - dev_err(child, "Failed to create controls, error %d\n", err); - return err; + dev_err(dev, "Failed to create controls, error %d\n", err); + goto error; } - for (i = 0; i < numchans; i++) - chip->avail_substreams |= (1 << i); - err = snd_card_register(card); if (err) { - dev_err(child, "Failed to register card, error %d\n", err); - return err; + dev_err(dev, "Failed to register card, error %d\n", err); + goto error; } - dev_set_drvdata(child, chip); - dev_info(child, "card created with %d channels\n", numchans); + dev_set_drvdata(dev, chip); + + err = devm_add_action(dev, bcm2835_card_free, card); + if (err < 0) { + dev_err(dev, "Failed to add devm action, err %d\n", err); + goto error; + } + dev_info(dev, "card created with %d channels\n", numchans); return 0; + + error: + snd_card_free(card); + return err; } static int snd_add_child_devices(struct device *device, u32 numchans) { - int i; - int count_devices = 0; - int minchannels = 0; - int extrachannels = 0; int extrachannels_per_driver = 0; int extrachannels_remainder = 0; + int count_devices = 0; + int extrachannels = 0; + int minchannels = 0; + int i; for (i = 0; i < ARRAY_SIZE(children_devices); i++) if (*children_devices[i].is_enabled) @@ -374,9 +260,9 @@ static int snd_add_child_devices(struct device *device, u32 numchans) extrachannels_remainder); for (i = 0; i < ARRAY_SIZE(children_devices); i++) { - int err; - int numchannels_this_device; struct bcm2835_audio_driver *audio_driver; + int numchannels_this_device; + int err; if (!*children_devices[i].is_enabled) continue; @@ -407,31 +293,22 @@ static int snd_add_child_devices(struct device *device, u32 numchans) return 0; } -static int snd_bcm2835_alsa_probe_dt(struct platform_device *pdev) +static int snd_bcm2835_alsa_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - u32 numchans; int err; - err = of_property_read_u32(dev->of_node, "brcm,pwm-channels", - &numchans); - if (err) { - dev_err(dev, "Failed to get DT property 'brcm,pwm-channels'"); - return err; - } - - if (numchans == 0 || numchans > MAX_SUBSTREAMS) { - numchans = MAX_SUBSTREAMS; - dev_warn(dev, - "Illegal 'brcm,pwm-channels' value, will use %u\n", - numchans); + if (num_channels <= 0 || num_channels > MAX_SUBSTREAMS) { + num_channels = MAX_SUBSTREAMS; + dev_warn(dev, "Illegal num_channels value, will use %u\n", + num_channels); } err = bcm2835_devm_add_vchi_ctx(dev); if (err) return err; - err = snd_add_child_devices(dev, numchans); + err = snd_add_child_devices(dev, num_channels); if (err) return err; @@ -453,25 +330,19 @@ static int snd_bcm2835_alsa_resume(struct platform_device *pdev) #endif -static const struct of_device_id snd_bcm2835_of_match_table[] = { - { .compatible = "brcm,bcm2835-audio",}, - {}, -}; -MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table); - -static struct platform_driver bcm2835_alsa0_driver = { - .probe = snd_bcm2835_alsa_probe_dt, +static struct platform_driver bcm2835_alsa_driver = { + .probe = snd_bcm2835_alsa_probe, #ifdef CONFIG_PM .suspend = snd_bcm2835_alsa_suspend, .resume = snd_bcm2835_alsa_resume, #endif .driver = { .name = "bcm2835_audio", - .of_match_table = snd_bcm2835_of_match_table, }, }; -module_platform_driver(bcm2835_alsa0_driver); +module_platform_driver(bcm2835_alsa_driver); MODULE_AUTHOR("Dom Cobley"); MODULE_DESCRIPTION("Alsa driver for BCM2835 chip"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bcm2835_audio"); diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h index f44b04930a8fe7..595ad584243f6e 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h @@ -5,59 +5,12 @@ #define __SOUND_ARM_BCM2835_H #include -#include -#include #include #include -#include #include -#include #include -#include - #include "interface/vchi/vchi.h" -/* - * #define AUDIO_DEBUG_ENABLE - * #define AUDIO_VERBOSE_DEBUG_ENABLE - */ - -/* Debug macros */ - -#ifdef AUDIO_DEBUG_ENABLE -#ifdef AUDIO_VERBOSE_DEBUG_ENABLE - -#define audio_debug(fmt, arg...) \ - pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) - -#define audio_info(fmt, arg...) \ - pr_info("%s:%d " fmt, __func__, __LINE__, ##arg) - -#else - -#define audio_debug(fmt, arg...) - -#define audio_info(fmt, arg...) - -#endif /* AUDIO_VERBOSE_DEBUG_ENABLE */ - -#else - -#define audio_debug(fmt, arg...) - -#define audio_info(fmt, arg...) - -#endif /* AUDIO_DEBUG_ENABLE */ - -#define audio_error(fmt, arg...) \ - pr_err("%s:%d " fmt, __func__, __LINE__, ##arg) - -#define audio_warning(fmt, arg...) \ - pr_warn("%s:%d " fmt, __func__, __LINE__, ##arg) - -#define audio_alert(fmt, arg...) \ - pr_alert("%s:%d " fmt, __func__, __LINE__, ##arg) - #define MAX_SUBSTREAMS (8) #define AVAIL_SUBSTREAMS_MASK (0xff) @@ -74,6 +27,8 @@ enum { // convert chip to alsa volume #define chip2alsa(vol) -(((vol) * 100) >> 8) +#define CHIP_MIN_VOLUME 26214 /* minimum level aka mute */ + /* Some constants for values .. */ enum snd_bcm2835_route { AUDIO_DEST_AUTO = 0, @@ -98,13 +53,10 @@ struct bcm2835_chip { struct snd_card *card; struct snd_pcm *pcm; struct snd_pcm *pcm_spdif; - /* Bitmat for valid reg_base and irq numbers */ - unsigned int avail_substreams; struct device *dev; struct bcm2835_alsa_stream *alsa_stream[MAX_SUBSTREAMS]; int volume; - int old_volume; /* stores the volume value whist muted */ int dest; int mute; @@ -120,39 +72,27 @@ struct bcm2835_alsa_stream { struct snd_pcm_substream *substream; struct snd_pcm_indirect pcm_indirect; - spinlock_t lock; - - int open; - int running; int draining; - int channels; - int params_rate; - int pcm_format_width; - - unsigned int pos; + atomic_t pos; + unsigned int period_offset; unsigned int buffer_size; unsigned int period_size; - u64 interpolate_start; + ktime_t interpolate_start; - atomic_t retrieved; struct bcm2835_audio_instance *instance; - struct workqueue_struct *my_wq; int idx; }; int snd_bcm2835_new_ctl(struct bcm2835_chip *chip); -int snd_bcm2835_new_pcm(struct bcm2835_chip *chip, u32 numchannels); -int snd_bcm2835_new_spdif_pcm(struct bcm2835_chip *chip); -int snd_bcm2835_new_simple_pcm(struct bcm2835_chip *chip, - const char *name, - enum snd_bcm2835_route route, - u32 numchannels); +int snd_bcm2835_new_pcm(struct bcm2835_chip *chip, const char *name, + int idx, enum snd_bcm2835_route route, + u32 numchannels, bool spdif); int snd_bcm2835_new_hdmi_ctl(struct bcm2835_chip *chip); int snd_bcm2835_new_headphones_ctl(struct bcm2835_chip *chip); -int bcm2835_new_vchi_ctx(struct bcm2835_vchi_ctx *vchi_ctx); +int bcm2835_new_vchi_ctx(struct device *dev, struct bcm2835_vchi_ctx *vchi_ctx); void bcm2835_free_vchi_ctx(struct bcm2835_vchi_ctx *vchi_ctx); int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream); @@ -160,16 +100,15 @@ int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream); int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream, unsigned int channels, unsigned int samplerate, unsigned int bps); -int bcm2835_audio_setup(struct bcm2835_alsa_stream *alsa_stream); int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream); int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream); -int bcm2835_audio_set_ctls(struct bcm2835_chip *chip); +int bcm2835_audio_drain(struct bcm2835_alsa_stream *alsa_stream); +int bcm2835_audio_set_ctls(struct bcm2835_alsa_stream *alsa_stream); int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream, unsigned int count, void *src); -void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream); +void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream, + unsigned int size); unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream); -void bcm2835_audio_flush_buffers(struct bcm2835_alsa_stream *alsa_stream); -void bcm2835_audio_flush_playback_buffers(struct bcm2835_alsa_stream *alsa_stream); #endif /* __SOUND_ARM_BCM2835_H */ diff --git a/drivers/staging/vc04_services/bcm2835-audio/vc_vchi_audioserv_defs.h b/drivers/staging/vc04_services/bcm2835-audio/vc_vchi_audioserv_defs.h index 1a7f0884ac9ce6..d6401e914ac9fb 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/vc_vchi_audioserv_defs.h +++ b/drivers/staging/vc04_services/bcm2835-audio/vc_vchi_audioserv_defs.h @@ -7,8 +7,10 @@ #define VC_AUDIOSERV_MIN_VER 1 #define VC_AUDIOSERV_VER 2 -/* FourCC code used for VCHI connection */ +/* FourCC codes used for VCHI communication */ #define VC_AUDIO_SERVER_NAME MAKE_FOURCC("AUDS") +#define VC_AUDIO_WRITE_COOKIE1 MAKE_FOURCC("BCMA") +#define VC_AUDIO_WRITE_COOKIE2 MAKE_FOURCC("DATA") /* * List of screens that are currently supported @@ -91,7 +93,7 @@ struct vc_audio_msg { struct vc_audio_write write; struct vc_audio_result result; struct vc_audio_complete complete; - } u; + }; }; #endif /* _VC_AUDIO_DEFS_H_ */