12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735 |
- From 40fe0b17b886f30b316614830eedf0cfc755bbbd Mon Sep 17 00:00:00 2001
- From: popcornmix <popcornmix@gmail.com>
- Date: Mon, 26 Mar 2012 22:15:50 +0100
- Subject: [PATCH 013/114] bcm2708: alsa sound driver
- Signed-off-by: popcornmix <popcornmix@gmail.com>
- alsa: add mmap support and some cleanups to bcm2835 ALSA driver
- 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.
- snd-bcm2708: Add mutex, improve logging
- Fix for ALSA driver crash
- Avoids an issue when closing and opening vchiq where a message can arrive before service handle has been written
- alsa: reduce severity of expected warning message
- snd-bcm2708: Fix dmesg spam for non-error case
- ---
- arch/arm/configs/bcmrpi_defconfig | 20 +
- arch/arm/mach-bcm2708/bcm2708.c | 54 ++
- .../interface/vchiq_arm/vchiq_kern_lib.c | 7 +-
- .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 12 +-
- sound/arm/Kconfig | 7 +
- sound/arm/Makefile | 5 +
- sound/arm/bcm2835-ctl.c | 323 ++++++++
- sound/arm/bcm2835-pcm.c | 552 +++++++++++++
- sound/arm/bcm2835-vchiq.c | 902 +++++++++++++++++++++
- sound/arm/bcm2835.c | 420 ++++++++++
- sound/arm/bcm2835.h | 167 ++++
- sound/arm/vc_vchi_audioserv_defs.h | 116 +++
- 12 files changed, 2578 insertions(+), 7 deletions(-)
- create mode 100755 sound/arm/bcm2835-ctl.c
- create mode 100755 sound/arm/bcm2835-pcm.c
- create mode 100755 sound/arm/bcm2835-vchiq.c
- create mode 100755 sound/arm/bcm2835.c
- create mode 100755 sound/arm/bcm2835.h
- create mode 100644 sound/arm/vc_vchi_audioserv_defs.h
- --- a/arch/arm/configs/bcmrpi_defconfig
- +++ b/arch/arm/configs/bcmrpi_defconfig
- @@ -202,6 +202,26 @@ CONFIG_FRAMEBUFFER_CONSOLE=y
- CONFIG_LOGO=y
- # CONFIG_LOGO_LINUX_MONO is not set
- # CONFIG_LOGO_LINUX_VGA16 is not set
- +CONFIG_SOUND=y
- +CONFIG_SND=m
- +CONFIG_SND_SEQUENCER=m
- +CONFIG_SND_SEQ_DUMMY=m
- +CONFIG_SND_MIXER_OSS=m
- +CONFIG_SND_PCM_OSS=m
- +CONFIG_SND_SEQUENCER_OSS=y
- +CONFIG_SND_HRTIMER=m
- +CONFIG_SND_DUMMY=m
- +CONFIG_SND_ALOOP=m
- +CONFIG_SND_VIRMIDI=m
- +CONFIG_SND_MTPAV=m
- +CONFIG_SND_SERIAL_U16550=m
- +CONFIG_SND_MPU401=m
- +CONFIG_SND_BCM2835=m
- +CONFIG_SND_USB_AUDIO=m
- +CONFIG_SND_USB_UA101=m
- +CONFIG_SND_USB_CAIAQ=m
- +CONFIG_SND_USB_6FIRE=m
- +CONFIG_SOUND_PRIME=m
- CONFIG_HID_A4TECH=m
- CONFIG_HID_ACRUX=m
- CONFIG_HID_APPLE=m
- --- a/arch/arm/mach-bcm2708/bcm2708.c
- +++ b/arch/arm/mach-bcm2708/bcm2708.c
- @@ -403,6 +403,58 @@ struct platform_device bcm2708_powerman_
- .coherent_dma_mask = 0xffffffffUL},
- };
-
- +
- +static struct platform_device bcm2708_alsa_devices[] = {
- + [0] = {
- + .name = "bcm2835_AUD0",
- + .id = 0, /* first audio device */
- + .resource = 0,
- + .num_resources = 0,
- + },
- + [1] = {
- + .name = "bcm2835_AUD1",
- + .id = 1, /* second audio device */
- + .resource = 0,
- + .num_resources = 0,
- + },
- + [2] = {
- + .name = "bcm2835_AUD2",
- + .id = 2, /* third audio device */
- + .resource = 0,
- + .num_resources = 0,
- + },
- + [3] = {
- + .name = "bcm2835_AUD3",
- + .id = 3, /* forth audio device */
- + .resource = 0,
- + .num_resources = 0,
- + },
- + [4] = {
- + .name = "bcm2835_AUD4",
- + .id = 4, /* fifth audio device */
- + .resource = 0,
- + .num_resources = 0,
- + },
- + [5] = {
- + .name = "bcm2835_AUD5",
- + .id = 5, /* sixth audio device */
- + .resource = 0,
- + .num_resources = 0,
- + },
- + [6] = {
- + .name = "bcm2835_AUD6",
- + .id = 6, /* seventh audio device */
- + .resource = 0,
- + .num_resources = 0,
- + },
- + [7] = {
- + .name = "bcm2835_AUD7",
- + .id = 7, /* eighth audio device */
- + .resource = 0,
- + .num_resources = 0,
- + },
- +};
- +
- int __init bcm_register_device(struct platform_device *pdev)
- {
- int ret;
- @@ -508,6 +560,8 @@ void __init bcm2708_init(void)
- bcm_register_device(&bcm2708_powerman_device);
-
- bcm2708_init_led();
- + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++)
- + bcm_register_device(&bcm2708_alsa_devices[i]);
-
- for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
- struct amba_device *d = amba_devs[i];
- --- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c
- +++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c
- @@ -288,11 +288,12 @@ VCHIQ_STATUS_T vchiq_open_service(
- NULL);
-
- if (service) {
- + *phandle = service->handle;
- status = vchiq_open_service_internal(service, current->pid);
- - if (status == VCHIQ_SUCCESS)
- - *phandle = service->handle;
- - else
- + if (status != VCHIQ_SUCCESS) {
- vchiq_remove_service(service->handle);
- + *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
- + }
- }
-
- failed:
- --- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c
- +++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c
- @@ -634,6 +634,9 @@ int32_t vchi_service_open(VCHI_INSTANCE_
- {
- VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle;
- SHIM_SERVICE_T *service = service_alloc(instance, setup);
- +
- + *handle = (VCHI_SERVICE_HANDLE_T)service;
- +
- if (service) {
- VCHIQ_SERVICE_PARAMS_T params;
- VCHIQ_STATUS_T status;
- @@ -650,11 +653,10 @@ int32_t vchi_service_open(VCHI_INSTANCE_
- if (status != VCHIQ_SUCCESS) {
- service_free(service);
- service = NULL;
- + *handle = NULL;
- }
- }
-
- - *handle = (VCHI_SERVICE_HANDLE_T)service;
- -
- return (service != NULL) ? 0 : -1;
- }
- EXPORT_SYMBOL(vchi_service_open);
- @@ -665,6 +667,9 @@ int32_t vchi_service_create(VCHI_INSTANC
- {
- VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle;
- SHIM_SERVICE_T *service = service_alloc(instance, setup);
- +
- + *handle = (VCHI_SERVICE_HANDLE_T)service;
- +
- if (service) {
- VCHIQ_SERVICE_PARAMS_T params;
- VCHIQ_STATUS_T status;
- @@ -680,11 +685,10 @@ int32_t vchi_service_create(VCHI_INSTANC
- if (status != VCHIQ_SUCCESS) {
- service_free(service);
- service = NULL;
- + *handle = NULL;
- }
- }
-
- - *handle = (VCHI_SERVICE_HANDLE_T)service;
- -
- return (service != NULL) ? 0 : -1;
- }
- EXPORT_SYMBOL(vchi_service_create);
- --- a/sound/arm/Kconfig
- +++ b/sound/arm/Kconfig
- @@ -40,5 +40,12 @@ config SND_PXA2XX_AC97
- Say Y or M if you want to support any AC97 codec attached to
- the PXA2xx AC97 interface.
-
- +config SND_BCM2835
- + tristate "BCM2835 ALSA driver"
- + depends on ARCH_BCM2708 && BCM2708_VCHIQ && SND
- + select SND_PCM
- + help
- + Say Y or M if you want to support BCM2835 Alsa pcm card driver
- +
- endif # SND_ARM
-
- --- a/sound/arm/Makefile
- +++ b/sound/arm/Makefile
- @@ -14,3 +14,8 @@ snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_A
-
- obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
- snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
- +
- +obj-$(CONFIG_SND_BCM2835) += snd-bcm2835.o
- +snd-bcm2835-objs := bcm2835.o bcm2835-ctl.o bcm2835-pcm.o bcm2835-vchiq.o
- +
- +ccflags-y += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000
- --- /dev/null
- +++ b/sound/arm/bcm2835-ctl.c
- @@ -0,0 +1,323 @@
- +/*****************************************************************************
- +* Copyright 2011 Broadcom Corporation. All rights reserved.
- +*
- +* Unless you and Broadcom execute a separate written software license
- +* agreement governing use of this software, this software is licensed to you
- +* under the terms of the GNU General Public License version 2, available at
- +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
- +*
- +* Notwithstanding the above, under no circumstances may you combine this
- +* software in any way with any other Broadcom software provided under a
- +* license other than the GPL, without Broadcom's express prior written
- +* consent.
- +*****************************************************************************/
- +
- +#include <linux/platform_device.h>
- +#include <linux/init.h>
- +#include <linux/io.h>
- +#include <linux/jiffies.h>
- +#include <linux/slab.h>
- +#include <linux/time.h>
- +#include <linux/wait.h>
- +#include <linux/delay.h>
- +#include <linux/moduleparam.h>
- +#include <linux/sched.h>
- +
- +#include <sound/core.h>
- +#include <sound/control.h>
- +#include <sound/pcm.h>
- +#include <sound/pcm_params.h>
- +#include <sound/rawmidi.h>
- +#include <sound/initval.h>
- +#include <sound/tlv.h>
- +#include <sound/asoundef.h>
- +
- +#include "bcm2835.h"
- +
- +/* volume maximum and minimum in terms of 0.01dB */
- +#define CTRL_VOL_MAX 400
- +#define CTRL_VOL_MIN -10239 /* originally -10240 */
- +
- +
- +static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol,
- + struct snd_ctl_elem_info *uinfo)
- +{
- + audio_info(" ... IN\n");
- + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
- + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- + uinfo->count = 1;
- + uinfo->value.integer.min = CTRL_VOL_MIN;
- + uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */
- + } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) {
- + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- + uinfo->count = 1;
- + uinfo->value.integer.min = 0;
- + uinfo->value.integer.max = 1;
- + } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) {
- + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- + uinfo->count = 1;
- + uinfo->value.integer.min = 0;
- + uinfo->value.integer.max = AUDIO_DEST_MAX-1;
- + }
- + audio_info(" ... OUT\n");
- + 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);
- +
- + BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK));
- +
- + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
- + ucontrol->value.integer.value[0] = chip2alsa(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)
- + ucontrol->value.integer.value[0] = chip->dest;
- +
- + return 0;
- +}
- +
- +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 changed = 0;
- +
- + 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); */
- + return 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */
- + }
- + 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 (changed) {
- + if (bcm2835_audio_set_ctls(chip))
- + printk(KERN_ERR "Failed to set ALSA controls..\n");
- + }
- +
- + return changed;
- +}
- +
- +static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1);
- +
- +static 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,
- + },
- +};
- +
- +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;
- + unsigned int idx;
- +
- + 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));
- + if (err < 0)
- + return err;
- + }
- + return 0;
- +}
- --- /dev/null
- +++ b/sound/arm/bcm2835-pcm.c
- @@ -0,0 +1,552 @@
- +/*****************************************************************************
- +* Copyright 2011 Broadcom Corporation. All rights reserved.
- +*
- +* Unless you and Broadcom execute a separate written software license
- +* agreement governing use of this software, this software is licensed to you
- +* under the terms of the GNU General Public License version 2, available at
- +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
- +*
- +* Notwithstanding the above, under no circumstances may you combine this
- +* software in any way with any other Broadcom software provided under a
- +* license other than the GPL, without Broadcom's express prior written
- +* consent.
- +*****************************************************************************/
- +
- +#include <linux/interrupt.h>
- +#include <linux/slab.h>
- +
- +#include <sound/asoundef.h>
- +
- +#include "bcm2835.h"
- +
- +/* hardware definition */
- +static 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),
- + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
- + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
- + .rate_min = 8000,
- + .rate_max = 48000,
- + .channels_min = 1,
- + .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 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");
- + if (runtime->private_data)
- + kfree(runtime->private_data);
- + runtime->private_data = NULL;
- +}
- +
- +static irqreturn_t bcm2835_playback_fifo_irq(int irq, void *dev_id)
- +{
- + bcm2835_alsa_stream_t *alsa_stream = (bcm2835_alsa_stream_t *) dev_id;
- + uint32_t consumed = 0;
- + int new_period = 0;
- +
- + audio_info(" .. IN\n");
- +
- + 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;
- + }
- +
- + if (alsa_stream->substream) {
- + if (new_period)
- + snd_pcm_period_elapsed(alsa_stream->substream);
- + } else {
- + audio_warning(" unexpected NULL substream\n");
- + }
- + audio_info(" .. OUT\n");
- +
- + return IRQ_HANDLED;
- +}
- +
- +/* open callback */
- +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;
- + bcm2835_alsa_stream_t *alsa_stream;
- + int idx;
- + int err;
- +
- + audio_info(" .. IN (%d)\n", substream->number);
- +
- + 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);
- + 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",
- + 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(bcm2835_alsa_stream_t), GFP_KERNEL);
- + if (alsa_stream == NULL) {
- + err = -ENOMEM;
- + goto out;
- + }
- +
- + /* Initialise alsa_stream */
- + alsa_stream->chip = chip;
- + alsa_stream->substream = substream;
- + alsa_stream->idx = idx;
- +
- + sema_init(&alsa_stream->buffers_update_sem, 0);
- + sema_init(&alsa_stream->control_sem, 0);
- + spin_lock_init(&alsa_stream->lock);
- +
- + /* Enabled in start trigger, called on each "fifo irq" after that */
- + alsa_stream->enable_fifo_irq = 0;
- + alsa_stream->fifo_irq_handler = bcm2835_playback_fifo_irq;
- +
- + err = bcm2835_audio_open(alsa_stream);
- + if (err != 0) {
- + kfree(alsa_stream);
- + return err;
- + }
- + runtime->private_data = alsa_stream;
- + runtime->private_free = snd_bcm2835_playback_free;
- + 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;
- +
- +out:
- + mutex_unlock(&chip->audio_mutex);
- +
- + audio_info(" .. OUT =%d\n", err);
- +
- + 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)
- +{
- + /* the hardware-specific codes will be here */
- +
- + bcm2835_chip_t *chip;
- + struct snd_pcm_runtime *runtime;
- + bcm2835_alsa_stream_t *alsa_stream;
- +
- + audio_info(" .. IN\n");
- +
- + chip = snd_pcm_substream_chip(substream);
- + if(mutex_lock_interruptible(&chip->audio_mutex))
- + {
- + audio_error("Interrupted whilst waiting for lock\n");
- + return -EINTR;
- + }
- + 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 != 0)
- + 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;
- + /*
- + * Do not free up alsa_stream here, it will be freed up by
- + * runtime->private_free callback we registered in *_open above
- + */
- +
- + chip->opened &= ~(1 << substream->number);
- +
- + mutex_unlock(&chip->audio_mutex);
- + audio_info(" .. OUT\n");
- +
- + 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;
- + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
- + int err;
- +
- + audio_info(" .. IN\n");
- +
- + 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));
- + audio_info(" .. OUT\n");
- +
- + return err;
- +}
- +
- +/* hw_free callback */
- +static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream)
- +{
- + audio_info(" .. IN\n");
- + return snd_pcm_lib_free_pages(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 =
- + alsa_stream->pcm_indirect.sw_buffer_size =
- + snd_pcm_lib_buffer_bytes(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;
- +
- + 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);
- +
- + audio_info(" .. OUT\n");
- + return 0;
- +}
- +
- +static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream,
- + struct snd_pcm_indirect *rec, size_t bytes)
- +{
- + struct snd_pcm_runtime *runtime = substream->runtime;
- + bcm2835_alsa_stream_t *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);
- +
- +}
- +
- +static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream)
- +{
- + struct snd_pcm_runtime *runtime = substream->runtime;
- + bcm2835_alsa_stream_t *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;
- + snd_pcm_indirect_playback_transfer(substream, pcm_indirect,
- + snd_bcm2835_pcm_transfer);
- + return 0;
- +}
- +
- +/* trigger callback */
- +static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
- +{
- + struct snd_pcm_runtime *runtime = substream->runtime;
- + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
- + int err = 0;
- +
- + audio_info(" .. IN\n");
- +
- + 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 == 0) {
- + 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;
- + 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;
- + default:
- + err = -EINVAL;
- + }
- +
- + audio_info(" .. OUT\n");
- + return err;
- +}
- +
- +/* pointer callback */
- +static snd_pcm_uframes_t
- +snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream)
- +{
- + struct snd_pcm_runtime *runtime = substream->runtime;
- + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
- +
- + audio_info(" .. IN\n");
- +
- + 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);
- +
- + audio_info(" .. OUT\n");
- + 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 *)arg : 0, ret);
- + return ret;
- +}
- +
- +/* operators */
- +static struct snd_pcm_ops snd_bcm2835_playback_ops = {
- + .open = snd_bcm2835_playback_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,
- +};
- +
- +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)
- +{
- + struct snd_pcm *pcm;
- + int err;
- +
- + audio_info(" .. IN\n");
- + 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, MAX_SUBSTREAMS, 0, &pcm);
- + if (err < 0)
- + return err;
- + 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), 64 * 1024,
- + 64 * 1024);
- +
- + mutex_unlock(&chip->audio_mutex);
- + audio_info(" .. OUT\n");
- +
- + return 0;
- +}
- +
- +int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip)
- +{
- + struct snd_pcm *pcm;
- + int err;
- +
- + audio_info(" .. IN\n");
- + 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)
- + 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);
- + mutex_unlock(&chip->audio_mutex);
- + audio_info(" .. OUT\n");
- +
- + return 0;
- +}
- --- /dev/null
- +++ b/sound/arm/bcm2835-vchiq.c
- @@ -0,0 +1,902 @@
- +/*****************************************************************************
- +* Copyright 2011 Broadcom Corporation. All rights reserved.
- +*
- +* Unless you and Broadcom execute a separate written software license
- +* agreement governing use of this software, this software is licensed to you
- +* under the terms of the GNU General Public License version 2, available at
- +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
- +*
- +* Notwithstanding the above, under no circumstances may you combine this
- +* software in any way with any other Broadcom software provided under a
- +* license other than the GPL, without Broadcom's express prior written
- +* consent.
- +*****************************************************************************/
- +
- +#include <linux/device.h>
- +#include <sound/core.h>
- +#include <sound/initval.h>
- +#include <sound/pcm.h>
- +#include <linux/io.h>
- +#include <linux/interrupt.h>
- +#include <linux/fs.h>
- +#include <linux/file.h>
- +#include <linux/mm.h>
- +#include <linux/syscalls.h>
- +#include <asm/uaccess.h>
- +#include <linux/slab.h>
- +#include <linux/delay.h>
- +#include <linux/atomic.h>
- +#include <linux/module.h>
- +#include <linux/completion.h>
- +
- +#include "bcm2835.h"
- +
- +/* ---- Include Files -------------------------------------------------------- */
- +
- +#include "interface/vchi/vchi.h"
- +#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... )
- + #define LOG_INFO( fmt, arg... )
- + #define LOG_DBG( fmt, arg... )
- +#endif
- +
- +typedef struct opaque_AUDIO_INSTANCE_T {
- + uint32_t num_connections;
- + VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS];
- + struct completion msg_avail_comp;
- + struct mutex vchi_mutex;
- + bcm2835_alsa_stream_t *alsa_stream;
- + int32_t result;
- + short peer_version;
- +} AUDIO_INSTANCE_T;
- +
- +bool force_bulk = false;
- +
- +/* ---- Private Variables ---------------------------------------------------- */
- +
- +/* ---- Private Function Prototypes ------------------------------------------ */
- +
- +/* ---- Private Functions ---------------------------------------------------- */
- +
- +static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream);
- +static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream);
- +static int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream,
- + uint32_t count, void *src);
- +
- +typedef struct {
- + struct work_struct my_work;
- + bcm2835_alsa_stream_t *alsa_stream;
- + int cmd;
- + void *src;
- + uint32_t count;
- +} my_work_t;
- +
- +static void my_wq_function(struct work_struct *work)
- +{
- + my_work_t *w = (my_work_t *) work;
- + int ret = -9;
- + LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->cmd);
- + 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);
- + LOG_DBG(" .. OUT %d\n", ret);
- +}
- +
- +int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + int ret = -1;
- + LOG_DBG(" .. IN\n");
- + if (alsa_stream->my_wq) {
- + my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC);
- + /*--- Queue some work (item 1) ---*/
- + if (work) {
- + INIT_WORK((struct work_struct *)work, my_wq_function);
- + work->alsa_stream = alsa_stream;
- + work->cmd = BCM2835_AUDIO_START;
- + if (queue_work
- + (alsa_stream->my_wq, (struct work_struct *)work))
- + ret = 0;
- + } else
- + LOG_ERR(" .. Error: NULL work kmalloc\n");
- + }
- + LOG_DBG(" .. OUT %d\n", ret);
- + return ret;
- +}
- +
- +int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + int ret = -1;
- + LOG_DBG(" .. IN\n");
- + if (alsa_stream->my_wq) {
- + my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC);
- + /*--- Queue some work (item 1) ---*/
- + if (work) {
- + INIT_WORK((struct work_struct *)work, my_wq_function);
- + work->alsa_stream = alsa_stream;
- + work->cmd = BCM2835_AUDIO_STOP;
- + if (queue_work
- + (alsa_stream->my_wq, (struct work_struct *)work))
- + ret = 0;
- + } else
- + LOG_ERR(" .. Error: NULL work kmalloc\n");
- + }
- + LOG_DBG(" .. OUT %d\n", ret);
- + return ret;
- +}
- +
- +int bcm2835_audio_write(bcm2835_alsa_stream_t *alsa_stream,
- + uint32_t count, void *src)
- +{
- + int ret = -1;
- + LOG_DBG(" .. IN\n");
- + if (alsa_stream->my_wq) {
- + my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC);
- + /*--- Queue some work (item 1) ---*/
- + if (work) {
- + INIT_WORK((struct work_struct *)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, (struct work_struct *)work))
- + ret = 0;
- + } else
- + LOG_ERR(" .. Error: NULL work kmalloc\n");
- + }
- + LOG_DBG(" .. OUT %d\n", ret);
- + return ret;
- +}
- +
- +void my_workqueue_init(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1);
- + return;
- +}
- +
- +void my_workqueue_quit(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + if (alsa_stream->my_wq) {
- + flush_workqueue(alsa_stream->my_wq);
- + destroy_workqueue(alsa_stream->my_wq);
- + alsa_stream->my_wq = NULL;
- + }
- + return;
- +}
- +
- +static void audio_vchi_callback(void *param,
- + const VCHI_CALLBACK_REASON_T reason,
- + void *msg_handle)
- +{
- + AUDIO_INSTANCE_T *instance = (AUDIO_INSTANCE_T *) param;
- + int32_t status;
- + int32_t msg_len;
- + VC_AUDIO_MSG_T m;
- + LOG_DBG(" .. IN instance=%p, handle=%p, alsa=%p, reason=%d, handle=%p\n",
- + instance, instance ? instance->vchi_handle[0] : NULL, instance ? instance->alsa_stream : NULL, reason, msg_handle);
- +
- + 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],
- + &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;
- + complete(&instance->msg_avail_comp);
- + } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
- + bcm2835_alsa_stream_t *alsa_stream = instance->alsa_stream;
- + irq_handler_t callback = (irq_handler_t) m.u.complete.callback;
- + LOG_DBG
- + (" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n",
- + instance, m.u.complete.count);
- + if (alsa_stream && callback) {
- + atomic_add(m.u.complete.count, &alsa_stream->retrieved);
- + callback(0, alsa_stream);
- + } else {
- + LOG_ERR(" .. unexpected alsa_stream=%p, callback=%p\n",
- + alsa_stream, callback);
- + }
- + } else {
- + LOG_ERR(" .. unexpected m.type=%d\n", m.type);
- + }
- + LOG_DBG(" .. OUT\n");
- +}
- +
- +static AUDIO_INSTANCE_T *vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance,
- + VCHI_CONNECTION_T **
- + vchi_connections,
- + uint32_t num_connections)
- +{
- + uint32_t i;
- + AUDIO_INSTANCE_T *instance;
- + int status;
- +
- + 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 NULL;
- + }
- + /* Allocate memory for this instance */
- + instance = kmalloc(sizeof(*instance), GFP_KERNEL);
- + if (!instance)
- + return NULL;
- +
- + memset(instance, 0, sizeof(*instance));
- + 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 = {
- + VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER),
- + VC_AUDIO_SERVER_NAME, // 4cc service code
- + vchi_connections[i], // passed in fn pointers
- + 0, // rx fifo size (unused)
- + 0, // tx fifo size (unused)
- + audio_vchi_callback, // service callback
- + instance, // service callback parameter
- + 1, //TODO: remove VCOS_FALSE, // unaligned bulk recieves
- + 1, //TODO: remove VCOS_FALSE, // unaligned bulk transmits
- + 0 // want crc check on bulk transfers
- + };
- +
- + 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);
- +
- + 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;
- +
- +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]);
- + }
- +
- + kfree(instance);
- + LOG_ERR("%s: error\n", __func__);
- +
- + return NULL;
- +}
- +
- +static int32_t vc_vchi_audio_deinit(AUDIO_INSTANCE_T * instance)
- +{
- + uint32_t i;
- +
- + LOG_DBG(" .. IN\n");
- +
- + if (instance == NULL) {
- + 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;
- + }
- +
- + /* Close all VCHI service connections */
- + for (i = 0; i < instance->num_connections; i++) {
- + int32_t success;
- + LOG_DBG(" .. %i:closing %p\n", i, instance->vchi_handle[i]);
- + vchi_service_use(instance->vchi_handle[i]);
- +
- + success = vchi_service_close(instance->vchi_handle[i]);
- + if (success != 0) {
- + LOG_ERR
- + ("%s: failed to close VCHI service connection (status=%d)\n",
- + __func__, success);
- + }
- + }
- +
- + mutex_unlock(&instance->vchi_mutex);
- +
- + kfree(instance);
- +
- + LOG_DBG(" .. OUT\n");
- +
- + return 0;
- +}
- +
- +static int bcm2835_audio_open_connection(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + static VCHI_INSTANCE_T vchi_instance;
- + static VCHI_CONNECTION_T *vchi_connection;
- + static int initted;
- + AUDIO_INSTANCE_T *instance = alsa_stream->instance;
- + int ret;
- + LOG_DBG(" .. IN\n");
- +
- + 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;
- + ret = 0; // xxx todo -1;
- + goto err_free_mem;
- + }
- +
- + /* Initialize and create a VCHI connection */
- + if (!initted) {
- + ret = vchi_initialise(&vchi_instance);
- + if (ret != 0) {
- + LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)\n",
- + __func__, ret);
- +
- + ret = -EIO;
- + goto err_free_mem;
- + }
- + ret = vchi_connect(NULL, 0, vchi_instance);
- + if (ret != 0) {
- + LOG_ERR("%s: failed to connect VCHI instance (ret=%d)\n",
- + __func__, ret);
- +
- + ret = -EIO;
- + goto err_free_mem;
- + }
- + initted = 1;
- + }
- +
- + /* Initialize an instance of the audio service */
- + instance = vc_vchi_audio_init(vchi_instance, &vchi_connection, 1);
- +
- + if (instance == NULL) {
- + LOG_ERR("%s: failed to initialize audio service\n", __func__);
- +
- + ret = -EPERM;
- + goto err_free_mem;
- + }
- +
- + instance->alsa_stream = alsa_stream;
- + alsa_stream->instance = instance;
- +
- + LOG_DBG(" success !\n");
- +err_free_mem:
- + LOG_DBG(" .. OUT\n");
- +
- + return ret;
- +}
- +
- +int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + AUDIO_INSTANCE_T *instance;
- + VC_AUDIO_MSG_T m;
- + int32_t success;
- + int ret;
- + LOG_DBG(" .. IN\n");
- +
- + my_workqueue_init(alsa_stream);
- +
- + ret = bcm2835_audio_open_connection(alsa_stream);
- + if (ret != 0) {
- + ret = -1;
- + goto exit;
- + }
- + 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);
- + return -EINTR;
- + }
- + vchi_service_use(instance->vchi_handle[0]);
- +
- + m.type = VC_AUDIO_MSG_TYPE_OPEN;
- +
- + /* Send the message to the videocore */
- + success = vchi_msg_queue(instance->vchi_handle[0],
- + &m, sizeof m,
- + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
- +
- + if (success != 0) {
- + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- + __func__, success);
- +
- + ret = -1;
- + goto unlock;
- + }
- +
- + ret = 0;
- +
- +unlock:
- + vchi_service_release(instance->vchi_handle[0]);
- + mutex_unlock(&instance->vchi_mutex);
- +exit:
- + LOG_DBG(" .. OUT\n");
- + return ret;
- +}
- +
- +static int bcm2835_audio_set_ctls_chan(bcm2835_alsa_stream_t * alsa_stream,
- + bcm2835_chip_t * chip)
- +{
- + VC_AUDIO_MSG_T m;
- + AUDIO_INSTANCE_T *instance = alsa_stream->instance;
- + int32_t success;
- + int ret;
- + LOG_DBG(" .. IN\n");
- +
- + 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;
- +
- + 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 */
- + success = vchi_msg_queue(instance->vchi_handle[0],
- + &m, sizeof m,
- + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
- +
- + if (success != 0) {
- + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- + __func__, success);
- +
- + ret = -1;
- + goto unlock;
- + }
- +
- + /* We are expecting a reply from the videocore */
- + ret = wait_for_completion_interruptible(&instance->msg_avail_comp);
- + if (ret) {
- + LOG_ERR("%s: failed on waiting for event (status=%d)\n",
- + __func__, success);
- + goto unlock;
- + }
- +
- + if (instance->result != 0) {
- + 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);
- +
- + LOG_DBG(" .. OUT\n");
- + return ret;
- +}
- +
- +int bcm2835_audio_set_ctls(bcm2835_chip_t * chip)
- +{
- + int i;
- + int ret = 0;
- + LOG_DBG(" .. IN\n");
- + 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 /* returns 0 on success */
- + (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);
- + }
- + }
- + LOG_DBG(" .. OUT ret=%d\n", ret);
- + return ret;
- +}
- +
- +int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream,
- + uint32_t channels, uint32_t samplerate,
- + uint32_t bps)
- +{
- + VC_AUDIO_MSG_T m;
- + AUDIO_INSTANCE_T *instance = alsa_stream->instance;
- + int32_t success;
- + int ret;
- + LOG_DBG(" .. IN\n");
- +
- + LOG_INFO
- + (" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n",
- + channels, samplerate, bps);
- +
- + /* 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 != 0) {
- + 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 */
- + success = vchi_msg_queue(instance->vchi_handle[0],
- + &m, sizeof m,
- + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
- +
- + if (success != 0) {
- + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- + __func__, success);
- +
- + ret = -1;
- + goto unlock;
- + }
- +
- + /* We are expecting a reply from the videocore */
- + ret = wait_for_completion_interruptible(&instance->msg_avail_comp);
- + if (ret) {
- + LOG_ERR("%s: failed on waiting for event (status=%d)\n",
- + __func__, success);
- + goto unlock;
- + }
- +
- + if (instance->result != 0) {
- + 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);
- +
- + LOG_DBG(" .. OUT\n");
- + return ret;
- +}
- +
- +int bcm2835_audio_setup(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + LOG_DBG(" .. IN\n");
- +
- + LOG_DBG(" .. OUT\n");
- +
- + return 0;
- +}
- +
- +static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + VC_AUDIO_MSG_T m;
- + AUDIO_INSTANCE_T *instance = alsa_stream->instance;
- + int32_t success;
- + int ret;
- + LOG_DBG(" .. IN\n");
- +
- + 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 */
- + success = vchi_msg_queue(instance->vchi_handle[0],
- + &m, sizeof m,
- + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
- +
- + if (success != 0) {
- + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- + __func__, success);
- +
- + ret = -1;
- + goto unlock;
- + }
- +
- + ret = 0;
- +
- +unlock:
- + vchi_service_release(instance->vchi_handle[0]);
- + mutex_unlock(&instance->vchi_mutex);
- + LOG_DBG(" .. OUT\n");
- + return ret;
- +}
- +
- +static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + VC_AUDIO_MSG_T m;
- + AUDIO_INSTANCE_T *instance = alsa_stream->instance;
- + int32_t success;
- + int ret;
- + LOG_DBG(" .. IN\n");
- +
- + 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;
- +
- + /* Send the message to the videocore */
- + success = vchi_msg_queue(instance->vchi_handle[0],
- + &m, sizeof m,
- + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
- +
- + if (success != 0) {
- + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- + __func__, success);
- +
- + ret = -1;
- + goto unlock;
- + }
- +
- + ret = 0;
- +
- +unlock:
- + vchi_service_release(instance->vchi_handle[0]);
- + mutex_unlock(&instance->vchi_mutex);
- + LOG_DBG(" .. OUT\n");
- + return ret;
- +}
- +
- +int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + VC_AUDIO_MSG_T m;
- + AUDIO_INSTANCE_T *instance = alsa_stream->instance;
- + int32_t success;
- + int ret;
- + LOG_DBG(" .. IN\n");
- +
- + my_workqueue_quit(alsa_stream);
- +
- + 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 */
- + success = vchi_msg_queue(instance->vchi_handle[0],
- + &m, sizeof m,
- + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
- +
- + if (success != 0) {
- + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- + __func__, success);
- + ret = -1;
- + goto unlock;
- + }
- +
- + ret = wait_for_completion_interruptible(&instance->msg_avail_comp);
- + if (ret) {
- + LOG_ERR("%s: failed on waiting for event (status=%d)\n",
- + __func__, success);
- + goto unlock;
- + }
- + if (instance->result != 0) {
- + LOG_ERR("%s: failed result (status=%d)\n",
- + __func__, instance->result);
- +
- + ret = -1;
- + goto unlock;
- + }
- +
- + ret = 0;
- +
- +unlock:
- + vchi_service_release(instance->vchi_handle[0]);
- + mutex_unlock(&instance->vchi_mutex);
- +
- + /* Stop the audio service */
- + if (instance) {
- + vc_vchi_audio_deinit(instance);
- + alsa_stream->instance = NULL;
- + }
- + LOG_DBG(" .. OUT\n");
- + return ret;
- +}
- +
- +int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream,
- + uint32_t count, void *src)
- +{
- + VC_AUDIO_MSG_T m;
- + AUDIO_INSTANCE_T *instance = alsa_stream->instance;
- + int32_t success;
- + int ret;
- +
- + LOG_DBG(" .. IN\n");
- +
- + 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.callback = alsa_stream->fifo_irq_handler;
- + m.u.write.cookie = alsa_stream;
- + m.u.write.silence = src == NULL;
- +
- + /* Send the message to the videocore */
- + success = vchi_msg_queue(instance->vchi_handle[0],
- + &m, sizeof m,
- + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
- +
- + if (success != 0) {
- + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
- + __func__, success);
- +
- + ret = -1;
- + goto unlock;
- + }
- + if (!m.u.write.silence) {
- + if (m.u.write.max_packet == 0) {
- + /* Send the message to the videocore */
- + success = 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((int)m.u.write.max_packet, (int)count);
- + success = vchi_msg_queue(instance->vchi_handle[0],
- + src, bytes,
- + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
- + src = (char *)src + bytes;
- + count -= bytes;
- + }
- + }
- + if (success != 0) {
- + LOG_ERR
- + ("%s: failed on vchi_bulk_queue_transmit (status=%d)\n",
- + __func__, success);
- +
- + ret = -1;
- + goto unlock;
- + }
- + }
- + ret = 0;
- +
- +unlock:
- + vchi_service_release(instance->vchi_handle[0]);
- + mutex_unlock(&instance->vchi_mutex);
- + LOG_DBG(" .. OUT\n");
- + return ret;
- +}
- +
- +/**
- + * Returns all buffers from arm->vc
- + */
- +void bcm2835_audio_flush_buffers(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + LOG_DBG(" .. IN\n");
- + LOG_DBG(" .. OUT\n");
- + return;
- +}
- +
- +/**
- + * Forces VC to flush(drop) its filled playback buffers and
- + * return them the us. (VC->ARM)
- + */
- +void bcm2835_audio_flush_playback_buffers(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + LOG_DBG(" .. IN\n");
- + LOG_DBG(" .. OUT\n");
- +}
- +
- +uint32_t bcm2835_audio_retrieve_buffers(bcm2835_alsa_stream_t * alsa_stream)
- +{
- + uint32_t count = atomic_read(&alsa_stream->retrieved);
- + atomic_sub(count, &alsa_stream->retrieved);
- + return count;
- +}
- +
- +module_param(force_bulk, bool, 0444);
- +MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio");
- --- /dev/null
- +++ b/sound/arm/bcm2835.c
- @@ -0,0 +1,420 @@
- +/*****************************************************************************
- +* Copyright 2011 Broadcom Corporation. All rights reserved.
- +*
- +* Unless you and Broadcom execute a separate written software license
- +* agreement governing use of this software, this software is licensed to you
- +* under the terms of the GNU General Public License version 2, available at
- +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
- +*
- +* Notwithstanding the above, under no circumstances may you combine this
- +* software in any way with any other Broadcom software provided under a
- +* license other than the GPL, without Broadcom's express prior written
- +* consent.
- +*****************************************************************************/
- +
- +#include <linux/platform_device.h>
- +
- +#include <linux/init.h>
- +#include <linux/slab.h>
- +#include <linux/module.h>
- +
- +#include "bcm2835.h"
- +
- +/* module parameters (see "Module Parameters") */
- +/* SNDRV_CARDS: maximum number of cards supported by this module */
- +static int index[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = -1 };
- +static char *id[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = NULL };
- +static int enable[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = 1 };
- +
- +/* HACKY global pointers needed for successive probes to work : ssp
- + * But compared against the changes we will have to do in VC audio_ipc code
- + * to export 8 audio_ipc devices as a single IPC device and then monitor all
- + * four devices in a thread, this gets things done quickly and should be easier
- + * to debug if we run into issues
- + */
- +
- +static struct snd_card *g_card = NULL;
- +static bcm2835_chip_t *g_chip = NULL;
- +
- +static int snd_bcm2835_free(bcm2835_chip_t * chip)
- +{
- + kfree(chip);
- + return 0;
- +}
- +
- +/* component-destructor
- + * (see "Management of Cards and Components")
- + */
- +static int snd_bcm2835_dev_free(struct snd_device *device)
- +{
- + return snd_bcm2835_free(device->device_data);
- +}
- +
- +/* chip-specific constructor
- + * (see "Management of Cards and Components")
- + */
- +static int snd_bcm2835_create(struct snd_card *card,
- + struct platform_device *pdev,
- + bcm2835_chip_t ** rchip)
- +{
- + bcm2835_chip_t *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 == NULL)
- + return -ENOMEM;
- +
- + chip->card = card;
- +
- + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- + if (err < 0) {
- + snd_bcm2835_free(chip);
- + return err;
- + }
- +
- + *rchip = chip;
- + return 0;
- +}
- +
- +static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
- +{
- + static int dev;
- + bcm2835_chip_t *chip;
- + struct snd_card *card;
- + int err;
- +
- + if (dev >= MAX_SUBSTREAMS)
- + return -ENODEV;
- +
- + if (!enable[dev]) {
- + dev++;
- + return -ENOENT;
- + }
- +
- + if (dev > 0)
- + goto add_register_map;
- +
- + err = snd_card_new(NULL, index[dev], id[dev], THIS_MODULE, 0, &g_card);
- + if (err < 0)
- + goto out;
- +
- + snd_card_set_dev(g_card, &pdev->dev);
- + strcpy(g_card->driver, "bcm2835");
- + strcpy(g_card->shortname, "bcm2835 ALSA");
- + sprintf(g_card->longname, "%s", g_card->shortname);
- +
- + err = snd_bcm2835_create(g_card, pdev, &chip);
- + if (err < 0) {
- + dev_err(&pdev->dev, "Failed to create bcm2835 chip\n");
- + goto out_bcm2835_create;
- + }
- +
- + g_chip = chip;
- + err = snd_bcm2835_new_pcm(chip);
- + if (err < 0) {
- + dev_err(&pdev->dev, "Failed to create new BCM2835 pcm device\n");
- + 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");
- + goto out_bcm2835_new_ctl;
- + }
- +
- +add_register_map:
- + card = g_card;
- + chip = g_chip;
- +
- + BUG_ON(!(card && chip));
- +
- + chip->avail_substreams |= (1 << dev);
- + chip->pdev[dev] = pdev;
- +
- + if (dev == 0) {
- + err = snd_card_register(card);
- + if (err < 0) {
- + dev_err(&pdev->dev,
- + "Failed to register bcm2835 ALSA card \n");
- + goto out_card_register;
- + }
- + platform_set_drvdata(pdev, card);
- + audio_info("bcm2835 ALSA card created!\n");
- + } else {
- + audio_info("bcm2835 ALSA chip created!\n");
- + platform_set_drvdata(pdev, (void *)dev);
- + }
- +
- + dev++;
- +
- + return 0;
- +
- +out_card_register:
- +out_bcm2835_new_ctl:
- +out_bcm2835_new_spdif:
- +out_bcm2835_new_pcm:
- +out_bcm2835_create:
- + BUG_ON(!g_card);
- + if (snd_card_free(g_card))
- + dev_err(&pdev->dev, "Failed to free Registered alsa card\n");
- + g_card = NULL;
- +out:
- + dev = SNDRV_CARDS; /* stop more avail_substreams from being probed */
- + dev_err(&pdev->dev, "BCM2835 ALSA Probe failed !!\n");
- + return err;
- +}
- +
- +static int snd_bcm2835_alsa_remove(struct platform_device *pdev)
- +{
- + uint32_t idx;
- + void *drv_data;
- +
- + drv_data = platform_get_drvdata(pdev);
- +
- + if (drv_data == (void *)g_card) {
- + /* This is the card device */
- + snd_card_free((struct snd_card *)drv_data);
- + g_card = NULL;
- + g_chip = NULL;
- + } else {
- + idx = (uint32_t) drv_data;
- + if (g_card != NULL) {
- + BUG_ON(!g_chip);
- + /* We pass chip device numbers in audio ipc devices
- + * other than the one we registered our card with
- + */
- + idx = (uint32_t) drv_data;
- + BUG_ON(!idx || idx > MAX_SUBSTREAMS);
- + g_chip->avail_substreams &= ~(1 << idx);
- + /* There should be atleast one substream registered
- + * after we are done here, as it wil be removed when
- + * the *remove* is called for the card device
- + */
- + BUG_ON(!g_chip->avail_substreams);
- + }
- + }
- +
- + platform_set_drvdata(pdev, NULL);
- +
- + return 0;
- +}
- +
- +#ifdef CONFIG_PM
- +static int snd_bcm2835_alsa_suspend(struct platform_device *pdev,
- + pm_message_t state)
- +{
- + return 0;
- +}
- +
- +static int snd_bcm2835_alsa_resume(struct platform_device *pdev)
- +{
- + return 0;
- +}
- +
- +#endif
- +
- +static struct platform_driver bcm2835_alsa0_driver = {
- + .probe = snd_bcm2835_alsa_probe,
- + .remove = snd_bcm2835_alsa_remove,
- +#ifdef CONFIG_PM
- + .suspend = snd_bcm2835_alsa_suspend,
- + .resume = snd_bcm2835_alsa_resume,
- +#endif
- + .driver = {
- + .name = "bcm2835_AUD0",
- + .owner = THIS_MODULE,
- + },
- +};
- +
- +static struct platform_driver bcm2835_alsa1_driver = {
- + .probe = snd_bcm2835_alsa_probe,
- + .remove = snd_bcm2835_alsa_remove,
- +#ifdef CONFIG_PM
- + .suspend = snd_bcm2835_alsa_suspend,
- + .resume = snd_bcm2835_alsa_resume,
- +#endif
- + .driver = {
- + .name = "bcm2835_AUD1",
- + .owner = THIS_MODULE,
- + },
- +};
- +
- +static struct platform_driver bcm2835_alsa2_driver = {
- + .probe = snd_bcm2835_alsa_probe,
- + .remove = snd_bcm2835_alsa_remove,
- +#ifdef CONFIG_PM
- + .suspend = snd_bcm2835_alsa_suspend,
- + .resume = snd_bcm2835_alsa_resume,
- +#endif
- + .driver = {
- + .name = "bcm2835_AUD2",
- + .owner = THIS_MODULE,
- + },
- +};
- +
- +static struct platform_driver bcm2835_alsa3_driver = {
- + .probe = snd_bcm2835_alsa_probe,
- + .remove = snd_bcm2835_alsa_remove,
- +#ifdef CONFIG_PM
- + .suspend = snd_bcm2835_alsa_suspend,
- + .resume = snd_bcm2835_alsa_resume,
- +#endif
- + .driver = {
- + .name = "bcm2835_AUD3",
- + .owner = THIS_MODULE,
- + },
- +};
- +
- +static struct platform_driver bcm2835_alsa4_driver = {
- + .probe = snd_bcm2835_alsa_probe,
- + .remove = snd_bcm2835_alsa_remove,
- +#ifdef CONFIG_PM
- + .suspend = snd_bcm2835_alsa_suspend,
- + .resume = snd_bcm2835_alsa_resume,
- +#endif
- + .driver = {
- + .name = "bcm2835_AUD4",
- + .owner = THIS_MODULE,
- + },
- +};
- +
- +static struct platform_driver bcm2835_alsa5_driver = {
- + .probe = snd_bcm2835_alsa_probe,
- + .remove = snd_bcm2835_alsa_remove,
- +#ifdef CONFIG_PM
- + .suspend = snd_bcm2835_alsa_suspend,
- + .resume = snd_bcm2835_alsa_resume,
- +#endif
- + .driver = {
- + .name = "bcm2835_AUD5",
- + .owner = THIS_MODULE,
- + },
- +};
- +
- +static struct platform_driver bcm2835_alsa6_driver = {
- + .probe = snd_bcm2835_alsa_probe,
- + .remove = snd_bcm2835_alsa_remove,
- +#ifdef CONFIG_PM
- + .suspend = snd_bcm2835_alsa_suspend,
- + .resume = snd_bcm2835_alsa_resume,
- +#endif
- + .driver = {
- + .name = "bcm2835_AUD6",
- + .owner = THIS_MODULE,
- + },
- +};
- +
- +static struct platform_driver bcm2835_alsa7_driver = {
- + .probe = snd_bcm2835_alsa_probe,
- + .remove = snd_bcm2835_alsa_remove,
- +#ifdef CONFIG_PM
- + .suspend = snd_bcm2835_alsa_suspend,
- + .resume = snd_bcm2835_alsa_resume,
- +#endif
- + .driver = {
- + .name = "bcm2835_AUD7",
- + .owner = THIS_MODULE,
- + },
- +};
- +
- +static int bcm2835_alsa_device_init(void)
- +{
- + int err;
- + err = platform_driver_register(&bcm2835_alsa0_driver);
- + if (err) {
- + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
- + goto out;
- + }
- +
- + err = platform_driver_register(&bcm2835_alsa1_driver);
- + if (err) {
- + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
- + goto unregister_0;
- + }
- +
- + err = platform_driver_register(&bcm2835_alsa2_driver);
- + if (err) {
- + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
- + goto unregister_1;
- + }
- +
- + err = platform_driver_register(&bcm2835_alsa3_driver);
- + if (err) {
- + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
- + goto unregister_2;
- + }
- +
- + err = platform_driver_register(&bcm2835_alsa4_driver);
- + if (err) {
- + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
- + goto unregister_3;
- + }
- +
- + err = platform_driver_register(&bcm2835_alsa5_driver);
- + if (err) {
- + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
- + goto unregister_4;
- + }
- +
- + err = platform_driver_register(&bcm2835_alsa6_driver);
- + if (err) {
- + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
- + goto unregister_5;
- + }
- +
- + err = platform_driver_register(&bcm2835_alsa7_driver);
- + if (err) {
- + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
- + goto unregister_6;
- + }
- +
- + return 0;
- +
- +unregister_6:
- + platform_driver_unregister(&bcm2835_alsa6_driver);
- +unregister_5:
- + platform_driver_unregister(&bcm2835_alsa5_driver);
- +unregister_4:
- + platform_driver_unregister(&bcm2835_alsa4_driver);
- +unregister_3:
- + platform_driver_unregister(&bcm2835_alsa3_driver);
- +unregister_2:
- + platform_driver_unregister(&bcm2835_alsa2_driver);
- +unregister_1:
- + platform_driver_unregister(&bcm2835_alsa1_driver);
- +unregister_0:
- + platform_driver_unregister(&bcm2835_alsa0_driver);
- +out:
- + return err;
- +}
- +
- +static void bcm2835_alsa_device_exit(void)
- +{
- + platform_driver_unregister(&bcm2835_alsa0_driver);
- + platform_driver_unregister(&bcm2835_alsa1_driver);
- + platform_driver_unregister(&bcm2835_alsa2_driver);
- + platform_driver_unregister(&bcm2835_alsa3_driver);
- + platform_driver_unregister(&bcm2835_alsa4_driver);
- + platform_driver_unregister(&bcm2835_alsa5_driver);
- + platform_driver_unregister(&bcm2835_alsa6_driver);
- + platform_driver_unregister(&bcm2835_alsa7_driver);
- +}
- +
- +late_initcall(bcm2835_alsa_device_init);
- +module_exit(bcm2835_alsa_device_exit);
- +
- +MODULE_AUTHOR("Dom Cobley");
- +MODULE_DESCRIPTION("Alsa driver for BCM2835 chip");
- +MODULE_LICENSE("GPL");
- +MODULE_ALIAS("platform:bcm2835_alsa");
- --- /dev/null
- +++ b/sound/arm/bcm2835.h
- @@ -0,0 +1,167 @@
- +/*****************************************************************************
- +* Copyright 2011 Broadcom Corporation. All rights reserved.
- +*
- +* Unless you and Broadcom execute a separate written software license
- +* agreement governing use of this software, this software is licensed to you
- +* under the terms of the GNU General Public License version 2, available at
- +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
- +*
- +* Notwithstanding the above, under no circumstances may you combine this
- +* software in any way with any other Broadcom software provided under a
- +* license other than the GPL, without Broadcom's express prior written
- +* consent.
- +*****************************************************************************/
- +
- +#ifndef __SOUND_ARM_BCM2835_H
- +#define __SOUND_ARM_BCM2835_H
- +
- +#include <linux/device.h>
- +#include <linux/list.h>
- +#include <linux/interrupt.h>
- +#include <linux/wait.h>
- +#include <sound/core.h>
- +#include <sound/initval.h>
- +#include <sound/pcm.h>
- +#include <sound/pcm_params.h>
- +#include <sound/pcm-indirect.h>
- +#include <linux/workqueue.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...) \
- + printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg)
- +
- +#define audio_info(fmt, arg...) \
- + printk(KERN_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...) \
- + printk(KERN_ERR"%s:%d " fmt, __func__, __LINE__, ##arg)
- +
- +#define audio_warning(fmt, arg...) \
- + printk(KERN_WARNING"%s:%d " fmt, __func__, __LINE__, ##arg)
- +
- +#define audio_alert(fmt, arg...) \
- + printk(KERN_ALERT"%s:%d " fmt, __func__, __LINE__, ##arg)
- +
- +#define MAX_SUBSTREAMS (8)
- +#define AVAIL_SUBSTREAMS_MASK (0xff)
- +enum {
- + CTRL_VOL_MUTE,
- + CTRL_VOL_UNMUTE
- +};
- +
- +/* macros for alsa2chip and chip2alsa, instead of functions */
- +
- +#define alsa2chip(vol) (uint)(-((vol << 8) / 100)) /* convert alsa to chip volume (defined as macro rather than function call) */
- +#define chip2alsa(vol) -((vol * 100) >> 8) /* convert chip to alsa volume */
- +
- +/* Some constants for values .. */
- +typedef enum {
- + AUDIO_DEST_AUTO = 0,
- + AUDIO_DEST_HEADPHONES = 1,
- + AUDIO_DEST_HDMI = 2,
- + AUDIO_DEST_MAX,
- +} SND_BCM2835_ROUTE_T;
- +
- +typedef enum {
- + PCM_PLAYBACK_VOLUME,
- + PCM_PLAYBACK_MUTE,
- + PCM_PLAYBACK_DEVICE,
- +} SND_BCM2835_CTRL_T;
- +
- +/* definition of the chip-specific record */
- +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];
- + struct bcm2835_alsa_stream *alsa_stream[MAX_SUBSTREAMS];
- +
- + int volume;
- + int old_volume; /* stores the volume value whist muted */
- + int dest;
- + int mute;
- +
- + unsigned int opened;
- + unsigned int spdif_status;
- + struct mutex audio_mutex;
- +} bcm2835_chip_t;
- +
- +typedef struct bcm2835_alsa_stream {
- + bcm2835_chip_t *chip;
- + struct snd_pcm_substream *substream;
- + struct snd_pcm_indirect pcm_indirect;
- +
- + struct semaphore buffers_update_sem;
- + struct semaphore control_sem;
- + spinlock_t lock;
- + volatile uint32_t control;
- + volatile uint32_t status;
- +
- + int open;
- + int running;
- + int draining;
- +
- + int channels;
- + int params_rate;
- + int pcm_format_width;
- +
- + unsigned int pos;
- + unsigned int buffer_size;
- + unsigned int period_size;
- +
- + uint32_t enable_fifo_irq;
- + irq_handler_t fifo_irq_handler;
- +
- + atomic_t retrieved;
- + struct opaque_AUDIO_INSTANCE_T *instance;
- + struct workqueue_struct *my_wq;
- + int idx;
- +} bcm2835_alsa_stream_t;
- +
- +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);
- +int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream,
- + uint32_t channels, uint32_t samplerate,
- + uint32_t bps);
- +int bcm2835_audio_setup(bcm2835_alsa_stream_t * alsa_stream);
- +int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream);
- +int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream);
- +int bcm2835_audio_set_ctls(bcm2835_chip_t * chip);
- +int bcm2835_audio_write(bcm2835_alsa_stream_t * alsa_stream, uint32_t count,
- + void *src);
- +uint32_t bcm2835_audio_retrieve_buffers(bcm2835_alsa_stream_t * alsa_stream);
- +void bcm2835_audio_flush_buffers(bcm2835_alsa_stream_t * alsa_stream);
- +void bcm2835_audio_flush_playback_buffers(bcm2835_alsa_stream_t * alsa_stream);
- +
- +#endif /* __SOUND_ARM_BCM2835_H */
- --- /dev/null
- +++ b/sound/arm/vc_vchi_audioserv_defs.h
- @@ -0,0 +1,116 @@
- +/*****************************************************************************
- +* Copyright 2011 Broadcom Corporation. All rights reserved.
- +*
- +* Unless you and Broadcom execute a separate written software license
- +* agreement governing use of this software, this software is licensed to you
- +* under the terms of the GNU General Public License version 2, available at
- +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
- +*
- +* Notwithstanding the above, under no circumstances may you combine this
- +* software in any way with any other Broadcom software provided under a
- +* license other than the GPL, without Broadcom's express prior written
- +* consent.
- +*****************************************************************************/
- +
- +#ifndef _VC_AUDIO_DEFS_H_
- +#define _VC_AUDIO_DEFS_H_
- +
- +#define VC_AUDIOSERV_MIN_VER 1
- +#define VC_AUDIOSERV_VER 2
- +
- +// FourCC code used for VCHI connection
- +#define VC_AUDIO_SERVER_NAME MAKE_FOURCC("AUDS")
- +
- +// Maximum message length
- +#define VC_AUDIO_MAX_MSG_LEN (sizeof( VC_AUDIO_MSG_T ))
- +
- +// List of screens that are currently supported
- +// All message types supported for HOST->VC direction
- +typedef enum {
- + VC_AUDIO_MSG_TYPE_RESULT, // Generic result
- + VC_AUDIO_MSG_TYPE_COMPLETE, // Generic result
- + VC_AUDIO_MSG_TYPE_CONFIG, // Configure audio
- + VC_AUDIO_MSG_TYPE_CONTROL, // Configure audio
- + VC_AUDIO_MSG_TYPE_OPEN, // Configure audio
- + VC_AUDIO_MSG_TYPE_CLOSE, // Configure audio
- + VC_AUDIO_MSG_TYPE_START, // Configure audio
- + VC_AUDIO_MSG_TYPE_STOP, // Configure audio
- + VC_AUDIO_MSG_TYPE_WRITE, // Configure audio
- + VC_AUDIO_MSG_TYPE_MAX
- +} VC_AUDIO_MSG_TYPE;
- +
- +// configure the audio
- +typedef struct {
- + uint32_t channels;
- + uint32_t samplerate;
- + uint32_t bps;
- +
- +} VC_AUDIO_CONFIG_T;
- +
- +typedef struct {
- + uint32_t volume;
- + uint32_t dest;
- +
- +} VC_AUDIO_CONTROL_T;
- +
- +// audio
- +typedef struct {
- + uint32_t dummy;
- +
- +} VC_AUDIO_OPEN_T;
- +
- +// audio
- +typedef struct {
- + uint32_t dummy;
- +
- +} VC_AUDIO_CLOSE_T;
- +// audio
- +typedef struct {
- + uint32_t dummy;
- +
- +} VC_AUDIO_START_T;
- +// audio
- +typedef struct {
- + uint32_t draining;
- +
- +} VC_AUDIO_STOP_T;
- +
- +// configure the write audio samples
- +typedef struct {
- + uint32_t count; // in bytes
- + void *callback;
- + void *cookie;
- + uint16_t silence;
- + uint16_t max_packet;
- +} VC_AUDIO_WRITE_T;
- +
- +// Generic result for a request (VC->HOST)
- +typedef struct {
- + int32_t success; // Success value
- +
- +} VC_AUDIO_RESULT_T;
- +
- +// Generic result for a request (VC->HOST)
- +typedef struct {
- + int32_t count; // Success value
- + void *callback;
- + void *cookie;
- +} VC_AUDIO_COMPLETE_T;
- +
- +// Message header for all messages in HOST->VC direction
- +typedef struct {
- + int32_t type; // Message type (VC_AUDIO_MSG_TYPE)
- + union {
- + VC_AUDIO_CONFIG_T config;
- + VC_AUDIO_CONTROL_T control;
- + VC_AUDIO_OPEN_T open;
- + VC_AUDIO_CLOSE_T close;
- + VC_AUDIO_START_T start;
- + VC_AUDIO_STOP_T stop;
- + VC_AUDIO_WRITE_T write;
- + VC_AUDIO_RESULT_T result;
- + VC_AUDIO_COMPLETE_T complete;
- + } u;
- +} VC_AUDIO_MSG_T;
- +
- +#endif // _VC_AUDIO_DEFS_H_
|