From cfefe1fa1301a79454ba9f97c7520e051b2b30cb Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Wed, 27 Mar 2024 16:49:08 -0500 Subject: [PATCH 01/22] adding/revising WM8994 driver and Audio wrapper driver for it --- boards/stm32_common/stm32f746disco/audio.adb | 234 +++++++++++-------- boards/stm32_common/stm32f746disco/audio.ads | 33 ++- components/src/audio/W8994/wm8994.adb | 134 ++++++----- components/src/audio/W8994/wm8994.ads | 69 ++++-- 4 files changed, 298 insertions(+), 172 deletions(-) diff --git a/boards/stm32_common/stm32f746disco/audio.adb b/boards/stm32_common/stm32f746disco/audio.adb index fa481d98b..23f69cfe7 100644 --- a/boards/stm32_common/stm32f746disco/audio.adb +++ b/boards/stm32_common/stm32f746disco/audio.adb @@ -32,7 +32,6 @@ -- @author MCD Application Team -- ------------------------------------------------------------------------------ -with HAL; use HAL; with STM32; use STM32; with STM32.Device; use STM32.Device; with STM32.Board; use STM32.Board; @@ -50,25 +49,102 @@ package body Audio is SAI2_SD_B : GPIO_Point renames PG10; SAI2_FS_A : GPIO_Point renames PI7; SAI_Pins : constant GPIO_Points := - (SAI2_MCLK_A, SAI2_SCK_A, SAI2_SD_A, SAI2_SD_B, - SAI2_FS_A); + (SAI2_MCLK_A, SAI2_SCK_A, SAI2_SD_A, SAI2_SD_B, SAI2_FS_A); SAI_Pins_AF : GPIO_Alternate_Function renames GPIO_AF_SAI2_10; - -- SAI in/out conf SAI_Out_Block : SAI_Block renames Block_A; --- SAI_In_Block : SAI_Block renames Block_B; procedure Set_Audio_Clock (Freq : Audio_Frequency); + procedure Initialize_Audio_Out_Pins; - procedure Initialize_SAI_Out (Freq : Audio_Frequency); + + procedure Initialize_Audio_DMA; + -- Configure the DMA channel to the SAI peripheral + + procedure Initialize_SAI_Out (Freq : Audio_Frequency; Sink : Audio_Output_Device); + procedure Initialize_Audio_I2C; + -- Initialize the I2C Port to send commands to the driver + + function As_Device_Volume (Input : Audio_Volume) return WM8994.Volume_Level; + -- Converts the percentage input value to the device-specific range + + ---------------- + -- Initialize -- + ---------------- + + procedure Initialize + (This : in out WM8994_Audio_Device; + Volume : Audio_Volume; + Frequency : Audio_Frequency; + Sink : Audio_Output_Device) + is + begin + STM32.SAI.Deinitialize (Audio_SAI, SAI_Out_Block); + + Set_Audio_Clock (Frequency); + + -- Initialize the SAI + Initialize_Audio_Out_Pins; + Initialize_Audio_DMA; + Initialize_SAI_Out (Frequency, Sink); + + Initialize_Audio_I2C; + + if This.Device.Chip_ID /= WM8994.WM8994_ID then + raise Program_Error with "Invalid Chip ID received from the Audio Codec"; + end if; + + This.Device.Reset; + This.Device.Initialize + (Input => WM8994.No_Input, + Output => WM8994.Output_Device (Sink), + Volume => As_Device_Volume (Volume), + Frequency => WM8994.Audio_Frequency (Frequency)); + + This.Sink := Sink; + -- For sake of any individual calls to Set_Frequency, assuming the STM + -- code is correct that we also need to set the clocks when setting the + -- frequency + end Initialize; + + ---------------- + -- Set_Volume -- + ---------------- + + procedure Set_Volume + (This : in out WM8994_Audio_Device; + Volume : Audio_Volume) + is + begin + This.Device.Set_Volume (As_Device_Volume (Volume)); + end Set_Volume; + + ------------------- + -- Set_Frequency -- + ------------------- + + procedure Set_Frequency + (This : in out WM8994_Audio_Device; + Frequency : Audio_Frequency) + is + begin + -- The following is per the example source file from STM, + -- STM32746G-Disco_example\board_drivers\stm32746g_discovery_audio.c, + -- whereas the WM8894 driver just has a specific function that writes + -- the necessary configuration bits to the device register without any + -- clock configuration too. + Set_Audio_Clock (Frequency); + STM32.SAI.Disable (Audio_SAI, SAI_Out_Block); + Initialize_SAI_Out (Frequency, This.Sink); + STM32.SAI.Enable (Audio_SAI, SAI_Out_Block); + end Set_Frequency; --------------------- -- Set_Audio_Clock -- --------------------- - procedure Set_Audio_Clock (Freq : Audio_Frequency) - is + procedure Set_Audio_Clock (Freq : Audio_Frequency) is begin -- Two groups of frequencies: the 44kHz family and the 48kHz family -- The Actual audio frequency is calculated then with the following @@ -92,31 +168,21 @@ package body Audio is (Audio_SAI, PLLI2SN => 344, -- VCO Output = 344MHz PLLI2SQ => 7, -- SAI Clk(First level) = 49.142 MHz - PLLI2SDIVQ => 1); -- I2S Clk = 49.142 MHz + PLLI2SDIVQ => 1); -- I2S Clk = 49.142 MHz + + when Audio_Freq_12khz | Audio_Freq_24khz | Audio_Freq_88khz => + raise Program_Error with "freq not yet implemented"; + -- FIXME! end case; end Set_Audio_Clock; - ------------------------------- - -- Initialize_Audio_Out_Pins -- - ------------------------------- + -------------------------- + -- Initialize_Audio_DMA -- + -------------------------- - procedure Initialize_Audio_Out_Pins - is + procedure Initialize_Audio_DMA is begin - Enable_Clock (Audio_SAI); - Enable_Clock (SAI_Pins); - - Configure_IO - (SAI_Pins, - (Mode => Mode_AF, - AF => SAI_Pins_AF, - AF_Output_Type => Push_Pull, - AF_Speed => Speed_High, - Resistors => Floating)); - Enable_Clock (Audio_DMA); - - -- Configure the DMA channel to the SAI peripheral Disable (Audio_DMA, Audio_DMA_Out_Stream); Configure (Audio_DMA, @@ -134,16 +200,38 @@ package body Audio is Memory_Burst_Size => Memory_Burst_Single, Peripheral_Burst_Size => Peripheral_Burst_Single)); Clear_All_Status (Audio_DMA, Audio_DMA_Out_Stream); + end Initialize_Audio_DMA; + + ------------------------------- + -- Initialize_Audio_Out_Pins -- + ------------------------------- + + procedure Initialize_Audio_Out_Pins is + begin + Enable_Clock (Audio_SAI); + Enable_Clock (SAI_Pins); + + Configure_IO + (SAI_Pins, + (Mode => Mode_AF, + AF => SAI_Pins_AF, + AF_Output_Type => Push_Pull, + AF_Speed => Speed_High, + Resistors => Floating)); end Initialize_Audio_Out_Pins; ------------------------ -- Initialize_SAI_Out -- ------------------------ - procedure Initialize_SAI_Out (Freq : Audio_Frequency) + procedure Initialize_SAI_Out + (Freq : Audio_Frequency; + Sink : Audio_Output_Device) is + Active_Slots : SAI_Slots; begin STM32.SAI.Disable (Audio_SAI, SAI_Out_Block); + STM32.SAI.Configure_Audio_Block (Audio_SAI, SAI_Out_Block, @@ -158,6 +246,7 @@ package body Audio is Synchronization => Asynchronous_Mode, Output_Drive => Drive_Immediate, FIFO_Threshold => FIFO_1_Quarter_Full); + STM32.SAI.Configure_Block_Frame (Audio_SAI, SAI_Out_Block, @@ -166,13 +255,24 @@ package body Audio is Frame_Sync => FS_Frame_And_Channel_Identification, FS_Polarity => FS_Active_Low, FS_Offset => Before_First_Bit); + + case Sink is + when Headphone | Auto => + Active_Slots := Slot_0 or Slot_2; + when Speaker => + Active_Slots := Slot_1 or Slot_3; + when Both => + Active_Slots := Slot_0 or Slot_1 or Slot_2 or Slot_3; + end case; + STM32.SAI.Configure_Block_Slot (Audio_SAI, SAI_Out_Block, First_Bit_Offset => 0, Slot_Size => Data_Size, Number_Of_Slots => 4, - Enabled_Slots => Slot_0 or Slot_2); + Enabled_Slots => Active_Slots); + STM32.SAI.Enable (Audio_SAI, SAI_Out_Block); end Initialize_SAI_Out; @@ -180,8 +280,7 @@ package body Audio is -- Initialize_Audio_I2C -- -------------------------- - procedure Initialize_Audio_I2C - is + procedure Initialize_Audio_I2C is begin STM32.Setup.Setup_I2C_Master (Port => Audio_I2C, SDA => Audio_I2C_SDA, @@ -191,41 +290,6 @@ package body Audio is Clock_Speed => 100_000); end Initialize_Audio_I2C; - ---------------- - -- Initialize -- - ---------------- - - procedure Initialize_Audio_Out - (This : in out WM8994_Audio_Device; - Volume : Audio_Volume; - Frequency : Audio_Frequency) - is - begin - STM32.SAI.Deinitialize (Audio_SAI, SAI_Out_Block); - - Set_Audio_Clock (Frequency); - - -- Initialize the SAI - Initialize_Audio_Out_Pins; - Initialize_SAI_Out (Frequency); - - -- Initialize the I2C Port to send commands to the driver - Initialize_Audio_I2C; - - if This.Device.Read_ID /= WM8994.WM8994_ID then - raise Constraint_Error with "Invalid ID received from the Audio Code"; - end if; - - This.Device.Reset; - This.Device.Init - (Input => WM8994.No_Input, - Output => WM8994.Auto, - Volume => UInt8 (Volume), - Frequency => - WM8994.Audio_Frequency'Enum_Val - (Audio_Frequency'Enum_Rep (Frequency))); - end Initialize_Audio_Out; - ---------- -- Play -- ---------- @@ -268,8 +332,7 @@ package body Audio is -- Resume -- ------------ - procedure Resume (This : in out WM8994_Audio_Device) - is + procedure Resume (This : in out WM8994_Audio_Device) is begin This.Device.Resume; DMA_Resume (Audio_SAI, SAI_Out_Block); @@ -279,8 +342,7 @@ package body Audio is -- Stop -- ---------- - procedure Stop (This : in out WM8994_Audio_Device) - is + procedure Stop (This : in out WM8994_Audio_Device) is begin This.Device.Stop (WM8994.Stop_Power_Down_Sw); DMA_Stop (Audio_SAI, SAI_Out_Block); @@ -289,32 +351,12 @@ package body Audio is STM32.DMA.Clear_All_Status (Audio_DMA, Audio_DMA_Out_Stream); end Stop; - ---------------- - -- Set_Volume -- - ---------------- - - procedure Set_Volume - (This : in out WM8994_Audio_Device; - Volume : Audio_Volume) - is - begin - This.Device.Set_Volume (UInt8 (Volume)); - end Set_Volume; - - ------------------- - -- Set_Frequency -- - ------------------- + ---------------------- + -- As_Device_Volume -- + ---------------------- - procedure Set_Frequency - (This : in out WM8994_Audio_Device; - Frequency : Audio_Frequency) - is - pragma Unreferenced (This); - begin - Set_Audio_Clock (Frequency); - STM32.SAI.Disable (Audio_SAI, SAI_Out_Block); - Initialize_SAI_Out (Frequency); - STM32.SAI.Enable (Audio_SAI, SAI_Out_Block); - end Set_Frequency; + function As_Device_Volume (Input : Audio_Volume) return WM8994.Volume_Level is + (if Input = 100 then WM8994.Max_Volume + else UInt16 (Input) * WM8994.Max_Volume / 100); end Audio; diff --git a/boards/stm32_common/stm32f746disco/audio.ads b/boards/stm32_common/stm32f746disco/audio.ads index 04655c87d..07f578c59 100644 --- a/boards/stm32_common/stm32f746disco/audio.ads +++ b/boards/stm32_common/stm32f746disco/audio.ads @@ -32,28 +32,45 @@ -- @author MCD Application Team -- ------------------------------------------------------------------------------ -with HAL.Audio; use HAL.Audio; +-- with HAL.Audio; use HAL.Audio; +with HAL; use HAL; with HAL.I2C; use HAL.I2C; with Ravenscar_Time; -private with WM8994; +with WM8994; package Audio is type WM8994_Audio_Device (Port : not null Any_I2C_Port) is tagged limited private; - procedure Initialize_Audio_Out + type Audio_Output_Device is new WM8994.Output_Device + range WM8994.Speaker .. WM8994.Auto; + -- Only No_Output is not included + + type Audio_Frequency is new WM8994.Audio_Frequency; + -- TODO: use HAL.Audio package's type, with WM8994 additions to that type + + type Audio_Volume is range 0 .. 100; -- a percentage + -- TODO: use HAL.Audio package's type + + type Audio_Buffer is array (Natural range <>) of UInt16 + with Component_Size => 16, Alignment => 2; + -- TODO: change to signed 16-bit, since that's apparently what should be used for PCM samples, + -- so revert the change to HAL.Audio.Audio_Buffer (so it is INteger_16 again) and use that. + + procedure Initialize (This : in out WM8994_Audio_Device; Volume : Audio_Volume; - Frequency : Audio_Frequency); + Frequency : Audio_Frequency; + Sink : Audio_Output_Device); procedure Set_Volume (This : in out WM8994_Audio_Device; Volume : Audio_Volume); procedure Set_Frequency - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_Device; Frequency : Audio_Frequency); procedure Play @@ -73,9 +90,11 @@ private Audio_I2C_Addr : constant I2C_Address := 16#34#; - type WM8994_Audio_Device (Port : not null Any_I2C_Port) is - tagged limited record + type WM8994_Audio_Device + (Port : not null Any_I2C_Port) + is tagged limited record Device : WM8994.WM8994_Device (Port, Audio_I2C_Addr, Ravenscar_Time.Delays); + Sink : Audio_Output_Device; end record; end Audio; diff --git a/components/src/audio/W8994/wm8994.adb b/components/src/audio/W8994/wm8994.adb index 860fe601d..8bf49c6bd 100644 --- a/components/src/audio/W8994/wm8994.adb +++ b/components/src/audio/W8994/wm8994.adb @@ -31,20 +31,20 @@ package body WM8994 is - WM8994_CHIPID_ADDR : constant := 16#00#; + WM8994_CHIPID_ADDR : constant := 16#00#; - Output_Enabled : Boolean := False; - Input_Enabled : Boolean := False; + Output_Enabled : Boolean := False; + Input_Enabled : Boolean := False; pragma Unreferenced (Input_Enabled); - --------------- -- I2C_Write -- --------------- - procedure I2C_Write (This : in out WM8994_Device; - Reg : UInt16; - Value : UInt16) + procedure I2C_Write + (This : in out WM8994_Device; + Register : UInt16; + Value : UInt16) is Status : I2C_Status; Data : I2C_Data (1 .. 2); @@ -56,13 +56,13 @@ package body WM8994 is This.Port.Mem_Write (Addr => This.I2C_Addr, - Mem_Addr => Reg, + Mem_Addr => Register, Mem_Addr_Size => Memory_Size_16b, Data => Data, Status => Status); - if Reg /= 0 then - Check := I2C_Read (This, Reg); + if Register /= 0 then + Check := I2C_Read (This, Register); end if; end I2C_Write; @@ -70,40 +70,40 @@ package body WM8994 is -- I2C_Read -- -------------- - function I2C_Read (This : in out WM8994_Device; - Reg : UInt16) - return UInt16 + function I2C_Read + (This : in out WM8994_Device; + Register : UInt16) + return UInt16 is Status : I2C_Status; Data : I2C_Data (1 .. 2); - Ret : UInt16; + Result : UInt16; begin This.Port.Mem_Read (Addr => This.I2C_Addr, - Mem_Addr => Reg, + Mem_Addr => Register, Mem_Addr_Size => Memory_Size_16b, Data => Data, Status => Status); - Ret := Shift_Left (UInt16 (Data (1)), 8) or UInt16 (Data (2)); - - return Ret; + Result := Shift_Left (UInt16 (Data (1)), 8) or UInt16 (Data (2)); + return Result; end I2C_Read; - ---------- - -- Init -- - ---------- + ---------------- + -- Initialize -- + ---------------- - procedure Init + procedure Initialize (This : in out WM8994_Device; Input : Input_Device; Output : Output_Device; - Volume : UInt8; + Volume : Volume_Level; Frequency : Audio_Frequency) is Power_Mgnt_Reg_1 : UInt16 := 0; - begin - -- WM8994 Errata work-arounds + -- WM8994 Errata work-arounds. + -- See https://github.com/STMicroelectronics/stm32-wm8994/blob/main/wm8994.c I2C_Write (This, 16#102#, 16#0003#); I2C_Write (This, 16#817#, 16#0000#); I2C_Write (This, 16#102#, 16#0000#); @@ -302,16 +302,16 @@ package body WM8994 is -- Volume Control This.Set_Volume (Volume); end if; - end Init; + end Initialize; ------------- - -- Read_ID -- + -- Chip_ID -- ------------- - function Read_ID (This : in out WM8994_Device) return UInt16 is + function Chip_ID (This : in out WM8994_Device) return UInt16 is begin return This.I2C_Read (WM8994_CHIPID_ADDR); - end Read_ID; + end Chip_ID; ---------- -- Play -- @@ -375,7 +375,7 @@ package body WM8994 is -- Disable DAC1 and DAC2 I2C_Write (This, 16#05#, 16#0000#); - -- Reset Codec by writing in 0x0000 address register + -- Reset Codec by writing in 16#0000 address register I2C_Write (This, 16#0000#, 16#0000#); end if; end Stop; @@ -384,13 +384,10 @@ package body WM8994 is -- Set_Volume -- ---------------- - procedure Set_Volume (This : in out WM8994_Device; Volume : Volume_Level) + procedure Set_Volume + (This : in out WM8994_Device; + Volume : Volume_Level) is - -- Actual Volume in range 0 .. 16#3F# - Converted_Volume : constant UInt16 := - (if Volume = 100 then 63 - else UInt16 (Volume) * 63 / 100); - begin if Volume = 0 then -- Mute the codec @@ -399,16 +396,16 @@ package body WM8994 is This.Set_Mute (Mute_Off); -- Left Headphone Volume - I2C_Write (This, 16#1C#, Converted_Volume or 16#140#); + I2C_Write (This, 16#1C#, Volume or 16#140#); -- Right Headphone volume - I2C_Write (This, 16#1D#, Converted_Volume or 16#140#); + I2C_Write (This, 16#1D#, Volume or 16#140#); -- Left Speaker volume - I2C_Write (This, 16#26#, Converted_Volume or 16#140#); + I2C_Write (This, 16#26#, Volume or 16#140#); -- Right Speaker volume - I2C_Write (This, 16#27#, Converted_Volume or 16#140#); + I2C_Write (This, 16#27#, Volume or 16#140#); end if; end Set_Volume; @@ -438,8 +435,9 @@ package body WM8994 is -- Set_Output_Mode -- --------------------- - procedure Set_Output_Mode (This : in out WM8994_Device; - Device : Output_Device) + procedure Set_Output_Mode + (This : in out WM8994_Device; + Device : Output_Device) is begin case Device is @@ -495,32 +493,62 @@ package body WM8994 is -- Set_Frequency -- ------------------- - procedure Set_Frequency (This : in out WM8994_Device; - Freq : Audio_Frequency) + procedure Set_Frequency + (This : in out WM8994_Device; + Freq : Audio_Frequency) is begin + -- In the following, the values written set both the AIF1_SR [3:0] bits + -- and the AIF1CLK_RATE [3:0] bits (the latter to set the ratio). The + -- ratio is always 256, which is indicated by the bit pattern 2#0011#, + -- so the lower digits in the values is always 3 in hex. + -- + -- See the table labled "Register 0210h AIF1 Rate" pages 285 and 286 of + -- WM8994_Rev4.6 from Cirrus Logic case Freq is when Audio_Freq_8kHz => -- AIF1 Sample Rate = 8 (kHz), ratio=256 I2C_Write (This, 16#210#, 16#0003#); - when Audio_Freq_16kHz => - -- AIF1 Sample Rate = 16 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0033#); - when Audio_Freq_48kHz => - -- AIF1 Sample Rate = 48 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0083#); - when Audio_Freq_96kHz => - -- AIF1 Sample Rate = 96 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#00A3#); + when Audio_Freq_11kHz => -- AIF1 Sample Rate = 11.025 (kHz), ratio=256 I2C_Write (This, 16#210#, 16#0013#); + + when Audio_Freq_12kHz => + -- AIF1 Sample Rate = 12 (kHz), ratio=256 + I2C_Write (This, 16#210#, 16#0023#); + + when Audio_Freq_16kHz => + -- AIF1 Sample Rate = 16 (kHz), ratio=256 + I2C_Write (This, 16#210#, 16#0033#); + when Audio_Freq_22kHz => -- AIF1 Sample Rate = 22.050 (kHz), ratio=256 I2C_Write (This, 16#210#, 16#0043#); + + when Audio_Freq_24kHz => + -- AIF1 Sample Rate = 24 (kHz), ratio=256 + I2C_Write (This, 16#210#, 16#0053#); + + when Audio_Freq_32kHz => + -- AIF1 Sample Rate = 32 (kHz), ratio=256 + I2C_Write (This, 16#210#, 16#0063#); + when Audio_Freq_44kHz => -- AIF1 Sample Rate = 44.1 (kHz), ratio=256 I2C_Write (This, 16#210#, 16#0073#); + + when Audio_Freq_48kHz => + -- AIF1 Sample Rate = 48 (kHz), ratio=256 + I2C_Write (This, 16#210#, 16#0083#); + + when Audio_Freq_88kHz => + -- AIF1 Sample Rate = 88.2 (kHz), ratio=256 + I2C_Write (This, 16#210#, 16#0093#); + + when Audio_Freq_96kHz => + -- AIF1 Sample Rate = 96 (kHz), ratio=256 + I2C_Write (This, 16#210#, 16#00A3#); end case; end Set_Frequency; diff --git a/components/src/audio/W8994/wm8994.ads b/components/src/audio/W8994/wm8994.ads index fe55d45b5..1fd3211d1 100644 --- a/components/src/audio/W8994/wm8994.ads +++ b/components/src/audio/W8994/wm8994.ads @@ -37,12 +37,16 @@ with HAL.Time; package WM8994 is + -- TODO: see page 144 Datasheet WM8994_Rev4.6, from Cirrus Logic regarding + -- "For suppression of pop noise ..." + type Output_Device is (No_Output, Speaker, Headphone, Both, Auto); + type Input_Device is (No_Input, Microphone, @@ -53,21 +57,38 @@ package WM8994 is type Audio_Frequency is (Audio_Freq_8kHz, Audio_Freq_11kHz, + Audio_Freq_12kHz, Audio_Freq_16kHz, Audio_Freq_22kHz, + Audio_Freq_24kHz, + Audio_Freq_32kHz, Audio_Freq_44kHz, Audio_Freq_48kHz, + Audio_Freq_88kHz, Audio_Freq_96kHz) with Size => 32; + -- Sample rates from 8kHz to 96kHz are all supported, per Datasheet + -- WM8994_Rev4.6, pages 43 and 93, from Cirrus Logic. See all Tables 37 + -- and 41. + -- + -- Note that 88.2kHz and 96kHz modes are supported for AIF1 input (DAC + -- playback) only. + for Audio_Frequency use (Audio_Freq_8kHz => 8_000, Audio_Freq_11kHz => 11_025, + Audio_Freq_12kHz => 12_000, Audio_Freq_16kHz => 16_000, Audio_Freq_22kHz => 22_050, + Audio_Freq_24kHz => 24_000, + Audio_Freq_32kHz => 32_000, Audio_Freq_44kHz => 44_100, Audio_Freq_48kHz => 48_000, + Audio_Freq_88kHz => 88_200, Audio_Freq_96kHz => 96_000); + -- TODO: support 24K and 32K frequencies + type Mute is (Mute_On, Mute_Off); @@ -84,7 +105,9 @@ package WM8994 is -- is set to default configuration (user should re-Initialize the codec in -- order to play again the audio stream). - subtype Volume_Level is UInt8 range 0 .. 100; + Max_Volume : constant := 16#3F#; + + subtype Volume_Level is UInt16 range 0 .. Max_Volume; type WM8994_Device (Port : not null Any_I2C_Port; @@ -92,35 +115,49 @@ package WM8994 is Time : not null HAL.Time.Any_Delays) is tagged limited private; - procedure Init (This : in out WM8994_Device; - Input : Input_Device; - Output : Output_Device; - Volume : UInt8; - Frequency : Audio_Frequency); + procedure Initialize + (This : in out WM8994_Device; + Input : Input_Device; + Output : Output_Device; + Volume : Volume_Level; + Frequency : Audio_Frequency); + + function Chip_ID (This : in out WM8994_Device) return UInt16; - function Read_ID (This : in out WM8994_Device) return UInt16; procedure Play (This : in out WM8994_Device); + procedure Pause (This : in out WM8994_Device); + procedure Resume (This : in out WM8994_Device); + procedure Stop (This : in out WM8994_Device; Cmd : Stop_Mode); + procedure Set_Volume (This : in out WM8994_Device; Volume : Volume_Level); + procedure Set_Mute (This : in out WM8994_Device; Cmd : Mute); + procedure Set_Output_Mode (This : in out WM8994_Device; Device : Output_Device); + procedure Set_Frequency (This : in out WM8994_Device; Freq : Audio_Frequency); + procedure Reset (This : in out WM8994_Device); private - type WM8994_Device (Port : not null Any_I2C_Port; - I2C_Addr : UInt10; - Time : not null HAL.Time.Any_Delays) is tagged limited null record; - - procedure I2C_Write (This : in out WM8994_Device; - Reg : UInt16; - Value : UInt16); - function I2C_Read (This : in out WM8994_Device; - Reg : UInt16) + + type WM8994_Device + (Port : not null Any_I2C_Port; + I2C_Addr : UInt10; + Time : not null HAL.Time.Any_Delays) + is tagged limited null record; + + procedure I2C_Write (This : in out WM8994_Device; + Register : UInt16; + Value : UInt16); + + function I2C_Read (This : in out WM8994_Device; + Register : UInt16) return UInt16; end WM8994; From 7e0024e945234f8652f505cde082796e1356c9c7 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Wed, 27 Mar 2024 18:11:53 -0500 Subject: [PATCH 02/22] use new IO package for WM8994, with constants for register addresses --- components/src/audio/W8994/wm8994-io.adb | 55 +++++ components/src/audio/W8994/wm8994-io.ads | 250 +++++++++++++++++++++++ components/src/audio/W8994/wm8994.adb | 243 +++++++++------------- components/src/audio/W8994/wm8994.ads | 8 - 4 files changed, 401 insertions(+), 155 deletions(-) create mode 100644 components/src/audio/W8994/wm8994-io.adb create mode 100644 components/src/audio/W8994/wm8994-io.ads diff --git a/components/src/audio/W8994/wm8994-io.adb b/components/src/audio/W8994/wm8994-io.adb new file mode 100644 index 000000000..df4a8b9cf --- /dev/null +++ b/components/src/audio/W8994/wm8994-io.adb @@ -0,0 +1,55 @@ +package body WM8994.IO is + + --------------- + -- I2C_Write -- + --------------- + + procedure I2C_Write + (This : in out WM8994_Device; + Register : Register_Address; + Value : UInt16) + is + Status : I2C_Status; + Data : I2C_Data (1 .. 2); + Check : UInt16 with Unreferenced; + begin + -- Device is MSB first + Data (1) := UInt8 (Shift_Right (Value and 16#FF00#, 8)); + Data (2) := UInt8 (Value and 16#FF#); + + This.Port.Mem_Write + (Addr => This.I2C_Addr, + Mem_Addr => UInt16 (Register), + Mem_Addr_Size => Memory_Size_16b, + Data => Data, + Status => Status); + + if Register /= 0 then + Check := I2C_Read (This, Register); + end if; + end I2C_Write; + + -------------- + -- I2C_Read -- + -------------- + + function I2C_Read + (This : in out WM8994_Device; + Register : Register_Address) + return UInt16 + is + Status : I2C_Status; + Data : I2C_Data (1 .. 2); + Result : UInt16; + begin + This.Port.Mem_Read + (Addr => This.I2C_Addr, + Mem_Addr => UInt16 (Register), + Mem_Addr_Size => Memory_Size_16b, + Data => Data, + Status => Status); + Result := Shift_Left (UInt16 (Data (1)), 8) or UInt16 (Data (2)); + return Result; + end I2C_Read; + +end WM8994.IO; diff --git a/components/src/audio/W8994/wm8994-io.ads b/components/src/audio/W8994/wm8994-io.ads new file mode 100644 index 000000000..b9093fc11 --- /dev/null +++ b/components/src/audio/W8994/wm8994-io.ads @@ -0,0 +1,250 @@ +package WM8994.IO is + + type Register_Address is new UInt16; + + procedure I2C_Write + (This : in out WM8994_Device; + Register : Register_Address; + Value : UInt16); + + function I2C_Read + (This : in out WM8994_Device; + Register : Register_Address) + return UInt16; + + WM8994_SW_Reset : constant Register_Address := 16#0000#; + WM8994_PWR_Management_1 : constant Register_Address := 16#0001#; + WM8994_PWR_Management_2 : constant Register_Address := 16#0002#; + WM8994_PWR_Management_3 : constant Register_Address := 16#0003#; + WM8994_PWR_Management_4 : constant Register_Address := 16#0004#; + WM8994_PWR_Management_5 : constant Register_Address := 16#0005#; + WM8994_PWR_Management_6 : constant Register_Address := 16#0006#; + WM8994_Input_Mixer_1 : constant Register_Address := 16#0015#; + WM8994_Left_Line_In12_Vol : constant Register_Address := 16#0018#; + WM8994_Left_Line_In34_Vol : constant Register_Address := 16#0019#; + WM8994_Right_Line_In12_Vol : constant Register_Address := 16#001A#; + WM8994_Right_Line_In34_Vol : constant Register_Address := 16#001B#; + WM8994_Left_Output_Vol : constant Register_Address := 16#001C#; + WM8994_Right_Output_Vol : constant Register_Address := 16#001D#; + WM8994_Line_Output_Vol : constant Register_Address := 16#001E#; + WM8994_Output2_Vol : constant Register_Address := 16#001F#; + WM8994_Left_OPGA_Vol : constant Register_Address := 16#0020#; + WM8994_Right_OPGA_Vol : constant Register_Address := 16#0021#; + WM8994_SPKMIXL_ATT : constant Register_Address := 16#0022#; + WM8994_SPKMIXR_ATT : constant Register_Address := 16#0023#; + WM8994_Output_Mixer : constant Register_Address := 16#0024#; + WM8994_CLASS_D : constant Register_Address := 16#0025#; + WM8994_SPK_Left_Vol : constant Register_Address := 16#0026#; + WM8994_SPK_Right_Vol : constant Register_Address := 16#0027#; + WM8994_Input_Mixer_2 : constant Register_Address := 16#0028#; + WM8994_Input_Mixer_3 : constant Register_Address := 16#0029#; + WM8994_Input_Mixer_4 : constant Register_Address := 16#002A#; + WM8994_Input_Mixer_5 : constant Register_Address := 16#002B#; + WM8994_Input_Mixer_6 : constant Register_Address := 16#002C#; + WM8994_Output_Mixer_1 : constant Register_Address := 16#002D#; + WM8994_Output_Mixer_2 : constant Register_Address := 16#002E#; + WM8994_Output_Mixer_3 : constant Register_Address := 16#002F#; + WM8994_Output_Mixer_4 : constant Register_Address := 16#0030#; + WM8994_Output_Mixer_5 : constant Register_Address := 16#0031#; + WM8994_Output_Mixer_6 : constant Register_Address := 16#0032#; + WM8994_Output2_Mixer : constant Register_Address := 16#0033#; + WM8994_Line_Mixer_1 : constant Register_Address := 16#0034#; + WM8994_Line_Mixer_2 : constant Register_Address := 16#0035#; + WM8994_Speaker_Mixer : constant Register_Address := 16#0036#; + WM8994_Add_Control : constant Register_Address := 16#0037#; + WM8994_AntiPop1 : constant Register_Address := 16#0038#; + WM8994_AntiPop2 : constant Register_Address := 16#0039#; + WM8994_MicBias : constant Register_Address := 16#003A#; + WM8994_LDO1 : constant Register_Address := 16#003B#; + WM8994_LDO2 : constant Register_Address := 16#003C#; + WM8994_Charge_Pump1 : constant Register_Address := 16#004C#; + WM8994_Charge_Pump2 : constant Register_Address := 16#004D#; + WM8994_CLASS_W : constant Register_Address := 16#0051#; + WM8994_DC_Servo1 : constant Register_Address := 16#0054#; + WM8994_DC_Servo2 : constant Register_Address := 16#0055#; + WM8994_DC_Servo_Readback : constant Register_Address := 16#0058#; + WM8994_DC_Servo_Writeval : constant Register_Address := 16#0059#; + WM8994_Analog_HP : constant Register_Address := 16#0060#; + WM8994_Chip_Revision : constant Register_Address := 16#0100#; + WM8994_Control_Interface : constant Register_Address := 16#0101#; + WM8994_WRITE_SEQ_CTRL1 : constant Register_Address := 16#0110#; + WM8994_WRITE_SEQ_CTRL2 : constant Register_Address := 16#0111#; + WM8994_AIF1_Clocking1 : constant Register_Address := 16#0200#; + WM8994_AIF1_Clocking2 : constant Register_Address := 16#0201#; + WM8994_AIF2_Clocking1 : constant Register_Address := 16#0204#; + WM8994_AIF2_Clocking2 : constant Register_Address := 16#0205#; + WM8994_Clocking1 : constant Register_Address := 16#0208#; + WM8994_Clocking2 : constant Register_Address := 16#0209#; + WM8994_AIF1_Rate : constant Register_Address := 16#0210#; + WM8994_AIF2_Rate : constant Register_Address := 16#0211#; + WM8994_Rate_Status : constant Register_Address := 16#0212#; + WM8994_FLL1_Control1 : constant Register_Address := 16#0220#; + WM8994_FLL1_Control2 : constant Register_Address := 16#0221#; + WM8994_FLL1_Control3 : constant Register_Address := 16#0222#; + WM8994_FLL1_Control4 : constant Register_Address := 16#0223#; + WM8994_FLL1_Control5 : constant Register_Address := 16#0224#; + WM8994_FLL2_Control1 : constant Register_Address := 16#0240#; + WM8994_FLL2_Control2 : constant Register_Address := 16#0241#; + WM8994_FLL2_Control3 : constant Register_Address := 16#0242#; + WM8994_FLL2_Control4 : constant Register_Address := 16#0243#; + WM8994_FLL2_Control5 : constant Register_Address := 16#0244#; + WM8994_AIF1_Control1 : constant Register_Address := 16#0300#; + WM8994_AIF1_Control2 : constant Register_Address := 16#0301#; + WM8994_AIF1_Master_Slave : constant Register_Address := 16#0302#; + WM8994_AIF1_BCLK : constant Register_Address := 16#0303#; + WM8994_AIF1_ADC_LRCLK : constant Register_Address := 16#0304#; + WM8994_AIF1_DAC_LRCLK : constant Register_Address := 16#0305#; + WM8994_AIF1_DAC_DELTA : constant Register_Address := 16#0306#; + WM8994_AIF1_ADC_DELTA : constant Register_Address := 16#0307#; + WM8994_AIF2_Control1 : constant Register_Address := 16#0310#; + WM8994_AIF2_Control2 : constant Register_Address := 16#0311#; + WM8994_AIF2_Master_Slave : constant Register_Address := 16#0312#; + WM8994_AIF2_BCLK : constant Register_Address := 16#0313#; + WM8994_AIF2_ADC_LRCLK : constant Register_Address := 16#0314#; + WM8994_AIF2_DAC_LRCLK : constant Register_Address := 16#0315#; + WM8994_AIF2_DAC_DELTA : constant Register_Address := 16#0316#; + WM8994_AIF2_ADC_DELTA : constant Register_Address := 16#0317#; + WM8994_AIF1_ADC1_Left_Vol : constant Register_Address := 16#0400#; + WM8994_AIF1_ADC1_Right_Vol : constant Register_Address := 16#0401#; + WM8994_AIF1_DAC1_Left_Vol : constant Register_Address := 16#0402#; + WM8994_AIF1_DAC1_Right_Vol : constant Register_Address := 16#0403#; + WM8994_AIF1_ADC2_Left_Vol : constant Register_Address := 16#0404#; + WM8994_AIF1_ADC2_Right_Vol : constant Register_Address := 16#0405#; + WM8994_AIF1_DAC2_Left_Vol : constant Register_Address := 16#0406#; + WM8994_AIF1_DAC2_Right_Vol : constant Register_Address := 16#0407#; + WM8994_AIF1_ADC1_Filters : constant Register_Address := 16#0410#; + WM8994_AIF1_ADC2_Filters : constant Register_Address := 16#0411#; + WM8994_AIF1_DAC1_Filter1 : constant Register_Address := 16#0420#; + WM8994_AIF1_DAC1_Filter2 : constant Register_Address := 16#0421#; + WM8994_AIF1_DAC2_Filter1 : constant Register_Address := 16#0422#; + WM8994_AIF1_DAC2_Filter2 : constant Register_Address := 16#0423#; + WM8994_AIF1_DRC1 : constant Register_Address := 16#0440#; + WM8994_AIF1_DRC1_1 : constant Register_Address := 16#0441#; + WM8994_AIF1_DRC1_2 : constant Register_Address := 16#0442#; + WM8994_AIF1_DRC1_3 : constant Register_Address := 16#0443#; + WM8994_AIF1_DRC1_4 : constant Register_Address := 16#0444#; + WM8994_AIF1_DRC2 : constant Register_Address := 16#0450#; + WM8994_AIF1_DRC2_1 : constant Register_Address := 16#0451#; + WM8994_AIF1_DRC2_2 : constant Register_Address := 16#0452#; + WM8994_AIF1_DRC2_3 : constant Register_Address := 16#0453#; + WM8994_AIF1_DRC2_4 : constant Register_Address := 16#0454#; + WM8994_AIF1_DAC1_EQG_1 : constant Register_Address := 16#0480#; + WM8994_AIF1_DAC1_EQG_2 : constant Register_Address := 16#0481#; + WM8994_AIF1_DAC1_EQG_1A : constant Register_Address := 16#0482#; + WM8994_AIF1_DAC1_EQG_1B : constant Register_Address := 16#0483#; + WM8994_AIF1_DAC1_EQG_1PG : constant Register_Address := 16#0484#; + WM8994_AIF1_DAC1_EQG_2A : constant Register_Address := 16#0485#; + WM8994_AIF1_DAC1_EQG_2B : constant Register_Address := 16#0486#; + WM8994_AIF1_DAC1_EQG_2C : constant Register_Address := 16#0487#; + WM8994_AIF1_DAC1_EQG_2PG : constant Register_Address := 16#0488#; + WM8994_AIF1_DAC1_EQG_3A : constant Register_Address := 16#0489#; + WM8994_AIF1_DAC1_EQG_3B : constant Register_Address := 16#048A#; + WM8994_AIF1_DAC1_EQG_3C : constant Register_Address := 16#048B#; + WM8994_AIF1_DAC1_EQG_3PG : constant Register_Address := 16#048C#; + WM8994_AIF1_DAC1_EQG_4A : constant Register_Address := 16#048D#; + WM8994_AIF1_DAC1_EQG_4B : constant Register_Address := 16#048E#; + WM8994_AIF1_DAC1_EQG_4C : constant Register_Address := 16#048F#; + WM8994_AIF1_DAC1_EQG_4PG : constant Register_Address := 16#0490#; + WM8994_AIF1_DAC1_EQG_5A : constant Register_Address := 16#0491#; + WM8994_AIF1_DAC1_EQG_5B : constant Register_Address := 16#0492#; + WM8994_AIF1_DAC1_EQG_5PG : constant Register_Address := 16#0493#; + WM8994_AIF1_DAC2_EQG_1 : constant Register_Address := 16#04A0#; + WM8994_AIF1_DAC2_EQG_2 : constant Register_Address := 16#04A1#; + WM8994_AIF1_DAC2_EQG_1A : constant Register_Address := 16#04A2#; + WM8994_AIF1_DAC2_EQG_1B : constant Register_Address := 16#04A3#; + WM8994_AIF1_DAC2_EQG_1PG : constant Register_Address := 16#04A4#; + WM8994_AIF1_DAC2_EQG_2A : constant Register_Address := 16#04A5#; + WM8994_AIF1_DAC2_EQG_2B : constant Register_Address := 16#04A6#; + WM8994_AIF1_DAC2_EQG_2C : constant Register_Address := 16#04A7#; + WM8994_AIF1_DAC2_EQG_2PG : constant Register_Address := 16#04A8#; + WM8994_AIF1_DAC2_EQG_3A : constant Register_Address := 16#04A9#; + WM8994_AIF1_DAC2_EQG_3B : constant Register_Address := 16#04AA#; + WM8994_AIF1_DAC2_EQG_3C : constant Register_Address := 16#04AB#; + WM8994_AIF1_DAC2_EQG_3PG : constant Register_Address := 16#04AC#; + WM8994_AIF1_DAC2_EQG_4A : constant Register_Address := 16#04AD#; + WM8994_AIF1_DAC2_EQG_4B : constant Register_Address := 16#04AE#; + WM8994_AIF1_DAC2_EQG_4C : constant Register_Address := 16#04AF#; + WM8994_AIF1_DAC2_EQG_4PG : constant Register_Address := 16#04B0#; + WM8994_AIF1_DAC2_EQG_5A : constant Register_Address := 16#04B1#; + WM8994_AIF1_DAC2_EQG_5B : constant Register_Address := 16#04B2#; + WM8994_AIF1_DAC2_EQG_5PG : constant Register_Address := 16#04B3#; + WM8994_AIF2_ADC_Left_Vol : constant Register_Address := 16#0500#; + WM8994_AIF2_ADC_Right_Vol : constant Register_Address := 16#0501#; + WM8994_AIF2_DAC_Left_Vol : constant Register_Address := 16#0502#; + WM8994_AIF2_DAC_Right_Vol : constant Register_Address := 16#0503#; + WM8994_AIF2_ADC_Filters : constant Register_Address := 16#0510#; + WM8994_AIF2_DAC_Filter_1 : constant Register_Address := 16#0520#; + WM8994_AIF2_DAC_Filter_2 : constant Register_Address := 16#0521#; + WM8994_AIF2_DRC_1 : constant Register_Address := 16#0540#; + WM8994_AIF2_DRC_2 : constant Register_Address := 16#0541#; + WM8994_AIF2_DRC_3 : constant Register_Address := 16#0542#; + WM8994_AIF2_DRC_4 : constant Register_Address := 16#0543#; + WM8994_AIF2_DRC_5 : constant Register_Address := 16#0544#; + WM8994_AIF2_EQG_1 : constant Register_Address := 16#0580#; + WM8994_AIF2_EQG_2 : constant Register_Address := 16#0581#; + WM8994_AIF2_EQG_1A : constant Register_Address := 16#0582#; + WM8994_AIF2_EQG_1B : constant Register_Address := 16#0583#; + WM8994_AIF2_EQG_1PG : constant Register_Address := 16#0584#; + WM8994_AIF2_EQG_2A : constant Register_Address := 16#0585#; + WM8994_AIF2_EQG_2B : constant Register_Address := 16#0586#; + WM8994_AIF2_EQG_2C : constant Register_Address := 16#0587#; + WM8994_AIF2_EQG_2PG : constant Register_Address := 16#0588#; + WM8994_AIF2_EQG_3A : constant Register_Address := 16#0589#; + WM8994_AIF2_EQG_3B : constant Register_Address := 16#058A#; + WM8994_AIF2_EQG_3C : constant Register_Address := 16#058B#; + WM8994_AIF2_EQG_3PG : constant Register_Address := 16#058C#; + WM8994_AIF2_EQG_4A : constant Register_Address := 16#058D#; + WM8994_AIF2_EQG_4B : constant Register_Address := 16#058E#; + WM8994_AIF2_EQG_4C : constant Register_Address := 16#058F#; + WM8994_AIF2_EQG_4PG : constant Register_Address := 16#0590#; + WM8994_AIF2_EQG_5A : constant Register_Address := 16#0591#; + WM8994_AIF2_EQG_5B : constant Register_Address := 16#0592#; + WM8994_AIF2_EQG_5PG : constant Register_Address := 16#0593#; + WM8994_DAC1_Mixer_Vol : constant Register_Address := 16#0600#; + WM8994_AIF1_DAC1_LMR : constant Register_Address := 16#0601#; + WM8994_AIF1_DAC1_RMR : constant Register_Address := 16#0602#; + WM8994_DAC2_Mixer_Vol : constant Register_Address := 16#0603#; + WM8994_AIF1_DAC2_LMR : constant Register_Address := 16#0604#; + WM8994_AIF1_DAC2_RMR : constant Register_Address := 16#0605#; + WM8994_AIF1_ADC1_LMR : constant Register_Address := 16#0606#; + WM8994_AIF1_ADC1_RMR : constant Register_Address := 16#0607#; + WM8994_AIF1_ADC2_LMR : constant Register_Address := 16#0608#; + WM8994_AIF1_ADC2_RMR : constant Register_Address := 16#0609#; + WM8994_DAC1_Left_Vol : constant Register_Address := 16#0610#; + WM8994_DAC1_Right_Vol : constant Register_Address := 16#0611#; + WM8994_DAC2_Left_Vol : constant Register_Address := 16#0612#; + WM8994_DAC2_Right_Vol : constant Register_Address := 16#0613#; + WM8994_DAC_SoftMute : constant Register_Address := 16#0614#; + WM8994_Oversampling : constant Register_Address := 16#0620#; + WM8994_Sidetone : constant Register_Address := 16#0621#; + WM8994_GPIO1 : constant Register_Address := 16#0700#; + WM8994_GPIO2 : constant Register_Address := 16#0701#; + WM8994_GPIO3 : constant Register_Address := 16#0702#; + WM8994_GPIO4 : constant Register_Address := 16#0703#; + WM8994_GPIO5 : constant Register_Address := 16#0704#; + WM8994_GPIO6 : constant Register_Address := 16#0705#; + WM8994_GPIO7 : constant Register_Address := 16#0706#; + WM8994_GPIO8 : constant Register_Address := 16#0707#; + WM8994_GPIO9 : constant Register_Address := 16#0708#; + WM8994_GPIO10 : constant Register_Address := 16#0709#; + WM8994_GPIO11 : constant Register_Address := 16#070A#; + WM8994_PULL_Control_1 : constant Register_Address := 16#0720#; + WM8994_PULL_Control_2 : constant Register_Address := 16#0721#; + WM8994_INT_Status_1 : constant Register_Address := 16#0730#; + WM8994_INT_Status_2 : constant Register_Address := 16#0731#; + WM8994_INT_Raw_Status_2 : constant Register_Address := 16#0732#; + WM8994_INT_Status1_Mask : constant Register_Address := 16#0738#; + WM8994_INT_Status2_Mask : constant Register_Address := 16#0739#; + WM8994_INT_Control : constant Register_Address := 16#0740#; + WM8994_IRQ_Debounce : constant Register_Address := 16#0748#; + WM8994_WRITE_Sequencer0 : constant Register_Address := 16#3000#; + WM8994_WRITE_Sequencer1 : constant Register_Address := 16#3001#; + WM8994_WRITE_Sequencer2 : constant Register_Address := 16#3002#; + WM8994_WRITE_Sequencer3 : constant Register_Address := 16#3003#; + WM8994_WRITE_Sequencer4 : constant Register_Address := 16#3508#; + WM8994_WRITE_Sequencer5 : constant Register_Address := 16#3509#; + WM8994_WRITE_Sequencer6 : constant Register_Address := 16#3510#; + WM8994_WRITE_Sequencer7 : constant Register_Address := 16#3511#; + WM8994_SW_Reset_Mask : constant Register_Address := 16#FFFF#; + +end WM8994.IO; diff --git a/components/src/audio/W8994/wm8994.adb b/components/src/audio/W8994/wm8994.adb index 8bf49c6bd..6c33279bb 100644 --- a/components/src/audio/W8994/wm8994.adb +++ b/components/src/audio/W8994/wm8994.adb @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015-2016, AdaCore -- +-- Copyright (C) 2015-2024, AdaCore -- -- -- -- Redistribution and use in source and binary forms, with or without -- -- modification, are permitted provided that the following conditions are -- @@ -28,6 +28,7 @@ -- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- -- -- ------------------------------------------------------------------------------ +with WM8994.IO; use WM8994.IO; package body WM8994 is @@ -37,58 +38,6 @@ package body WM8994 is Input_Enabled : Boolean := False; pragma Unreferenced (Input_Enabled); - --------------- - -- I2C_Write -- - --------------- - - procedure I2C_Write - (This : in out WM8994_Device; - Register : UInt16; - Value : UInt16) - is - Status : I2C_Status; - Data : I2C_Data (1 .. 2); - Check : UInt16 with Unreferenced; - begin - -- Device is MSB first - Data (1) := UInt8 (Shift_Right (Value and 16#FF00#, 8)); - Data (2) := UInt8 (Value and 16#FF#); - - This.Port.Mem_Write - (Addr => This.I2C_Addr, - Mem_Addr => Register, - Mem_Addr_Size => Memory_Size_16b, - Data => Data, - Status => Status); - - if Register /= 0 then - Check := I2C_Read (This, Register); - end if; - end I2C_Write; - - -------------- - -- I2C_Read -- - -------------- - - function I2C_Read - (This : in out WM8994_Device; - Register : UInt16) - return UInt16 - is - Status : I2C_Status; - Data : I2C_Data (1 .. 2); - Result : UInt16; - begin - This.Port.Mem_Read - (Addr => This.I2C_Addr, - Mem_Addr => Register, - Mem_Addr_Size => Memory_Size_16b, - Data => Data, - Status => Status); - Result := Shift_Left (UInt16 (Data (1)), 8) or UInt16 (Data (2)); - return Result; - end I2C_Read; - ---------------- -- Initialize -- ---------------- @@ -109,10 +58,10 @@ package body WM8994 is I2C_Write (This, 16#102#, 16#0000#); -- Enable VMID soft restart, Start-up Bias current enabled - I2C_Write (This, 16#39#, 16#006C#); + I2C_Write (This, WM8994_AntiPop2, 16#006C#); -- Enable BIAS generator, Enable VMID - I2C_Write (This, 16#01#, 16#0003#); + I2C_Write (This, WM8994_PWR_Management_1, 16#0003#); This.Time.Delay_Milliseconds (50); @@ -129,53 +78,53 @@ package body WM8994 is -- Enable AIF1ADC2 (Left), Enable AIF1ADC2 (Right) -- Enable DMICDAT2 (Left), Enable DMICDAT2 (Right) -- Enable Left ADC, Enable Right ADC - I2C_Write (This, 16#04#, 16#0C30#); + I2C_Write (This, WM8994_PWR_Management_4, 16#0C30#); -- Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right -- Timeslot 1 - I2C_Write (This, 16#450#, 16#00DB#); + I2C_Write (This, WM8994_AIF1_DRC2, 16#00DB#); -- Disable IN1L, IN1R, IN2L, IN2R, Enable Thermal sensor & -- shutdown - I2C_Write (This, 16#02#, 16#6000#); + I2C_Write (This, WM8994_PWR_Management_4, 16#6000#); -- Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path - I2C_Write (This, 16#608#, 16#0002#); + I2C_Write (This, WM8994_AIF1_ADC2_LMR, 16#0002#); -- Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path - I2C_Write (This, 16#609#, 16#0002#); + I2C_Write (This, WM8994_AIF1_ADC2_RMR, 16#0002#); -- GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC2 -- signal detect - I2C_Write (This, 16#700#, 16#000E#); + I2C_Write (This, WM8994_GPIO1, 16#000E#); when Input_Line => -- Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right) -- Enable Left ADC, Enable Right ADC - I2C_Write (This, 16#04#, 16#0303#); + I2C_Write (This, WM8994_PWR_Management_4, 16#0303#); -- Enable AIF1 DRC1 Signal Detect & DRC in AIF1ADC1 Left/Right -- Timeslot 0 - I2C_Write (This, 16#440#, 16#00DB#); + I2C_Write (This, WM8994_AIF1_DRC1, 16#00DB#); -- Enable IN1L and IN1R, Disable IN2L and IN2R, Enable Thermal -- sensor & shutdown - I2C_Write (This, 16#02#, 16#6350#); + I2C_Write (This, WM8994_PWR_Management_4, 16#6350#); -- Enable the ADCL(Left) to AIF1 Timeslot 0 (Left) mixer path - I2C_Write (This, 16#606#, 16#0002#); + I2C_Write (This, WM8994_AIF1_ADC1_LMR, 16#0002#); -- Enable the ADCR(Right) to AIF1 Timeslot 0 (Right) mixer path - I2C_Write (This, 16#607#, 16#0002#); + I2C_Write (This, WM8994_AIF1_ADC1_RMR, 16#0002#); -- GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 -- signal detect - I2C_Write (This, 16#700#, 16#000D#); + I2C_Write (This, WM8994_GPIO1, 16#000D#); end case; This.Set_Frequency (Frequency); -- AIF1 Word Length = 16-bits, AIF1 Format = I2S (Default Register -- Value) - I2C_Write (This, 16#300#, 16#4010#); + I2C_Write (This, WM8994_AIF1_Control1, 16#4010#); -- slave mode - I2C_Write (This, 16#302#, 16#0000#); + I2C_Write (This, WM8994_AIF1_Master_Slave, 16#0000#); -- Enable the DSP processing clock for AIF1, Enable the core clock - I2C_Write (This, 16#208#, 16#000A#); + I2C_Write (This, WM8994_Clocking1, 16#000A#); -- Enable AIF1 Clock, AIF1 Clock Source = MCLK1 pin - I2C_Write (This, 16#200#, 16#0001#); + I2C_Write (This, WM8994_AIF1_Clocking1, 16#0001#); if Output /= No_Output then -- Analog Output Configuration @@ -185,79 +134,79 @@ package body WM8994 is I2C_Write (This, 16#03#, 16#0300#); -- Left Speaker Mixer Volume = 0dB - I2C_Write (This, 16#22#, 16#0000#); + I2C_Write (This, WM8994_SPKMIXL_ATT, 16#0000#); -- Speaker output mode = Class D, Right Speaker Mixer Volume = 0dB -- ((16#23#, 16#0100#) = class AB) - I2C_Write (This, 16#23#, 16#0000#); + I2C_Write (This, WM8994_SPKMIXR_ATT, 16#0000#); -- Unmute DAC2 (Left) to Left Speaker Mixer (SPKMIXL) path, -- Unmute DAC2 (Right) to Right Speaker Mixer (SPKMIXR) path - I2C_Write (This, 16#36#, 16#0300#); + I2C_Write (This, WM8994_Speaker_Mixer, 16#0300#); -- Enable bias generator, Enable VMID, Enable SPKOUTL, Enable SPKOUTR - I2C_Write (This, 16#01#, 16#3003#); + I2C_Write (This, WM8994_PWR_Management_1, 16#3003#); -- Headphone/Speaker Enable -- Enable Class W, Class W Envelope Tracking = AIF1 Timeslot 0 - I2C_Write (This, 16#51#, 16#0001#); + I2C_Write (This, WM8994_CLASS_W, 16#0001#); -- Enable bias generator, Enable VMID, Enable HPOUT1 (Left) and -- Enable HPOUT1 (Right) input stages idem for Speaker Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0303# or 16#3003#; - I2C_Write (This, 16#01#, Power_Mgnt_Reg_1); + I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); -- Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate stages - I2C_Write (This, 16#60#, 16#0022#); + I2C_Write (This, WM8994_Analog_HP, 16#0022#); -- Enable Charge Pump - I2C_Write (This, 16#4C#, 16#9F25#); + I2C_Write (This, WM8994_Charge_Pump1, 16#9F25#); -- Add Delay This.Time.Delay_Milliseconds (15); -- Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path - I2C_Write (This, 16#2D#, 16#0001#); + I2C_Write (This, WM8994_Output_Mixer_1, 16#0001#); -- Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) -- path. - I2C_Write (This, 16#2E#, 16#0001#); + I2C_Write (This, WM8994_Output_Mixer_2, 16#0001#); -- Enable Left Output Mixer (MIXOUTL), Enable Right Output Mixer -- (MIXOUTR) idem for SPKOUTL and SPKOUTR. - I2C_Write (This, 16#03#, 16#0030# or 16#0300#); + I2C_Write (This, WM8994_PWR_Management_3, 16#0030# or 16#0300#); -- Enable DC Servo and trigger start-up mode on left and right -- channels. - I2C_Write (This, 16#54#, 16#0033#); + I2C_Write (This, WM8994_DC_Servo1, 16#0033#); -- Add Delay This.Time.Delay_Milliseconds (250); -- Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate and output -- stages. Remove clamps. - I2C_Write (This, 16#60#, 16#00EE#); + I2C_Write (This, WM8994_Analog_HP, 16#00EE#); -- Unmutes -- Unmute DAC 1 (Left) - I2C_Write (This, 16#610#, 16#00C0#); + I2C_Write (This, WM8994_DAC1_Left_Vol, 16#00C0#); -- Unmute DAC 1 (Right) - I2C_Write (This, 16#611#, 16#00C0#); + I2C_Write (This, WM8994_DAC1_Right_Vol, 16#00C0#); -- Unmute the AIF1 Timeslot 0 DAC path - I2C_Write (This, 16#420#, 16#0000#); + I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0000#); -- Unmute DAC 2 (Left) - I2C_Write (This, 16#612#, 16#00C0#); + I2C_Write (This, WM8994_DAC2_Left_Vol, 16#00C0#); -- Unmute DAC 2 (Right) - I2C_Write (This, 16#613#, 16#00C0#); + I2C_Write (This, WM8994_DAC2_Right_Vol, 16#00C0#); -- Unmute the AIF1 Timeslot 1 DAC2 path - I2C_Write (This, 16#422#, 16#0000#); + I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0000#); -- Volume Control This.Set_Volume (Volume); @@ -267,36 +216,36 @@ package body WM8994 is if Input = Microphone then -- Enable Microphone bias 1 generator, Enable VMID Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0013#; - I2C_Write (This, 16#01#, Power_Mgnt_Reg_1); + I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); -- ADC oversample enable - I2C_Write (This, 16#620#, 16#0002#); + I2C_Write (This, WM8994_Oversampling, 16#0002#); -- AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz - I2C_Write (This, 16#411#, 16#3800#); + I2C_Write (This, WM8994_AIF1_ADC2_Filters, 16#3800#); elsif Input = Input_Line then -- Enable normal bias generator, Enable VMID Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0003#; - I2C_Write (This, 16#01#, Power_Mgnt_Reg_1); + I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); -- Disable mute on IN1L, IN1L Volume = +0dB - I2C_Write (This, 16#18#, 16#000B#); + I2C_Write (This, WM8994_Left_Line_In12_Vol, 16#000B#); -- Disable mute on IN1R, IN1R Volume = +0dB - I2C_Write (This, 16#1A#, 16#000B#); + I2C_Write (This, WM8994_Right_Line_In12_Vol, 16#000B#); -- Disable mute on IN1L_TO_MIXINL, Gain = +0dB - I2C_Write (This, 16#29#, 16#0025#); + I2C_Write (This, WM8994_Input_Mixer_3, 16#0025#); -- Disable mute on IN1R_TO_MIXINL, Gain = +0dB - I2C_Write (This, 16#2A#, 16#0025#); + I2C_Write (This, WM8994_Input_Mixer_4, 16#0025#); -- IN1LN_TO_IN1L, IN1LP_TO_VMID, IN1RN_TO_IN1R, IN1RP_TO_VMID - I2C_Write (This, 16#28#, 16#0011#); + I2C_Write (This, WM8994_Input_Mixer_2, 16#0011#); -- AIF ADC1 HPF enable, HPF cut = hifi mode fc=4Hz at fs=48kHz - I2C_Write (This, 16#410#, 16#1800#); + I2C_Write (This, WM8994_AIF1_ADC1_Filters, 16#1800#); end if; -- Volume Control @@ -310,7 +259,7 @@ package body WM8994 is function Chip_ID (This : in out WM8994_Device) return UInt16 is begin - return This.I2C_Read (WM8994_CHIPID_ADDR); + return I2C_Read (This, WM8994_CHIPID_ADDR); end Chip_ID; ---------- @@ -332,7 +281,7 @@ package body WM8994 is This.Set_Mute (Mute_On); -- CODEC in powersave mode - I2C_Write (This, 16#02#, 16#01#); + I2C_Write (This, WM8994_PWR_Management_2, 16#01#); end Pause; ------------ @@ -361,22 +310,22 @@ package body WM8994 is Output_Enabled := False; -- Mute the AIF1 Timeslot 0 DAC1 path - I2C_Write (This, 16#420#, 16#0200#); + I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0200#); -- Mute the AIF1 Timeslot 1 DAC2 path - I2C_Write (This, 16#422#, 16#0200#); + I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0200#); -- Disable DAC1L_TO_HPOUT1L - I2C_Write (This, 16#2D#, 16#0000#); + I2C_Write (This, WM8994_Output_Mixer_1, 16#0000#); -- Disable DAC1R_TO_HPOUT1R - I2C_Write (This, 16#2E#, 16#0000#); + I2C_Write (This, WM8994_Output_Mixer_2, 16#0000#); -- Disable DAC1 and DAC2 - I2C_Write (This, 16#05#, 16#0000#); + I2C_Write (This, WM8994_PWR_Management_5, 16#0000#); - -- Reset Codec by writing in 16#0000 address register - I2C_Write (This, 16#0000#, 16#0000#); + -- Reset Codec + I2C_Write (This, WM8994_SW_Reset, 16#0000#); end if; end Stop; @@ -396,16 +345,16 @@ package body WM8994 is This.Set_Mute (Mute_Off); -- Left Headphone Volume - I2C_Write (This, 16#1C#, Volume or 16#140#); + I2C_Write (This, WM8994_Left_Output_Vol, Volume or 16#140#); -- Right Headphone volume - I2C_Write (This, 16#1D#, Volume or 16#140#); + I2C_Write (This, WM8994_Right_Output_Vol, Volume or 16#140#); -- Left Speaker volume - I2C_Write (This, 16#26#, Volume or 16#140#); + I2C_Write (This, WM8994_SPK_Left_Vol, Volume or 16#140#); -- Right Speaker volume - I2C_Write (This, 16#27#, Volume or 16#140#); + I2C_Write (This, WM8994_SPK_Right_Vol, Volume or 16#140#); end if; end Set_Volume; @@ -419,14 +368,14 @@ package body WM8994 is case Cmd is when Mute_On => -- Soft Mute the AIF1 Timeslot 0 DAC1 path L&R - I2C_Write (This, 16#420#, 16#0200#); + I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0200#); -- Soft Mute the AIF1 Timeslot 1 DAC2 path L&R - I2C_Write (This, 16#422#, 16#0200#); + I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0200#); when Mute_Off => -- Unmute the AIF1 Timeslot 0 DAC1 path L&R - I2C_Write (This, 16#420#, 16#0000#); + I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0000#); -- Unmute the AIF1 Timeslot 1 DAC2 path L&R - I2C_Write (This, 16#422#, 16#0000#); + I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0000#); end case; end if; end Set_Mute; @@ -443,49 +392,49 @@ package body WM8994 is case Device is when No_Output => -- Disable DAC1 (left), DAC1 (Right) - I2C_Write (This, 16#05#, 16#0000#); + I2C_Write (This, WM8994_PWR_Management_5, 16#0000#); -- Mute the AIF1 Timeslot 0 DAC1 path - I2C_Write (This, 16#420#, 16#0200#); + I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0200#); -- Mute the AIF1 Timeslot 1 DAC2 path - I2C_Write (This, 16#422#, 16#0200#); + I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0200#); when Speaker => -- Enable DAC1 (left), DAC1 (Right) - I2C_Write (This, 16#05#, 16#0C0C#); + I2C_Write (This, WM8994_PWR_Management_5, 16#0C0C#); -- Enable the AIF1 Timeslot 0 (Left) to DAC1 (left) mixer path - I2C_Write (This, 16#601#, 16#0000#); + I2C_Write (This, WM8994_AIF1_DAC1_LMR, 16#0000#); -- Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path - I2C_Write (This, 16#602#, 16#0000#); + I2C_Write (This, WM8994_AIF1_DAC1_RMR, 16#0000#); -- Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path - I2C_Write (This, 16#604#, 16#0002#); + I2C_Write (This, WM8994_AIF1_DAC2_LMR, 16#0002#); -- Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path - I2C_Write (This, 16#605#, 16#0002#); + I2C_Write (This, WM8994_AIF1_DAC2_RMR, 16#0002#); when Headphone | Auto => -- Disable DAC1 (left), DAC1 (Right) -- Enable DAC2 (left), DAC2 (Right) - I2C_Write (This, 16#05#, 16#0303#); + I2C_Write (This, WM8994_PWR_Management_5, 16#0303#); -- Enable the AIF1 Timeslot 0 (Left) to DAC1 (left) mixer path - I2C_Write (This, 16#601#, 16#0001#); + I2C_Write (This, WM8994_AIF1_DAC1_LMR, 16#0001#); -- Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path - I2C_Write (This, 16#602#, 16#0001#); + I2C_Write (This, WM8994_AIF1_DAC1_RMR, 16#0001#); -- Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path - I2C_Write (This, 16#604#, 16#0000#); + I2C_Write (This, WM8994_AIF1_DAC2_LMR, 16#0000#); -- Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path - I2C_Write (This, 16#605#, 16#0000#); + I2C_Write (This, WM8994_AIF1_DAC2_RMR, 16#0000#); when Both => -- Enable DAC1 (left), DAC1 (Right) -- Enable DAC2 (left), DAC2 (Right) - I2C_Write (This, 16#05#, 16#0303# or 16#0C0C#); + I2C_Write (This, WM8994_PWR_Management_5, 16#0303# or 16#0C0C#); -- Enable the AIF1 Timeslot 0 (Left) to DAC1 (left) mixer path - I2C_Write (This, 16#601#, 16#0001#); + I2C_Write (This, WM8994_AIF1_DAC1_LMR, 16#0001#); -- Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path - I2C_Write (This, 16#602#, 16#0001#); + I2C_Write (This, WM8994_AIF1_DAC1_RMR, 16#0001#); -- Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path - I2C_Write (This, 16#604#, 16#0002#); + I2C_Write (This, WM8994_AIF1_DAC2_LMR, 16#0002#); -- Enable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path - I2C_Write (This, 16#605#, 16#0002#); + I2C_Write (This, WM8994_AIF1_DAC2_RMR, 16#0002#); end case; end Set_Output_Mode; @@ -508,47 +457,47 @@ package body WM8994 is case Freq is when Audio_Freq_8kHz => -- AIF1 Sample Rate = 8 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0003#); + I2C_Write (This, WM8994_AIF1_Rate, 16#0003#); when Audio_Freq_11kHz => -- AIF1 Sample Rate = 11.025 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0013#); + I2C_Write (This, WM8994_AIF1_Rate, 16#0013#); when Audio_Freq_12kHz => -- AIF1 Sample Rate = 12 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0023#); + I2C_Write (This, WM8994_AIF1_Rate, 16#0023#); when Audio_Freq_16kHz => -- AIF1 Sample Rate = 16 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0033#); + I2C_Write (This, WM8994_AIF1_Rate, 16#0033#); when Audio_Freq_22kHz => -- AIF1 Sample Rate = 22.050 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0043#); + I2C_Write (This, WM8994_AIF1_Rate, 16#0043#); when Audio_Freq_24kHz => -- AIF1 Sample Rate = 24 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0053#); + I2C_Write (This, WM8994_AIF1_Rate, 16#0053#); when Audio_Freq_32kHz => -- AIF1 Sample Rate = 32 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0063#); + I2C_Write (This, WM8994_AIF1_Rate, 16#0063#); when Audio_Freq_44kHz => -- AIF1 Sample Rate = 44.1 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0073#); + I2C_Write (This, WM8994_AIF1_Rate, 16#0073#); when Audio_Freq_48kHz => -- AIF1 Sample Rate = 48 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0083#); + I2C_Write (This, WM8994_AIF1_Rate, 16#0083#); when Audio_Freq_88kHz => -- AIF1 Sample Rate = 88.2 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#0093#); + I2C_Write (This, WM8994_AIF1_Rate, 16#0093#); when Audio_Freq_96kHz => -- AIF1 Sample Rate = 96 (kHz), ratio=256 - I2C_Write (This, 16#210#, 16#00A3#); + I2C_Write (This, WM8994_AIF1_Rate, 16#00A3#); end case; end Set_Frequency; @@ -558,7 +507,7 @@ package body WM8994 is procedure Reset (This : in out WM8994_Device) is begin - I2C_Write (This, 16#0000#, 16#0000#); + I2C_Write (This, WM8994_SW_Reset, 16#0000#); Output_Enabled := False; Input_Enabled := False; end Reset; diff --git a/components/src/audio/W8994/wm8994.ads b/components/src/audio/W8994/wm8994.ads index 1fd3211d1..fdf28d581 100644 --- a/components/src/audio/W8994/wm8994.ads +++ b/components/src/audio/W8994/wm8994.ads @@ -152,12 +152,4 @@ private Time : not null HAL.Time.Any_Delays) is tagged limited null record; - procedure I2C_Write (This : in out WM8994_Device; - Register : UInt16; - Value : UInt16); - - function I2C_Read (This : in out WM8994_Device; - Register : UInt16) - return UInt16; - end WM8994; From c933f7a276d13d1c8e386d6d32113fbe383dc0b7 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Thu, 28 Mar 2024 09:23:43 -0500 Subject: [PATCH 03/22] better name for procedure Play much commenting --- boards/stm32_common/stm32f746disco/audio.adb | 10 ++--- boards/stm32_common/stm32f746disco/audio.ads | 43 ++++++++++++++------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/boards/stm32_common/stm32f746disco/audio.adb b/boards/stm32_common/stm32f746disco/audio.adb index 23f69cfe7..0a85dad15 100644 --- a/boards/stm32_common/stm32f746disco/audio.adb +++ b/boards/stm32_common/stm32f746disco/audio.adb @@ -290,11 +290,11 @@ package body Audio is Clock_Speed => 100_000); end Initialize_Audio_I2C; - ---------- - -- Play -- - ---------- + ------------------- + -- Start_Playing -- + ------------------- - procedure Play + procedure Start_Playing (This : in out WM8994_Audio_Device; Buffer : Audio_Buffer) is @@ -316,7 +316,7 @@ package body Audio is if not Enabled (Audio_SAI, SAI_Out_Block) then Enable (Audio_SAI, SAI_Out_Block); end if; - end Play; + end Start_Playing; ----------- -- Pause -- diff --git a/boards/stm32_common/stm32f746disco/audio.ads b/boards/stm32_common/stm32f746disco/audio.ads index 07f578c59..4ccdf1eb8 100644 --- a/boards/stm32_common/stm32f746disco/audio.ads +++ b/boards/stm32_common/stm32f746disco/audio.ads @@ -44,7 +44,7 @@ package Audio is type WM8994_Audio_Device (Port : not null Any_I2C_Port) is tagged limited private; - type Audio_Output_Device is new WM8994.Output_Device + type Audio_Output_Device is new WM8994.Output_Device range WM8994.Speaker .. WM8994.Auto; -- Only No_Output is not included @@ -57,34 +57,53 @@ package Audio is type Audio_Buffer is array (Natural range <>) of UInt16 with Component_Size => 16, Alignment => 2; -- TODO: change to signed 16-bit, since that's apparently what should be used for PCM samples, - -- so revert the change to HAL.Audio.Audio_Buffer (so it is INteger_16 again) and use that. + -- so revert the change to HAL.Audio.Audio_Buffer (so it is Integer_16 again) and use that. procedure Initialize (This : in out WM8994_Audio_Device; Volume : Audio_Volume; Frequency : Audio_Frequency; Sink : Audio_Output_Device); + -- This routine initializes the hardware and configures the volume, + -- sampling frequency, and output device (the sink). This routine must be + -- called, before any others. The routines for setting the volume and + -- the output frequency are optional. - procedure Set_Volume - (This : in out WM8994_Audio_Device; - Volume : Audio_Volume); - - procedure Set_Frequency - (This : in out WM8994_Audio_Device; - Frequency : Audio_Frequency); - - procedure Play + procedure Start_Playing (This : in out WM8994_Audio_Device; Buffer : Audio_Buffer); + -- Start playing, for the first time, content from the audio file/stream. + -- This routine must be called, perhaps just once but more than once if the + -- other routines below are called. The effect is to tell the underlying + -- WM8994 codec where the buffer to be played is located, and cause the + -- codec to start playing that buffer. Playing continues after the call + -- returns. An additional mechanism, outside this package, updates the + -- content of the buffer while the codec is playing it. That update/play + -- process continues until either there is no more music to be played, or + -- Stop or Pause is called. procedure Pause (This : in out WM8994_Audio_Device); + -- After calling Pause, only Resume should be called for resuming play (do + -- not call Start_Playing again). procedure Resume (This : in out WM8994_Audio_Device); + -- Procedure Resume should be called only when the audio is playing or + -- paused (not stopped). procedure Stop (This : in out WM8994_Audio_Device); + -- Stops the hardware and update/play process. Once called, you must call + -- Start_Playing again if you want to restart the output. + + procedure Set_Volume + (This : in out WM8994_Audio_Device; + Volume : Audio_Volume); + + procedure Set_Frequency + (This : in out WM8994_Audio_Device; + Frequency : Audio_Frequency); private @@ -94,7 +113,7 @@ private (Port : not null Any_I2C_Port) is tagged limited record Device : WM8994.WM8994_Device (Port, Audio_I2C_Addr, Ravenscar_Time.Delays); - Sink : Audio_Output_Device; + Sink : Audio_Output_Device := No_Output; end record; end Audio; From 570166822d05339a6c360b288bc8634d44e059e7 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Thu, 28 Mar 2024 10:00:56 -0500 Subject: [PATCH 04/22] Move to signed 16-bit PCM samples, rather than unsigned --- boards/stm32_common/stm32f746disco/audio.ads | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/boards/stm32_common/stm32f746disco/audio.ads b/boards/stm32_common/stm32f746disco/audio.ads index 4ccdf1eb8..5b8dd2ab3 100644 --- a/boards/stm32_common/stm32f746disco/audio.ads +++ b/boards/stm32_common/stm32f746disco/audio.ads @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2016, AdaCore -- +-- Copyright (C) 2016-2024, AdaCore -- -- -- -- Redistribution and use in source and binary forms, with or without -- -- modification, are permitted provided that the following conditions are -- @@ -32,10 +32,10 @@ -- @author MCD Application Team -- ------------------------------------------------------------------------------ --- with HAL.Audio; use HAL.Audio; -with HAL; use HAL; +with HAL; use HAL; with HAL.I2C; use HAL.I2C; with Ravenscar_Time; +with Interfaces; use Interfaces; with WM8994; @@ -49,15 +49,11 @@ package Audio is -- Only No_Output is not included type Audio_Frequency is new WM8994.Audio_Frequency; - -- TODO: use HAL.Audio package's type, with WM8994 additions to that type type Audio_Volume is range 0 .. 100; -- a percentage - -- TODO: use HAL.Audio package's type - type Audio_Buffer is array (Natural range <>) of UInt16 + type Audio_Buffer is array (Natural range <>) of Integer_16 with Component_Size => 16, Alignment => 2; - -- TODO: change to signed 16-bit, since that's apparently what should be used for PCM samples, - -- so revert the change to HAL.Audio.Audio_Buffer (so it is Integer_16 again) and use that. procedure Initialize (This : in out WM8994_Audio_Device; @@ -113,7 +109,7 @@ private (Port : not null Any_I2C_Port) is tagged limited record Device : WM8994.WM8994_Device (Port, Audio_I2C_Addr, Ravenscar_Time.Delays); - Sink : Audio_Output_Device := No_Output; + Sink : Audio_Output_Device; -- := No_Output; -- TODO... end record; end Audio; From 69ca86ed47d370ec962c5386f626a6f83c73dd02 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Thu, 28 Mar 2024 10:45:19 -0500 Subject: [PATCH 05/22] Add important comments re: mystery code and update copyright year * Document the fact (in comments) that the "Errata work-arounds" are completely mysterious. * update copyright year * Add comment for entire unit at top --- components/src/audio/W8994/wm8994.adb | 7 +++++-- components/src/audio/W8994/wm8994.ads | 22 ++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/components/src/audio/W8994/wm8994.adb b/components/src/audio/W8994/wm8994.adb index 6c33279bb..8d2c1fdd8 100644 --- a/components/src/audio/W8994/wm8994.adb +++ b/components/src/audio/W8994/wm8994.adb @@ -51,8 +51,11 @@ package body WM8994 is is Power_Mgnt_Reg_1 : UInt16 := 0; begin - -- WM8994 Errata work-arounds. - -- See https://github.com/STMicroelectronics/stm32-wm8994/blob/main/wm8994.c + -- WM8994 Errata work-arounds. See + -- https://github.com/STMicroelectronics/stm32-wm8994/blob/main/wm8994.c + -- These registers are not documented, and the effects of these writes + -- are not documented anywhere either. Nobody seems to have written it + -- down anywhere. I2C_Write (This, 16#102#, 16#0003#); I2C_Write (This, 16#817#, 16#0000#); I2C_Write (This, 16#102#, 16#0000#); diff --git a/components/src/audio/W8994/wm8994.ads b/components/src/audio/W8994/wm8994.ads index fdf28d581..eaa7a8a9f 100644 --- a/components/src/audio/W8994/wm8994.ads +++ b/components/src/audio/W8994/wm8994.ads @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015-2016, AdaCore -- +-- Copyright (C) 2015-2024, AdaCore -- -- -- -- Redistribution and use in source and binary forms, with or without -- -- modification, are permitted provided that the following conditions are -- @@ -29,7 +29,8 @@ -- -- ------------------------------------------------------------------------------ --- Driver for the WM8994 CODEC +-- This package provides a simple driver for the WM8994 CODEC. It does not +-- provide a full definition of the WM8994's functionality. with HAL; use HAL; with HAL.I2C; use HAL.I2C; @@ -87,23 +88,20 @@ package WM8994 is Audio_Freq_88kHz => 88_200, Audio_Freq_96kHz => 96_000); - -- TODO: support 24K and 32K frequencies - type Mute is (Mute_On, Mute_Off); type Stop_Mode is (Stop_Power_Down_Sw, + -- Stop_Power_Down_Sw only mutes the audio codec, it does not alter + -- hardware settings. When resuming from this mode the codec keeps the + -- previous initialization so there is no need to re-initialize the + -- codec registers. Stop_Power_Down_Hw); - -- Stop_Power_Down_Sw: - -- only mutes the audio codec. When resuming from this mode the codec - -- keeps the previous initialization (no need to re-Initialize the codec - -- registers). - -- Stop_Power_Down_Hw: - -- Physically power down the codec. When resuming from this mode, the codec - -- is set to default configuration (user should re-Initialize the codec in - -- order to play again the audio stream). + -- Stop_Power_Down_Hw physically powers down the codec hardware. When + -- resuming from this mode, the codec is set to default configuration + -- so users should re-initialize the codec. Max_Volume : constant := 16#3F#; From 2e6117e8b350859bef846d14e822643a9dee5af1 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Thu, 28 Mar 2024 16:49:33 -0500 Subject: [PATCH 06/22] Make the output device type use a static predicate to limit the options Otherwise, as a range constraint, the result would be order-dependent on the parent type. Now use No_Output for the default value of the Output record component, to check for lack of a prior call to Initialize. --- boards/stm32_common/stm32f746disco/audio.adb | 13 +++++--- boards/stm32_common/stm32f746disco/audio.ads | 32 ++++++++++++++------ 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/boards/stm32_common/stm32f746disco/audio.adb b/boards/stm32_common/stm32f746disco/audio.adb index 0a85dad15..75d2d4ffd 100644 --- a/boards/stm32_common/stm32f746disco/audio.adb +++ b/boards/stm32_common/stm32f746disco/audio.adb @@ -84,11 +84,9 @@ package body Audio is Set_Audio_Clock (Frequency); - -- Initialize the SAI Initialize_Audio_Out_Pins; Initialize_Audio_DMA; Initialize_SAI_Out (Frequency, Sink); - Initialize_Audio_I2C; if This.Device.Chip_ID /= WM8994.WM8994_ID then @@ -102,7 +100,7 @@ package body Audio is Volume => As_Device_Volume (Volume), Frequency => WM8994.Audio_Frequency (Frequency)); - This.Sink := Sink; + This.Output := WM8994.Output_Device (Sink); -- For sake of any individual calls to Set_Frequency, assuming the STM -- code is correct that we also need to set the clocks when setting the -- frequency @@ -128,7 +126,12 @@ package body Audio is (This : in out WM8994_Audio_Device; Frequency : Audio_Frequency) is + use type WM8994.Output_Device; begin + if This.Output = WM8994.No_Output then + raise Constraint_Error with "No prior call to Initialize"; + end if; + -- The following is per the example source file from STM, -- STM32746G-Disco_example\board_drivers\stm32746g_discovery_audio.c, -- whereas the WM8894 driver just has a specific function that writes @@ -136,7 +139,7 @@ package body Audio is -- clock configuration too. Set_Audio_Clock (Frequency); STM32.SAI.Disable (Audio_SAI, SAI_Out_Block); - Initialize_SAI_Out (Frequency, This.Sink); + Initialize_SAI_Out (Frequency, Audio_Output_Device (This.Output)); STM32.SAI.Enable (Audio_SAI, SAI_Out_Block); end Set_Frequency; @@ -257,7 +260,7 @@ package body Audio is FS_Offset => Before_First_Bit); case Sink is - when Headphone | Auto => + when Headphone => Active_Slots := Slot_0 or Slot_2; when Speaker => Active_Slots := Slot_1 or Slot_3; diff --git a/boards/stm32_common/stm32f746disco/audio.ads b/boards/stm32_common/stm32f746disco/audio.ads index 5b8dd2ab3..2054e5d39 100644 --- a/boards/stm32_common/stm32f746disco/audio.ads +++ b/boards/stm32_common/stm32f746disco/audio.ads @@ -44,9 +44,9 @@ package Audio is type WM8994_Audio_Device (Port : not null Any_I2C_Port) is tagged limited private; - type Audio_Output_Device is new WM8994.Output_Device - range WM8994.Speaker .. WM8994.Auto; - -- Only No_Output is not included + type Audio_Output_Device is new WM8994.Output_Device with + Static_Predicate => Audio_Output_Device in + Headphone | Speaker | Both; type Audio_Frequency is new WM8994.Audio_Frequency; @@ -68,15 +68,16 @@ package Audio is procedure Start_Playing (This : in out WM8994_Audio_Device; Buffer : Audio_Buffer); - -- Start playing, for the first time, content from the audio file/stream. + -- Start playing, for the first time, content from the specified buffer. -- This routine must be called, perhaps just once but more than once if the -- other routines below are called. The effect is to tell the underlying -- WM8994 codec where the buffer to be played is located, and cause the - -- codec to start playing that buffer. Playing continues after the call - -- returns. An additional mechanism, outside this package, updates the - -- content of the buffer while the codec is playing it. That update/play - -- process continues until either there is no more music to be played, or - -- Stop or Pause is called. + -- codec to start playing the contents. + -- + -- NB: playing continues after the call returns. An additional mechanism, + -- outside this package, updates the content of the buffer while the codec + -- is playing it. That update/play process continues until either there is + -- no more music to be played, or Stop or Pause is called. procedure Pause (This : in out WM8994_Audio_Device); @@ -109,7 +110,18 @@ private (Port : not null Any_I2C_Port) is tagged limited record Device : WM8994.WM8994_Device (Port, Audio_I2C_Addr, Ravenscar_Time.Delays); - Sink : Audio_Output_Device; -- := No_Output; -- TODO... + Output : WM8994.Output_Device := WM8994.No_Output; + -- The initial value of Output is overwritten by Initialize. The value + -- No_Output will trigger a C_E if ever referenced, so it is used as a + -- check that Initialize has been called. We need the component Output + -- itself for the sake of a clean parameter profile for Set_Frequency, + -- otherwise clients would have to pass another parameter to specify + -- the output device selection again (after having done so when calling + -- Initialize). That's because Set_Frequency needs to do enough hardware + -- re-initialization to accommodate the new frequency, but doing so + -- requires the output device selection so that the re-init can select + -- the active slots. Just activating all slots (as is done in the STM + -- C code) doesn't work (at least in the current Ada code). end record; end Audio; From 4caa5958b5c411767d3645ee6c2b24d079dc07d8 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Sat, 21 Mar 2026 16:00:00 -0500 Subject: [PATCH 07/22] Change type names to match WM8994 datasheet. * Change ADT name to Audio_CODEC for closer match to documentation and meaningfulness * For the same reasons, change name Output_Device to Analog_Outputs. * Minor reordering to put the primary ADT declaration near the top of the package. --- components/src/audio/W8994/wm8994-io.adb | 35 ++++++++++++- components/src/audio/W8994/wm8994-io.ads | 35 ++++++++++++- components/src/audio/W8994/wm8994.adb | 29 +++++----- components/src/audio/W8994/wm8994.ads | 67 +++++++++++------------- 4 files changed, 113 insertions(+), 53 deletions(-) diff --git a/components/src/audio/W8994/wm8994-io.adb b/components/src/audio/W8994/wm8994-io.adb index df4a8b9cf..0b4d8953f 100644 --- a/components/src/audio/W8994/wm8994-io.adb +++ b/components/src/audio/W8994/wm8994-io.adb @@ -1,3 +1,34 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2026, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + package body WM8994.IO is --------------- @@ -5,7 +36,7 @@ package body WM8994.IO is --------------- procedure I2C_Write - (This : in out WM8994_Device; + (This : in out Audio_CODEC; Register : Register_Address; Value : UInt16) is @@ -34,7 +65,7 @@ package body WM8994.IO is -------------- function I2C_Read - (This : in out WM8994_Device; + (This : in out Audio_CODEC; Register : Register_Address) return UInt16 is diff --git a/components/src/audio/W8994/wm8994-io.ads b/components/src/audio/W8994/wm8994-io.ads index b9093fc11..5901cd273 100644 --- a/components/src/audio/W8994/wm8994-io.ads +++ b/components/src/audio/W8994/wm8994-io.ads @@ -1,14 +1,45 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2026, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + package WM8994.IO is type Register_Address is new UInt16; procedure I2C_Write - (This : in out WM8994_Device; + (This : in out Audio_CODEC; Register : Register_Address; Value : UInt16); function I2C_Read - (This : in out WM8994_Device; + (This : in out Audio_CODEC; Register : Register_Address) return UInt16; diff --git a/components/src/audio/W8994/wm8994.adb b/components/src/audio/W8994/wm8994.adb index 8d2c1fdd8..12acfd7d0 100644 --- a/components/src/audio/W8994/wm8994.adb +++ b/components/src/audio/W8994/wm8994.adb @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015-2024, AdaCore -- +-- Copyright (C) 2015-2026, AdaCore -- -- -- -- Redistribution and use in source and binary forms, with or without -- -- modification, are permitted provided that the following conditions are -- @@ -28,6 +28,7 @@ -- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- -- -- ------------------------------------------------------------------------------ + with WM8994.IO; use WM8994.IO; package body WM8994 is @@ -43,9 +44,9 @@ package body WM8994 is ---------------- procedure Initialize - (This : in out WM8994_Device; + (This : in out Audio_CODEC; Input : Input_Device; - Output : Output_Device; + Output : Analog_Outputs; Volume : Volume_Level; Frequency : Audio_Frequency) is @@ -260,7 +261,7 @@ package body WM8994 is -- Chip_ID -- ------------- - function Chip_ID (This : in out WM8994_Device) return UInt16 is + function Chip_ID (This : in out Audio_CODEC) return UInt16 is begin return I2C_Read (This, WM8994_CHIPID_ADDR); end Chip_ID; @@ -269,7 +270,7 @@ package body WM8994 is -- Play -- ---------- - procedure Play (This : in out WM8994_Device) is + procedure Play (This : in out Audio_CODEC) is begin This.Set_Mute (Mute_Off); end Play; @@ -278,7 +279,7 @@ package body WM8994 is -- Pause -- ----------- - procedure Pause (This : in out WM8994_Device) is + procedure Pause (This : in out Audio_CODEC) is begin -- Pause the audio playing This.Set_Mute (Mute_On); @@ -291,7 +292,7 @@ package body WM8994 is -- Resume -- ------------ - procedure Resume (This : in out WM8994_Device) is + procedure Resume (This : in out Audio_CODEC) is begin This.Set_Mute (Mute_Off); end Resume; @@ -300,7 +301,7 @@ package body WM8994 is -- Stop -- ---------- - procedure Stop (This : in out WM8994_Device; Cmd : Stop_Mode) is + procedure Stop (This : in out Audio_CODEC; Cmd : Stop_Mode) is begin if Output_Enabled then -- Mute the output first @@ -337,7 +338,7 @@ package body WM8994 is ---------------- procedure Set_Volume - (This : in out WM8994_Device; + (This : in out Audio_CODEC; Volume : Volume_Level) is begin @@ -365,7 +366,7 @@ package body WM8994 is -- Set_Mute -- -------------- - procedure Set_Mute (This : in out WM8994_Device; Cmd : Mute) is + procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Modes) is begin if Output_Enabled then case Cmd is @@ -388,8 +389,8 @@ package body WM8994 is --------------------- procedure Set_Output_Mode - (This : in out WM8994_Device; - Device : Output_Device) + (This : in out Audio_CODEC; + Device : Analog_Outputs) is begin case Device is @@ -446,7 +447,7 @@ package body WM8994 is ------------------- procedure Set_Frequency - (This : in out WM8994_Device; + (This : in out Audio_CODEC; Freq : Audio_Frequency) is begin @@ -508,7 +509,7 @@ package body WM8994 is -- Reset -- ----------- - procedure Reset (This : in out WM8994_Device) is + procedure Reset (This : in out Audio_CODEC) is begin I2C_Write (This, WM8994_SW_Reset, 16#0000#); Output_Enabled := False; diff --git a/components/src/audio/W8994/wm8994.ads b/components/src/audio/W8994/wm8994.ads index eaa7a8a9f..37bc3c6b0 100644 --- a/components/src/audio/W8994/wm8994.ads +++ b/components/src/audio/W8994/wm8994.ads @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015-2024, AdaCore -- +-- Copyright (C) 2015-2026, AdaCore -- -- -- -- Redistribution and use in source and binary forms, with or without -- -- modification, are permitted provided that the following conditions are -- @@ -38,10 +38,13 @@ with HAL.Time; package WM8994 is - -- TODO: see page 144 Datasheet WM8994_Rev4.6, from Cirrus Logic regarding - -- "For suppression of pop noise ..." + type Audio_CODEC + (Port : not null Any_I2C_Port; + I2C_Addr : UInt10; + Time : not null HAL.Time.Any_Delays) + is tagged limited private; - type Output_Device is + type Analog_Outputs is (No_Output, Speaker, Headphone, @@ -53,8 +56,6 @@ package WM8994 is Microphone, Input_Line); - WM8994_ID : constant := 16#8994#; - type Audio_Frequency is (Audio_Freq_8kHz, Audio_Freq_11kHz, @@ -88,7 +89,18 @@ package WM8994 is Audio_Freq_88kHz => 88_200, Audio_Freq_96kHz => 96_000); - type Mute is + Max_Volume : constant := 16#3F#; + + subtype Volume_Level is UInt16 range 0 .. Max_Volume; + + procedure Initialize + (This : in out Audio_CODEC; + Input : Input_Device; + Output : Analog_Outputs; + Volume : Volume_Level; + Frequency : Audio_Frequency); + + type Mute_Modes is (Mute_On, Mute_Off); @@ -103,48 +115,33 @@ package WM8994 is -- resuming from this mode, the codec is set to default configuration -- so users should re-initialize the codec. - Max_Volume : constant := 16#3F#; - - subtype Volume_Level is UInt16 range 0 .. Max_Volume; - - type WM8994_Device - (Port : not null Any_I2C_Port; - I2C_Addr : UInt10; - Time : not null HAL.Time.Any_Delays) - is tagged limited private; - - procedure Initialize - (This : in out WM8994_Device; - Input : Input_Device; - Output : Output_Device; - Volume : Volume_Level; - Frequency : Audio_Frequency); + WM8994_ID : constant := 16#8994#; - function Chip_ID (This : in out WM8994_Device) return UInt16; + function Chip_ID (This : in out Audio_CODEC) return UInt16; - procedure Play (This : in out WM8994_Device); + procedure Play (This : in out Audio_CODEC); - procedure Pause (This : in out WM8994_Device); + procedure Pause (This : in out Audio_CODEC); - procedure Resume (This : in out WM8994_Device); + procedure Resume (This : in out Audio_CODEC); - procedure Stop (This : in out WM8994_Device; Cmd : Stop_Mode); + procedure Stop (This : in out Audio_CODEC; Cmd : Stop_Mode); - procedure Set_Volume (This : in out WM8994_Device; Volume : Volume_Level); + procedure Set_Volume (This : in out Audio_CODEC; Volume : Volume_Level); - procedure Set_Mute (This : in out WM8994_Device; Cmd : Mute); + procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Modes); - procedure Set_Output_Mode (This : in out WM8994_Device; - Device : Output_Device); + procedure Set_Output_Mode (This : in out Audio_CODEC; + Device : Analog_Outputs); - procedure Set_Frequency (This : in out WM8994_Device; + procedure Set_Frequency (This : in out Audio_CODEC; Freq : Audio_Frequency); - procedure Reset (This : in out WM8994_Device); + procedure Reset (This : in out Audio_CODEC); private - type WM8994_Device + type Audio_CODEC (Port : not null Any_I2C_Port; I2C_Addr : UInt10; Time : not null HAL.Time.Any_Delays) From 473f1d489bf6aca98b78a38f72c34388f9d180dc Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Mon, 23 Mar 2026 15:06:02 -0500 Subject: [PATCH 08/22] use new name for WM8994_Audio_Device --- boards/stm32_common/stm32f746disco/audio.adb | 36 +++++++++---------- boards/stm32_common/stm32f746disco/audio.ads | 34 +++++++++--------- .../stm32f746disco/stm32-board.ads | 2 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/boards/stm32_common/stm32f746disco/audio.adb b/boards/stm32_common/stm32f746disco/audio.adb index 75d2d4ffd..e3ae6bd9d 100644 --- a/boards/stm32_common/stm32f746disco/audio.adb +++ b/boards/stm32_common/stm32f746disco/audio.adb @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2016, AdaCore -- +-- Copyright (C) 2016-2026, AdaCore -- -- -- -- Redistribution and use in source and binary forms, with or without -- -- modification, are permitted provided that the following conditions are -- @@ -61,7 +61,7 @@ package body Audio is procedure Initialize_Audio_DMA; -- Configure the DMA channel to the SAI peripheral - procedure Initialize_SAI_Out (Freq : Audio_Frequency; Sink : Audio_Output_Device); + procedure Initialize_SAI_Out (Freq : Audio_Frequency; Sink : Audio_Outputs); procedure Initialize_Audio_I2C; -- Initialize the I2C Port to send commands to the driver @@ -74,10 +74,10 @@ package body Audio is ---------------- procedure Initialize - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Volume : Audio_Volume; Frequency : Audio_Frequency; - Sink : Audio_Output_Device) + Sink : Audio_Outputs) is begin STM32.SAI.Deinitialize (Audio_SAI, SAI_Out_Block); @@ -96,11 +96,11 @@ package body Audio is This.Device.Reset; This.Device.Initialize (Input => WM8994.No_Input, - Output => WM8994.Output_Device (Sink), + Output => WM8994.Analog_Outputs (Sink), Volume => As_Device_Volume (Volume), Frequency => WM8994.Audio_Frequency (Frequency)); - This.Output := WM8994.Output_Device (Sink); + This.Sink := WM8994.Analog_Outputs (Sink); -- For sake of any individual calls to Set_Frequency, assuming the STM -- code is correct that we also need to set the clocks when setting the -- frequency @@ -111,7 +111,7 @@ package body Audio is ---------------- procedure Set_Volume - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Volume : Audio_Volume) is begin @@ -123,12 +123,12 @@ package body Audio is ------------------- procedure Set_Frequency - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Frequency : Audio_Frequency) is - use type WM8994.Output_Device; + use type WM8994.Analog_Outputs; begin - if This.Output = WM8994.No_Output then + if This.Sink = WM8994.No_Output then raise Constraint_Error with "No prior call to Initialize"; end if; @@ -139,7 +139,7 @@ package body Audio is -- clock configuration too. Set_Audio_Clock (Frequency); STM32.SAI.Disable (Audio_SAI, SAI_Out_Block); - Initialize_SAI_Out (Frequency, Audio_Output_Device (This.Output)); + Initialize_SAI_Out (Frequency, Audio_Outputs (This.Sink)); STM32.SAI.Enable (Audio_SAI, SAI_Out_Block); end Set_Frequency; @@ -161,7 +161,7 @@ package body Audio is -- HSE/PLLM = 1MHz = PLLI2S VCO Input Configure_SAI_I2S_Clock (Audio_SAI, - PLLI2SN => 429, -- VCO Output = 429MHz + PLLI2SN => 429, -- VCO Sink = 429MHz PLLI2SQ => 2, -- SAI Clk(First level) = 214.5 MHz PLLI2SDIVQ => 19); -- I2S Clk = 215.4 / 19 = 11.289 MHz @@ -169,7 +169,7 @@ package body Audio is Audio_Freq_48kHz | Audio_Freq_96kHz => Configure_SAI_I2S_Clock (Audio_SAI, - PLLI2SN => 344, -- VCO Output = 344MHz + PLLI2SN => 344, -- VCO Sink = 344MHz PLLI2SQ => 7, -- SAI Clk(First level) = 49.142 MHz PLLI2SDIVQ => 1); -- I2S Clk = 49.142 MHz @@ -229,7 +229,7 @@ package body Audio is procedure Initialize_SAI_Out (Freq : Audio_Frequency; - Sink : Audio_Output_Device) + Sink : Audio_Outputs) is Active_Slots : SAI_Slots; begin @@ -298,7 +298,7 @@ package body Audio is ------------------- procedure Start_Playing - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Buffer : Audio_Buffer) is begin @@ -325,7 +325,7 @@ package body Audio is -- Pause -- ----------- - procedure Pause (This : in out WM8994_Audio_Device) is + procedure Pause (This : in out WM8994_Audio_CODEC) is begin This.Device.Pause; DMA_Pause (Audio_SAI, SAI_Out_Block); @@ -335,7 +335,7 @@ package body Audio is -- Resume -- ------------ - procedure Resume (This : in out WM8994_Audio_Device) is + procedure Resume (This : in out WM8994_Audio_CODEC) is begin This.Device.Resume; DMA_Resume (Audio_SAI, SAI_Out_Block); @@ -345,7 +345,7 @@ package body Audio is -- Stop -- ---------- - procedure Stop (This : in out WM8994_Audio_Device) is + procedure Stop (This : in out WM8994_Audio_CODEC) is begin This.Device.Stop (WM8994.Stop_Power_Down_Sw); DMA_Stop (Audio_SAI, SAI_Out_Block); diff --git a/boards/stm32_common/stm32f746disco/audio.ads b/boards/stm32_common/stm32f746disco/audio.ads index 2054e5d39..593110e3d 100644 --- a/boards/stm32_common/stm32f746disco/audio.ads +++ b/boards/stm32_common/stm32f746disco/audio.ads @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2016-2024, AdaCore -- +-- Copyright (C) 2016-2026, AdaCore -- -- -- -- Redistribution and use in source and binary forms, with or without -- -- modification, are permitted provided that the following conditions are -- @@ -41,11 +41,11 @@ with WM8994; package Audio is - type WM8994_Audio_Device (Port : not null Any_I2C_Port) is + type WM8994_Audio_CODEC (Port : not null Any_I2C_Port) is tagged limited private; - type Audio_Output_Device is new WM8994.Output_Device with - Static_Predicate => Audio_Output_Device in + type Audio_Outputs is new WM8994.Analog_Outputs with + Static_Predicate => Audio_Outputs in Headphone | Speaker | Both; type Audio_Frequency is new WM8994.Audio_Frequency; @@ -56,17 +56,17 @@ package Audio is with Component_Size => 16, Alignment => 2; procedure Initialize - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Volume : Audio_Volume; Frequency : Audio_Frequency; - Sink : Audio_Output_Device); + Sink : Audio_Outputs); -- This routine initializes the hardware and configures the volume, -- sampling frequency, and output device (the sink). This routine must be -- called, before any others. The routines for setting the volume and -- the output frequency are optional. procedure Start_Playing - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Buffer : Audio_Buffer); -- Start playing, for the first time, content from the specified buffer. -- This routine must be called, perhaps just once but more than once if the @@ -80,40 +80,40 @@ package Audio is -- no more music to be played, or Stop or Pause is called. procedure Pause - (This : in out WM8994_Audio_Device); + (This : in out WM8994_Audio_CODEC); -- After calling Pause, only Resume should be called for resuming play (do -- not call Start_Playing again). procedure Resume - (This : in out WM8994_Audio_Device); + (This : in out WM8994_Audio_CODEC); -- Procedure Resume should be called only when the audio is playing or -- paused (not stopped). procedure Stop - (This : in out WM8994_Audio_Device); + (This : in out WM8994_Audio_CODEC); -- Stops the hardware and update/play process. Once called, you must call -- Start_Playing again if you want to restart the output. procedure Set_Volume - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Volume : Audio_Volume); procedure Set_Frequency - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Frequency : Audio_Frequency); private Audio_I2C_Addr : constant I2C_Address := 16#34#; - type WM8994_Audio_Device + type WM8994_Audio_CODEC (Port : not null Any_I2C_Port) is tagged limited record - Device : WM8994.WM8994_Device (Port, Audio_I2C_Addr, Ravenscar_Time.Delays); - Output : WM8994.Output_Device := WM8994.No_Output; - -- The initial value of Output is overwritten by Initialize. The value + Device : WM8994.Audio_CODEC (Port, Audio_I2C_Addr, Ravenscar_Time.Delays); + Sink : WM8994.Analog_Outputs := WM8994.No_Output; + -- The initial value of Sink is overwritten by Initialize. The value -- No_Output will trigger a C_E if ever referenced, so it is used as a - -- check that Initialize has been called. We need the component Output + -- check that Initialize has been called. We need the component Sink -- itself for the sake of a clean parameter profile for Set_Frequency, -- otherwise clients would have to pass another parameter to specify -- the output device selection again (after having done so when calling diff --git a/boards/stm32_common/stm32f746disco/stm32-board.ads b/boards/stm32_common/stm32f746disco/stm32-board.ads index c74527d0e..b1c61221b 100644 --- a/boards/stm32_common/stm32f746disco/stm32-board.ads +++ b/boards/stm32_common/stm32f746disco/stm32-board.ads @@ -160,7 +160,7 @@ package STM32.Board is Audio_DMA_Out_Stream : DMA_Stream_Selector renames Stream_4; Audio_DMA_Out_Channel : DMA_Channel_Selector renames Channel_3; - Audio_Device : aliased Audio.WM8994_Audio_Device (Audio_I2C'Access); + Audio_Device : aliased Audio.WM8994_Audio_CODEC (Audio_I2C'Access); -------------------------- -- micro SD card reader -- From 9ad19bde3190d92db5abfc563a597e42285e8466 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Mon, 23 Mar 2026 13:48:59 -0500 Subject: [PATCH 09/22] change procedure name Start_Playing back to Play correct letter casing fix words changed in comments due to refactoring --- boards/stm32_common/stm32f746disco/audio.adb | 10 +++++----- boards/stm32_common/stm32f746disco/audio.ads | 12 +++++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/boards/stm32_common/stm32f746disco/audio.adb b/boards/stm32_common/stm32f746disco/audio.adb index e3ae6bd9d..cefb2a307 100644 --- a/boards/stm32_common/stm32f746disco/audio.adb +++ b/boards/stm32_common/stm32f746disco/audio.adb @@ -161,7 +161,7 @@ package body Audio is -- HSE/PLLM = 1MHz = PLLI2S VCO Input Configure_SAI_I2S_Clock (Audio_SAI, - PLLI2SN => 429, -- VCO Sink = 429MHz + PLLI2SN => 429, -- VCO Output = 429MHz PLLI2SQ => 2, -- SAI Clk(First level) = 214.5 MHz PLLI2SDIVQ => 19); -- I2S Clk = 215.4 / 19 = 11.289 MHz @@ -169,11 +169,11 @@ package body Audio is Audio_Freq_48kHz | Audio_Freq_96kHz => Configure_SAI_I2S_Clock (Audio_SAI, - PLLI2SN => 344, -- VCO Sink = 344MHz + PLLI2SN => 344, -- VCO Output = 344MHz PLLI2SQ => 7, -- SAI Clk(First level) = 49.142 MHz PLLI2SDIVQ => 1); -- I2S Clk = 49.142 MHz - when Audio_Freq_12khz | Audio_Freq_24khz | Audio_Freq_88khz => + when Audio_Freq_12kHz | Audio_Freq_24kHz | Audio_Freq_88kHz => raise Program_Error with "freq not yet implemented"; -- FIXME! end case; @@ -297,7 +297,7 @@ package body Audio is -- Start_Playing -- ------------------- - procedure Start_Playing + procedure Play (This : in out WM8994_Audio_CODEC; Buffer : Audio_Buffer) is @@ -319,7 +319,7 @@ package body Audio is if not Enabled (Audio_SAI, SAI_Out_Block) then Enable (Audio_SAI, SAI_Out_Block); end if; - end Start_Playing; + end Play; ----------- -- Pause -- diff --git a/boards/stm32_common/stm32f746disco/audio.ads b/boards/stm32_common/stm32f746disco/audio.ads index 593110e3d..12342b09a 100644 --- a/boards/stm32_common/stm32f746disco/audio.ads +++ b/boards/stm32_common/stm32f746disco/audio.ads @@ -65,17 +65,15 @@ package Audio is -- called, before any others. The routines for setting the volume and -- the output frequency are optional. - procedure Start_Playing + procedure Play (This : in out WM8994_Audio_CODEC; Buffer : Audio_Buffer); - -- Start playing, for the first time, content from the specified buffer. - -- This routine must be called, perhaps just once but more than once if the - -- other routines below are called. The effect is to tell the underlying - -- WM8994 codec where the buffer to be played is located, and cause the - -- codec to start playing the contents. + -- Start playing content from the specified buffer. The effect is to tell + -- the underlying WM8994 CODEC where the buffer to be played is located, + -- and cause the CODEC to start playing the contents. -- -- NB: playing continues after the call returns. An additional mechanism, - -- outside this package, updates the content of the buffer while the codec + -- outside this package, updates the content of the buffer while the CODEC -- is playing it. That update/play process continues until either there is -- no more music to be played, or Stop or Pause is called. From 0bb0d7f445f2fe60074bf2f3fb051f532f9e80a2 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Tue, 24 Mar 2026 10:34:22 -0500 Subject: [PATCH 10/22] use a "private with" for Ravenscar_Time --- boards/stm32_common/stm32f746disco/audio.ads | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/boards/stm32_common/stm32f746disco/audio.ads b/boards/stm32_common/stm32f746disco/audio.ads index 12342b09a..d56ed81c7 100644 --- a/boards/stm32_common/stm32f746disco/audio.ads +++ b/boards/stm32_common/stm32f746disco/audio.ads @@ -32,13 +32,13 @@ -- @author MCD Application Team -- ------------------------------------------------------------------------------ -with HAL; use HAL; -with HAL.I2C; use HAL.I2C; -with Ravenscar_Time; +with HAL; use HAL; +with HAL.I2C; use HAL.I2C; with Interfaces; use Interfaces; - with WM8994; +private with Ravenscar_Time; + package Audio is type WM8994_Audio_CODEC (Port : not null Any_I2C_Port) is From 582ce1633f82a7e556db28befdd17abb3e2acbb1 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Fri, 8 May 2026 18:45:32 -0500 Subject: [PATCH 11/22] Minor name refactoring for consistency within the package --- components/src/audio/W8994/wm8994.adb | 6 +++--- components/src/audio/W8994/wm8994.ads | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components/src/audio/W8994/wm8994.adb b/components/src/audio/W8994/wm8994.adb index 12acfd7d0..0dee4e31a 100644 --- a/components/src/audio/W8994/wm8994.adb +++ b/components/src/audio/W8994/wm8994.adb @@ -46,7 +46,7 @@ package body WM8994 is procedure Initialize (This : in out Audio_CODEC; Input : Input_Device; - Output : Analog_Outputs; + Output : Output_Device; Volume : Volume_Level; Frequency : Audio_Frequency) is @@ -366,7 +366,7 @@ package body WM8994 is -- Set_Mute -- -------------- - procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Modes) is + procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Mode) is begin if Output_Enabled then case Cmd is @@ -390,7 +390,7 @@ package body WM8994 is procedure Set_Output_Mode (This : in out Audio_CODEC; - Device : Analog_Outputs) + Device : Output_Device) is begin case Device is diff --git a/components/src/audio/W8994/wm8994.ads b/components/src/audio/W8994/wm8994.ads index 37bc3c6b0..1be49b66f 100644 --- a/components/src/audio/W8994/wm8994.ads +++ b/components/src/audio/W8994/wm8994.ads @@ -44,7 +44,7 @@ package WM8994 is Time : not null HAL.Time.Any_Delays) is tagged limited private; - type Analog_Outputs is + type Output_Device is (No_Output, Speaker, Headphone, @@ -96,11 +96,11 @@ package WM8994 is procedure Initialize (This : in out Audio_CODEC; Input : Input_Device; - Output : Analog_Outputs; + Output : Output_Device; Volume : Volume_Level; Frequency : Audio_Frequency); - type Mute_Modes is + type Mute_Mode is (Mute_On, Mute_Off); @@ -129,10 +129,10 @@ package WM8994 is procedure Set_Volume (This : in out Audio_CODEC; Volume : Volume_Level); - procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Modes); + procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Mode); procedure Set_Output_Mode (This : in out Audio_CODEC; - Device : Analog_Outputs); + Device : Output_Device); procedure Set_Frequency (This : in out Audio_CODEC; Freq : Audio_Frequency); From 4ac5b515e1f243a8e2298b132c86516bb2ec404c Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Fri, 8 May 2026 18:53:12 -0500 Subject: [PATCH 12/22] Move the odd package-level Boolean objects to be components of the primary ADT --- components/src/audio/W8994/wm8994.adb | 18 +++++++----------- components/src/audio/W8994/wm8994.ads | 5 ++++- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/components/src/audio/W8994/wm8994.adb b/components/src/audio/W8994/wm8994.adb index 0dee4e31a..84d7bffce 100644 --- a/components/src/audio/W8994/wm8994.adb +++ b/components/src/audio/W8994/wm8994.adb @@ -35,10 +35,6 @@ package body WM8994 is WM8994_CHIPID_ADDR : constant := 16#00#; - Output_Enabled : Boolean := False; - Input_Enabled : Boolean := False; - pragma Unreferenced (Input_Enabled); - ---------------- -- Initialize -- ---------------- @@ -69,8 +65,8 @@ package body WM8994 is This.Time.Delay_Milliseconds (50); - Output_Enabled := Output /= No_Output; - Input_Enabled := Input /= No_Input; + This.Output_Enabled := Output /= No_Output; + This.Input_Enabled := Input /= No_Input; This.Set_Output_Mode (Output); @@ -303,7 +299,7 @@ package body WM8994 is procedure Stop (This : in out Audio_CODEC; Cmd : Stop_Mode) is begin - if Output_Enabled then + if This.Output_Enabled then -- Mute the output first This.Set_Mute (Mute_On); @@ -311,7 +307,7 @@ package body WM8994 is return; end if; - Output_Enabled := False; + This.Output_Enabled := False; -- Mute the AIF1 Timeslot 0 DAC1 path I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0200#); @@ -368,7 +364,7 @@ package body WM8994 is procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Mode) is begin - if Output_Enabled then + if This.Output_Enabled then case Cmd is when Mute_On => -- Soft Mute the AIF1 Timeslot 0 DAC1 path L&R @@ -512,8 +508,8 @@ package body WM8994 is procedure Reset (This : in out Audio_CODEC) is begin I2C_Write (This, WM8994_SW_Reset, 16#0000#); - Output_Enabled := False; - Input_Enabled := False; + This.Output_Enabled := False; + This.Input_Enabled := False; end Reset; end WM8994; diff --git a/components/src/audio/W8994/wm8994.ads b/components/src/audio/W8994/wm8994.ads index 1be49b66f..a90f41107 100644 --- a/components/src/audio/W8994/wm8994.ads +++ b/components/src/audio/W8994/wm8994.ads @@ -145,6 +145,9 @@ private (Port : not null Any_I2C_Port; I2C_Addr : UInt10; Time : not null HAL.Time.Any_Delays) - is tagged limited null record; + is tagged limited record + Output_Enabled : Boolean := False; + Input_Enabled : Boolean := False; + end record; end WM8994; From e57ed5e0606fd2c543ad290f640e63e2c5d06225 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Sat, 9 May 2026 11:49:08 -0500 Subject: [PATCH 13/22] Update for type name change in package WM8994, in which Analog_Outputs was changed back to Output_Device --- boards/stm32_common/stm32f746disco/audio.adb | 6 +++--- boards/stm32_common/stm32f746disco/audio.ads | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/boards/stm32_common/stm32f746disco/audio.adb b/boards/stm32_common/stm32f746disco/audio.adb index cefb2a307..4ab71c0c0 100644 --- a/boards/stm32_common/stm32f746disco/audio.adb +++ b/boards/stm32_common/stm32f746disco/audio.adb @@ -96,11 +96,11 @@ package body Audio is This.Device.Reset; This.Device.Initialize (Input => WM8994.No_Input, - Output => WM8994.Analog_Outputs (Sink), + Output => WM8994.Output_Device (Sink), Volume => As_Device_Volume (Volume), Frequency => WM8994.Audio_Frequency (Frequency)); - This.Sink := WM8994.Analog_Outputs (Sink); + This.Sink := WM8994.Output_Device (Sink); -- For sake of any individual calls to Set_Frequency, assuming the STM -- code is correct that we also need to set the clocks when setting the -- frequency @@ -126,7 +126,7 @@ package body Audio is (This : in out WM8994_Audio_CODEC; Frequency : Audio_Frequency) is - use type WM8994.Analog_Outputs; + use type WM8994.Output_Device; begin if This.Sink = WM8994.No_Output then raise Constraint_Error with "No prior call to Initialize"; diff --git a/boards/stm32_common/stm32f746disco/audio.ads b/boards/stm32_common/stm32f746disco/audio.ads index d56ed81c7..e10b9e4e8 100644 --- a/boards/stm32_common/stm32f746disco/audio.ads +++ b/boards/stm32_common/stm32f746disco/audio.ads @@ -44,7 +44,7 @@ package Audio is type WM8994_Audio_CODEC (Port : not null Any_I2C_Port) is tagged limited private; - type Audio_Outputs is new WM8994.Analog_Outputs with + type Audio_Outputs is new WM8994.Output_Device with Static_Predicate => Audio_Outputs in Headphone | Speaker | Both; @@ -108,7 +108,7 @@ private (Port : not null Any_I2C_Port) is tagged limited record Device : WM8994.Audio_CODEC (Port, Audio_I2C_Addr, Ravenscar_Time.Delays); - Sink : WM8994.Analog_Outputs := WM8994.No_Output; + Sink : WM8994.Output_Device := WM8994.No_Output; -- The initial value of Sink is overwritten by Initialize. The value -- No_Output will trigger a C_E if ever referenced, so it is used as a -- check that Initialize has been called. We need the component Sink From f58ee783659fa8bc2fa8242d416d790e1abfeed2 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Wed, 13 May 2026 09:50:46 -0500 Subject: [PATCH 14/22] Remove immediate internal read after write in procedure I2C_Write. Presumably this was for debugging but is not necessary in normal operation. --- components/src/audio/W8994/wm8994-io.adb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/components/src/audio/W8994/wm8994-io.adb b/components/src/audio/W8994/wm8994-io.adb index 0b4d8953f..893ffefa3 100644 --- a/components/src/audio/W8994/wm8994-io.adb +++ b/components/src/audio/W8994/wm8994-io.adb @@ -42,7 +42,6 @@ package body WM8994.IO is is Status : I2C_Status; Data : I2C_Data (1 .. 2); - Check : UInt16 with Unreferenced; begin -- Device is MSB first Data (1) := UInt8 (Shift_Right (Value and 16#FF00#, 8)); @@ -54,10 +53,6 @@ package body WM8994.IO is Mem_Addr_Size => Memory_Size_16b, Data => Data, Status => Status); - - if Register /= 0 then - Check := I2C_Read (This, Register); - end if; end I2C_Write; -------------- From b3f13a359525e75981111b2814ec7e31891a5692 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Wed, 13 May 2026 10:30:10 -0500 Subject: [PATCH 15/22] Keep track of which output is selected and only manipulate the hardware for that selection. * Package WM8994 spec: - Change Boolean Output_Enabled to Current_Output to track which output is currently selected. * Package WM8994 body: The bulk of the changes are in the WM8994 package body. The following describes the problems corrected. - Procedure Initialize: the speaker hardware was enabled unconditionally when any output selected. - Procedure Set_Volume: always set both headphone and speaker volumes. - Procedure Set_Mute: always muted both output paths. - Procedure Set_Output_Mode: the comments were inverted for both cases (says "Enable but does the opposite). - Procedure Pause: incorrectly (accidentally) shut down thermal management. Per WM8994 Rev 4.6 p.256, Register 02h bit 0 is undefined. Writing 16#0001# has no documented effect but clears bits 13-14, disabling the thermal sensor (TSHUT_ENA) and thermal shutdown (TSHUT_OPDIS). Muting via Set_Mute is sufficient. --- components/src/audio/W8994/wm8994.adb | 132 ++++++++++++++++---------- components/src/audio/W8994/wm8994.ads | 4 +- 2 files changed, 82 insertions(+), 54 deletions(-) diff --git a/components/src/audio/W8994/wm8994.adb b/components/src/audio/W8994/wm8994.adb index 84d7bffce..dd8c586d0 100644 --- a/components/src/audio/W8994/wm8994.adb +++ b/components/src/audio/W8994/wm8994.adb @@ -65,7 +65,7 @@ package body WM8994 is This.Time.Delay_Milliseconds (50); - This.Output_Enabled := Output /= No_Output; + This.Current_Output := Output; This.Input_Enabled := Input /= No_Input; This.Set_Output_Mode (Output); @@ -126,12 +126,12 @@ package body WM8994 is -- Enable AIF1 Clock, AIF1 Clock Source = MCLK1 pin I2C_Write (This, WM8994_AIF1_Clocking1, 16#0001#); - if Output /= No_Output then - -- Analog Output Configuration + if Output = Speaker or else Output = Both then + -- Speaker Analog Output Configuration -- Enable SPKRVOL PGA, Enable SPKMIXR, Enable SPKLVOL PGA, Enable -- SPKMIXL - I2C_Write (This, 16#03#, 16#0300#); + I2C_Write (This, WM8994_PWR_Management_3, 16#0300#); -- Left Speaker Mixer Volume = 0dB I2C_Write (This, WM8994_SPKMIXL_ATT, 16#0000#); @@ -145,16 +145,28 @@ package body WM8994 is I2C_Write (This, WM8994_Speaker_Mixer, 16#0300#); -- Enable bias generator, Enable VMID, Enable SPKOUTL, Enable SPKOUTR - I2C_Write (This, WM8994_PWR_Management_1, 16#3003#); + Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#3003#; + I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); - -- Headphone/Speaker Enable + -- Unmute DAC 2 (Left) + I2C_Write (This, WM8994_DAC2_Left_Vol, 16#00C0#); + + -- Unmute DAC 2 (Right) + I2C_Write (This, WM8994_DAC2_Right_Vol, 16#00C0#); + + -- Unmute the AIF1 Timeslot 1 DAC2 path + I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0000#); + end if; + + if Output = Headphone or else Output = Auto or else Output = Both then + -- Headphone Analog Output Configuration -- Enable Class W, Class W Envelope Tracking = AIF1 Timeslot 0 I2C_Write (This, WM8994_CLASS_W, 16#0001#); -- Enable bias generator, Enable VMID, Enable HPOUT1 (Left) and - -- Enable HPOUT1 (Right) input stages idem for Speaker - Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0303# or 16#3003#; + -- Enable HPOUT1 (Right) input stages + Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0303#; I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); -- Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate stages @@ -170,15 +182,17 @@ package body WM8994 is I2C_Write (This, WM8994_Output_Mixer_1, 16#0001#); -- Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) - -- path. + -- path I2C_Write (This, WM8994_Output_Mixer_2, 16#0001#); -- Enable Left Output Mixer (MIXOUTL), Enable Right Output Mixer - -- (MIXOUTR) idem for SPKOUTL and SPKOUTR. - I2C_Write (This, WM8994_PWR_Management_3, 16#0030# or 16#0300#); + -- (MIXOUTR) + I2C_Write (This, WM8994_PWR_Management_3, + (if Output = Both then 16#0030# or 16#0300# + else 16#0030#)); -- Enable DC Servo and trigger start-up mode on left and right - -- channels. + -- channels I2C_Write (This, WM8994_DC_Servo1, 16#0033#); -- Add Delay @@ -188,26 +202,17 @@ package body WM8994 is -- stages. Remove clamps. I2C_Write (This, WM8994_Analog_HP, 16#00EE#); - -- Unmutes - -- Unmute DAC 1 (Left) I2C_Write (This, WM8994_DAC1_Left_Vol, 16#00C0#); -- Unmute DAC 1 (Right) I2C_Write (This, WM8994_DAC1_Right_Vol, 16#00C0#); - -- Unmute the AIF1 Timeslot 0 DAC path + -- Unmute the AIF1 Timeslot 0 DAC1 path I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0000#); + end if; - -- Unmute DAC 2 (Left) - I2C_Write (This, WM8994_DAC2_Left_Vol, 16#00C0#); - - -- Unmute DAC 2 (Right) - I2C_Write (This, WM8994_DAC2_Right_Vol, 16#00C0#); - - -- Unmute the AIF1 Timeslot 1 DAC2 path - I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0000#); - + if Output /= No_Output then -- Volume Control This.Set_Volume (Volume); end if; @@ -277,11 +282,7 @@ package body WM8994 is procedure Pause (This : in out Audio_CODEC) is begin - -- Pause the audio playing This.Set_Mute (Mute_On); - - -- CODEC in powersave mode - I2C_Write (This, WM8994_PWR_Management_2, 16#01#); end Pause; ------------ @@ -299,7 +300,7 @@ package body WM8994 is procedure Stop (This : in out Audio_CODEC; Cmd : Stop_Mode) is begin - if This.Output_Enabled then + if This.Current_Output /= No_Output then -- Mute the output first This.Set_Mute (Mute_On); @@ -307,7 +308,7 @@ package body WM8994 is return; end if; - This.Output_Enabled := False; + This.Current_Output := No_Output; -- Mute the AIF1 Timeslot 0 DAC1 path I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0200#); @@ -344,17 +345,26 @@ package body WM8994 is else This.Set_Mute (Mute_Off); - -- Left Headphone Volume - I2C_Write (This, WM8994_Left_Output_Vol, Volume or 16#140#); + if This.Current_Output = Headphone + or else This.Current_Output = Auto + or else This.Current_Output = Both + then + -- Left Headphone Volume + I2C_Write (This, WM8994_Left_Output_Vol, Volume or 16#140#); - -- Right Headphone volume - I2C_Write (This, WM8994_Right_Output_Vol, Volume or 16#140#); + -- Right Headphone Volume + I2C_Write (This, WM8994_Right_Output_Vol, Volume or 16#140#); + end if; - -- Left Speaker volume - I2C_Write (This, WM8994_SPK_Left_Vol, Volume or 16#140#); + if This.Current_Output = Speaker + or else This.Current_Output = Both + then + -- Left Speaker volume + I2C_Write (This, WM8994_SPK_Left_Vol, Volume or 16#140#); - -- Right Speaker volume - I2C_Write (This, WM8994_SPK_Right_Vol, Volume or 16#140#); + -- Right Speaker volume + I2C_Write (This, WM8994_SPK_Right_Vol, Volume or 16#140#); + end if; end if; end Set_Volume; @@ -364,18 +374,36 @@ package body WM8994 is procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Mode) is begin - if This.Output_Enabled then + if This.Current_Output /= No_Output then case Cmd is when Mute_On => - -- Soft Mute the AIF1 Timeslot 0 DAC1 path L&R - I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0200#); - -- Soft Mute the AIF1 Timeslot 1 DAC2 path L&R - I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0200#); + if This.Current_Output = Headphone + or else This.Current_Output = Auto + or else This.Current_Output = Both + then + -- Soft Mute the AIF1 Timeslot 0 DAC1 path L&R + I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0200#); + end if; + if This.Current_Output = Speaker + or else This.Current_Output = Both + then + -- Soft Mute the AIF1 Timeslot 1 DAC2 path L&R + I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0200#); + end if; when Mute_Off => - -- Unmute the AIF1 Timeslot 0 DAC1 path L&R - I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0000#); - -- Unmute the AIF1 Timeslot 1 DAC2 path L&R - I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0000#); + if This.Current_Output = Headphone + or else This.Current_Output = Auto + or else This.Current_Output = Both + then + -- Unmute the AIF1 Timeslot 0 DAC1 path L&R + I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0000#); + end if; + if This.Current_Output = Speaker + or else This.Current_Output = Both + then + -- Unmute the AIF1 Timeslot 1 DAC2 path L&R + I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0000#); + end if; end case; end if; end Set_Mute; @@ -401,13 +429,13 @@ package body WM8994 is when Speaker => -- Enable DAC1 (left), DAC1 (Right) I2C_Write (This, WM8994_PWR_Management_5, 16#0C0C#); - -- Enable the AIF1 Timeslot 0 (Left) to DAC1 (left) mixer path + -- Disable the AIF1 Timeslot 0 (Left) to DAC1 (left) mixer path I2C_Write (This, WM8994_AIF1_DAC1_LMR, 16#0000#); - -- Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path + -- Disable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path I2C_Write (This, WM8994_AIF1_DAC1_RMR, 16#0000#); - -- Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path + -- Enable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path I2C_Write (This, WM8994_AIF1_DAC2_LMR, 16#0002#); - -- Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path + -- Enable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path I2C_Write (This, WM8994_AIF1_DAC2_RMR, 16#0002#); when Headphone | Auto => @@ -508,7 +536,7 @@ package body WM8994 is procedure Reset (This : in out Audio_CODEC) is begin I2C_Write (This, WM8994_SW_Reset, 16#0000#); - This.Output_Enabled := False; + This.Current_Output := No_Output; This.Input_Enabled := False; end Reset; diff --git a/components/src/audio/W8994/wm8994.ads b/components/src/audio/W8994/wm8994.ads index a90f41107..80fad8ebe 100644 --- a/components/src/audio/W8994/wm8994.ads +++ b/components/src/audio/W8994/wm8994.ads @@ -146,8 +146,8 @@ private I2C_Addr : UInt10; Time : not null HAL.Time.Any_Delays) is tagged limited record - Output_Enabled : Boolean := False; - Input_Enabled : Boolean := False; + Current_Output : Output_Device := No_Output; + Input_Enabled : Boolean := False; end record; end WM8994; From 869fe0cda7ab9900f294b12d24704fdecc853052 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Wed, 13 May 2026 11:33:26 -0500 Subject: [PATCH 16/22] Refactor sequences of I2C writes into internal help er procedures for readability/comprehension --- components/src/audio/W8994/wm8994.adb | 414 +++++++++++++++----------- 1 file changed, 244 insertions(+), 170 deletions(-) diff --git a/components/src/audio/W8994/wm8994.adb b/components/src/audio/W8994/wm8994.adb index dd8c586d0..38275967c 100644 --- a/components/src/audio/W8994/wm8994.adb +++ b/components/src/audio/W8994/wm8994.adb @@ -35,6 +35,39 @@ package body WM8994 is WM8994_CHIPID_ADDR : constant := 16#00#; + -- The following four procedures encapsulate the analog output and input + -- enable sequences for Initialize. They are local to the package body and + -- must not be called outside of Initialize: each sequence depends on the + -- shared Power_Mgnt_Reg_1 accumulator being correct on entry, and on the + -- digital path (clocking, DAC routing) having already been configured by + -- Set_Output_Mode and Set_Frequency before any of them are called. + + procedure Enable_Speaker_Output + (This : in out Audio_CODEC; + Power_Mgnt_Reg_1 : in out UInt16); + + procedure Enable_Headphone_Output + (This : in out Audio_CODEC; + Power_Mgnt_Reg_1 : in out UInt16; + Include_Speaker : Boolean); + -- Include_Speaker must be True when both outputs are being enabled + -- simultaneously (Output = Both), so that the PWR_Management_3 write + -- also preserves the speaker mixer bits set by Enable_Speaker_Output. + + procedure Enable_Microphone_Input + (This : in out Audio_CODEC; + Power_Mgnt_Reg_1 : in out UInt16); + -- Must be called after Enable_Speaker_Output / Enable_Headphone_Output + -- so that Power_Mgnt_Reg_1 already contains any output power bits; the + -- microphone bias bits are OR-ed in and written as a single update. + + procedure Enable_Line_Input + (This : in out Audio_CODEC; + Power_Mgnt_Reg_1 : in out UInt16); + -- Must be called after Enable_Speaker_Output / Enable_Headphone_Output + -- so that Power_Mgnt_Reg_1 already contains any output power bits; the + -- bias enable bits are OR-ed in and written as a single update. + ---------------- -- Initialize -- ---------------- @@ -75,47 +108,31 @@ package body WM8994 is null; when Microphone => - -- Enable AIF1ADC2 (Left), Enable AIF1ADC2 (Right) - -- Enable DMICDAT2 (Left), Enable DMICDAT2 (Right) - -- Enable Left ADC, Enable Right ADC + -- Digital path configuration for DMIC2 via AIF1 Timeslot 1; + -- analog power for microphone bias is applied later, after any + -- output power bits have been accumulated into Power_Mgnt_Reg_1. I2C_Write (This, WM8994_PWR_Management_4, 16#0C30#); - -- Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right - -- Timeslot 1 I2C_Write (This, WM8994_AIF1_DRC2, 16#00DB#); - -- Disable IN1L, IN1R, IN2L, IN2R, Enable Thermal sensor & - -- shutdown I2C_Write (This, WM8994_PWR_Management_4, 16#6000#); - -- Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path I2C_Write (This, WM8994_AIF1_ADC2_LMR, 16#0002#); - -- Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path I2C_Write (This, WM8994_AIF1_ADC2_RMR, 16#0002#); - -- GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC2 - -- signal detect I2C_Write (This, WM8994_GPIO1, 16#000E#); when Input_Line => - -- Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right) - -- Enable Left ADC, Enable Right ADC + -- Digital path configuration for IN1L/IN1R via AIF1 Timeslot 0; + -- analog power and PGA configuration is applied later, after any + -- output power bits have been accumulated into Power_Mgnt_Reg_1. I2C_Write (This, WM8994_PWR_Management_4, 16#0303#); - -- Enable AIF1 DRC1 Signal Detect & DRC in AIF1ADC1 Left/Right - -- Timeslot 0 I2C_Write (This, WM8994_AIF1_DRC1, 16#00DB#); - -- Enable IN1L and IN1R, Disable IN2L and IN2R, Enable Thermal - -- sensor & shutdown I2C_Write (This, WM8994_PWR_Management_4, 16#6350#); - -- Enable the ADCL(Left) to AIF1 Timeslot 0 (Left) mixer path I2C_Write (This, WM8994_AIF1_ADC1_LMR, 16#0002#); - -- Enable the ADCR(Right) to AIF1 Timeslot 0 (Right) mixer path I2C_Write (This, WM8994_AIF1_ADC1_RMR, 16#0002#); - -- GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 - -- signal detect I2C_Write (This, WM8994_GPIO1, 16#000D#); end case; This.Set_Frequency (Frequency); - -- AIF1 Word Length = 16-bits, AIF1 Format = I2S (Default Register - -- Value) + -- AIF1 Word Length = 16-bits, AIF1 Format = I2S (Default Register Value) I2C_Write (This, WM8994_AIF1_Control1, 16#4010#); -- slave mode I2C_Write (This, WM8994_AIF1_Master_Slave, 16#0000#); @@ -126,134 +143,33 @@ package body WM8994 is -- Enable AIF1 Clock, AIF1 Clock Source = MCLK1 pin I2C_Write (This, WM8994_AIF1_Clocking1, 16#0001#); + -- Analog output power sequencing: speaker first (if active), then + -- headphone (if active). Each call ORs its power bits into + -- Power_Mgnt_Reg_1 before writing PWR_Management_1, so that both + -- sets of bits are present if both outputs are enabled. if Output = Speaker or else Output = Both then - -- Speaker Analog Output Configuration - - -- Enable SPKRVOL PGA, Enable SPKMIXR, Enable SPKLVOL PGA, Enable - -- SPKMIXL - I2C_Write (This, WM8994_PWR_Management_3, 16#0300#); - - -- Left Speaker Mixer Volume = 0dB - I2C_Write (This, WM8994_SPKMIXL_ATT, 16#0000#); - - -- Speaker output mode = Class D, Right Speaker Mixer Volume = 0dB - -- ((16#23#, 16#0100#) = class AB) - I2C_Write (This, WM8994_SPKMIXR_ATT, 16#0000#); - - -- Unmute DAC2 (Left) to Left Speaker Mixer (SPKMIXL) path, - -- Unmute DAC2 (Right) to Right Speaker Mixer (SPKMIXR) path - I2C_Write (This, WM8994_Speaker_Mixer, 16#0300#); - - -- Enable bias generator, Enable VMID, Enable SPKOUTL, Enable SPKOUTR - Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#3003#; - I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); - - -- Unmute DAC 2 (Left) - I2C_Write (This, WM8994_DAC2_Left_Vol, 16#00C0#); - - -- Unmute DAC 2 (Right) - I2C_Write (This, WM8994_DAC2_Right_Vol, 16#00C0#); - - -- Unmute the AIF1 Timeslot 1 DAC2 path - I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0000#); + Enable_Speaker_Output (This, Power_Mgnt_Reg_1); end if; if Output = Headphone or else Output = Auto or else Output = Both then - -- Headphone Analog Output Configuration - - -- Enable Class W, Class W Envelope Tracking = AIF1 Timeslot 0 - I2C_Write (This, WM8994_CLASS_W, 16#0001#); - - -- Enable bias generator, Enable VMID, Enable HPOUT1 (Left) and - -- Enable HPOUT1 (Right) input stages - Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0303#; - I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); - - -- Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate stages - I2C_Write (This, WM8994_Analog_HP, 16#0022#); - - -- Enable Charge Pump - I2C_Write (This, WM8994_Charge_Pump1, 16#9F25#); - - -- Add Delay - This.Time.Delay_Milliseconds (15); - - -- Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path - I2C_Write (This, WM8994_Output_Mixer_1, 16#0001#); - - -- Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) - -- path - I2C_Write (This, WM8994_Output_Mixer_2, 16#0001#); - - -- Enable Left Output Mixer (MIXOUTL), Enable Right Output Mixer - -- (MIXOUTR) - I2C_Write (This, WM8994_PWR_Management_3, - (if Output = Both then 16#0030# or 16#0300# - else 16#0030#)); - - -- Enable DC Servo and trigger start-up mode on left and right - -- channels - I2C_Write (This, WM8994_DC_Servo1, 16#0033#); - - -- Add Delay - This.Time.Delay_Milliseconds (250); - - -- Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate and output - -- stages. Remove clamps. - I2C_Write (This, WM8994_Analog_HP, 16#00EE#); - - -- Unmute DAC 1 (Left) - I2C_Write (This, WM8994_DAC1_Left_Vol, 16#00C0#); - - -- Unmute DAC 1 (Right) - I2C_Write (This, WM8994_DAC1_Right_Vol, 16#00C0#); - - -- Unmute the AIF1 Timeslot 0 DAC1 path - I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0000#); + Enable_Headphone_Output (This, Power_Mgnt_Reg_1, + Include_Speaker => Output = Both); end if; if Output /= No_Output then - -- Volume Control This.Set_Volume (Volume); end if; - if Input /= No_Input then - if Input = Microphone then - -- Enable Microphone bias 1 generator, Enable VMID - Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0013#; - I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); - - -- ADC oversample enable - I2C_Write (This, WM8994_Oversampling, 16#0002#); - - -- AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz - I2C_Write (This, WM8994_AIF1_ADC2_Filters, 16#3800#); - - elsif Input = Input_Line then - -- Enable normal bias generator, Enable VMID - Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0003#; - I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); - - -- Disable mute on IN1L, IN1L Volume = +0dB - I2C_Write (This, WM8994_Left_Line_In12_Vol, 16#000B#); - - -- Disable mute on IN1R, IN1R Volume = +0dB - I2C_Write (This, WM8994_Right_Line_In12_Vol, 16#000B#); - - -- Disable mute on IN1L_TO_MIXINL, Gain = +0dB - I2C_Write (This, WM8994_Input_Mixer_3, 16#0025#); - - -- Disable mute on IN1R_TO_MIXINL, Gain = +0dB - I2C_Write (This, WM8994_Input_Mixer_4, 16#0025#); - - -- IN1LN_TO_IN1L, IN1LP_TO_VMID, IN1RN_TO_IN1R, IN1RP_TO_VMID - I2C_Write (This, WM8994_Input_Mixer_2, 16#0011#); - - -- AIF ADC1 HPF enable, HPF cut = hifi mode fc=4Hz at fs=48kHz - I2C_Write (This, WM8994_AIF1_ADC1_Filters, 16#1800#); - end if; + -- Analog input power sequencing: output power bits are already + -- accumulated in Power_Mgnt_Reg_1, so the input enable writes a + -- single combined value to PWR_Management_1. + case Input is + when No_Input => null; + when Microphone => Enable_Microphone_Input (This, Power_Mgnt_Reg_1); + when Input_Line => Enable_Line_Input (This, Power_Mgnt_Reg_1); + end case; - -- Volume Control + if Input /= No_Input then This.Set_Volume (Volume); end if; end Initialize; @@ -301,7 +217,6 @@ package body WM8994 is procedure Stop (This : in out Audio_CODEC; Cmd : Stop_Mode) is begin if This.Current_Output /= No_Output then - -- Mute the output first This.Set_Mute (Mute_On); if Cmd = Stop_Power_Down_Sw then @@ -312,16 +227,12 @@ package body WM8994 is -- Mute the AIF1 Timeslot 0 DAC1 path I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0200#); - -- Mute the AIF1 Timeslot 1 DAC2 path I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0200#); - -- Disable DAC1L_TO_HPOUT1L I2C_Write (This, WM8994_Output_Mixer_1, 16#0000#); - -- Disable DAC1R_TO_HPOUT1R I2C_Write (This, WM8994_Output_Mixer_2, 16#0000#); - -- Disable DAC1 and DAC2 I2C_Write (This, WM8994_PWR_Management_5, 16#0000#); @@ -340,28 +251,20 @@ package body WM8994 is is begin if Volume = 0 then - -- Mute the codec This.Set_Mute (Mute_On); else This.Set_Mute (Mute_Off); - if This.Current_Output = Headphone - or else This.Current_Output = Auto - or else This.Current_Output = Both - then + if This.Current_Output in Headphone | Auto | Both then -- Left Headphone Volume I2C_Write (This, WM8994_Left_Output_Vol, Volume or 16#140#); - -- Right Headphone Volume I2C_Write (This, WM8994_Right_Output_Vol, Volume or 16#140#); end if; - if This.Current_Output = Speaker - or else This.Current_Output = Both - then + if This.Current_Output in Speaker | Both then -- Left Speaker volume I2C_Write (This, WM8994_SPK_Left_Vol, Volume or 16#140#); - -- Right Speaker volume I2C_Write (This, WM8994_SPK_Right_Vol, Volume or 16#140#); end if; @@ -377,30 +280,20 @@ package body WM8994 is if This.Current_Output /= No_Output then case Cmd is when Mute_On => - if This.Current_Output = Headphone - or else This.Current_Output = Auto - or else This.Current_Output = Both - then + if This.Current_Output in Headphone | Auto | Both then -- Soft Mute the AIF1 Timeslot 0 DAC1 path L&R I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0200#); end if; - if This.Current_Output = Speaker - or else This.Current_Output = Both - then + if This.Current_Output in Speaker | Both then -- Soft Mute the AIF1 Timeslot 1 DAC2 path L&R I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0200#); end if; when Mute_Off => - if This.Current_Output = Headphone - or else This.Current_Output = Auto - or else This.Current_Output = Both - then + if This.Current_Output in Headphone | Auto | Both then -- Unmute the AIF1 Timeslot 0 DAC1 path L&R I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0000#); end if; - if This.Current_Output = Speaker - or else This.Current_Output = Both - then + if This.Current_Output in Speaker | Both then -- Unmute the AIF1 Timeslot 1 DAC2 path L&R I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0000#); end if; @@ -480,7 +373,7 @@ package body WM8994 is -- ratio is always 256, which is indicated by the bit pattern 2#0011#, -- so the lower digits in the values is always 3 in hex. -- - -- See the table labled "Register 0210h AIF1 Rate" pages 285 and 286 of + -- See the table labeled "Register 0210h AIF1 Rate" pages 285 and 286 of -- WM8994_Rev4.6 from Cirrus Logic case Freq is when Audio_Freq_8kHz => @@ -540,4 +433,185 @@ package body WM8994 is This.Input_Enabled := False; end Reset; + ---------------------------- + -- Enable_Speaker_Output -- + ---------------------------- + + procedure Enable_Speaker_Output + (This : in out Audio_CODEC; + Power_Mgnt_Reg_1 : in out UInt16) + is + begin + -- Enable SPKRVOL PGA, Enable SPKMIXR, Enable SPKLVOL PGA, Enable SPKMIXL + I2C_Write (This, WM8994_PWR_Management_3, 16#0300#); + + -- Left Speaker Mixer Volume = 0dB + I2C_Write (This, WM8994_SPKMIXL_ATT, 16#0000#); + + -- Speaker output mode = Class D, Right Speaker Mixer Volume = 0dB + -- ((16#23#, 16#0100#) = class AB) + I2C_Write (This, WM8994_SPKMIXR_ATT, 16#0000#); + + -- Unmute DAC2 (Left) to Left Speaker Mixer (SPKMIXL) path, + -- Unmute DAC2 (Right) to Right Speaker Mixer (SPKMIXR) path + I2C_Write (This, WM8994_Speaker_Mixer, 16#0300#); + + -- Enable bias generator, Enable VMID, Enable SPKOUTL, Enable SPKOUTR + Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#3003#; + I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); + + -- Unmute DAC 2 (Left) + I2C_Write (This, WM8994_DAC2_Left_Vol, 16#00C0#); + + -- Unmute DAC 2 (Right) + I2C_Write (This, WM8994_DAC2_Right_Vol, 16#00C0#); + + -- Unmute the AIF1 Timeslot 1 DAC2 path + I2C_Write (This, WM8994_AIF1_DAC2_Filter1, 16#0000#); + end Enable_Speaker_Output; + + ----------------------------- + -- Enable_Headphone_Output -- + ----------------------------- + + procedure Enable_Headphone_Output + (This : in out Audio_CODEC; + Power_Mgnt_Reg_1 : in out UInt16; + Include_Speaker : Boolean) + is + begin + -- Enable Class W, Class W Envelope Tracking = AIF1 Timeslot 0 + I2C_Write (This, WM8994_CLASS_W, 16#0001#); + + -- Enable bias generator, Enable VMID, Enable HPOUT1 (Left) and + -- Enable HPOUT1 (Right) input stages + Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0303#; + I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); + + -- Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate stages + I2C_Write (This, WM8994_Analog_HP, 16#0022#); + + -- Enable Charge Pump + I2C_Write (This, WM8994_Charge_Pump1, 16#9F25#); + + This.Time.Delay_Milliseconds (15); + + -- Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path + I2C_Write (This, WM8994_Output_Mixer_1, 16#0001#); + + -- Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path + I2C_Write (This, WM8994_Output_Mixer_2, 16#0001#); + + -- Enable Left Output Mixer (MIXOUTL), Enable Right Output Mixer + -- (MIXOUTR); when speaker is also active, preserve its mixer bits too + I2C_Write (This, WM8994_PWR_Management_3, + (if Include_Speaker then 16#0030# or 16#0300# + else 16#0030#)); + + -- Enable DC Servo and trigger start-up mode on left and right channels + I2C_Write (This, WM8994_DC_Servo1, 16#0033#); + + -- Add Delay: DC Servo requires up to 250ms to converge + This.Time.Delay_Milliseconds (250); + + -- Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate and output + -- stages. Remove clamps. + I2C_Write (This, WM8994_Analog_HP, 16#00EE#); + + -- Unmute DAC 1 (Left) + I2C_Write (This, WM8994_DAC1_Left_Vol, 16#00C0#); + + -- Unmute DAC 1 (Right) + I2C_Write (This, WM8994_DAC1_Right_Vol, 16#00C0#); + + -- Unmute the AIF1 Timeslot 0 DAC1 path + I2C_Write (This, WM8994_AIF1_DAC1_Filter1, 16#0000#); + end Enable_Headphone_Output; + + ------------------------------ + -- Enable_Microphone_Input -- + ------------------------------ + + procedure Enable_Microphone_Input + (This : in out Audio_CODEC; + Power_Mgnt_Reg_1 : in out UInt16) + is + begin + -- Enable AIF1ADC2 (Left), Enable AIF1ADC2 (Right) + -- Enable DMICDAT2 (Left), Enable DMICDAT2 (Right) + -- Enable Left ADC, Enable Right ADC + I2C_Write (This, WM8994_PWR_Management_4, 16#0C30#); + -- Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right + -- Timeslot 1 + I2C_Write (This, WM8994_AIF1_DRC2, 16#00DB#); + -- Disable IN1L, IN1R, IN2L, IN2R, Enable Thermal sensor & shutdown + I2C_Write (This, WM8994_PWR_Management_4, 16#6000#); + -- Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path + I2C_Write (This, WM8994_AIF1_ADC2_LMR, 16#0002#); + -- Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path + I2C_Write (This, WM8994_AIF1_ADC2_RMR, 16#0002#); + -- GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC2 + -- signal detect + I2C_Write (This, WM8994_GPIO1, 16#000E#); + + -- Enable Microphone bias 1 generator, Enable VMID + Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0013#; + I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); + + -- ADC oversample enable + I2C_Write (This, WM8994_Oversampling, 16#0002#); + + -- AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz + I2C_Write (This, WM8994_AIF1_ADC2_Filters, 16#3800#); + end Enable_Microphone_Input; + + ------------------------- + -- Enable_Line_Input -- + ------------------------- + + procedure Enable_Line_Input + (This : in out Audio_CODEC; + Power_Mgnt_Reg_1 : in out UInt16) + is + begin + -- Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right) + -- Enable Left ADC, Enable Right ADC + I2C_Write (This, WM8994_PWR_Management_4, 16#0303#); + -- Enable AIF1 DRC1 Signal Detect & DRC in AIF1ADC1 Left/Right + -- Timeslot 0 + I2C_Write (This, WM8994_AIF1_DRC1, 16#00DB#); + -- Enable IN1L and IN1R, Disable IN2L and IN2R, Enable Thermal + -- sensor & shutdown + I2C_Write (This, WM8994_PWR_Management_4, 16#6350#); + -- Enable the ADCL(Left) to AIF1 Timeslot 0 (Left) mixer path + I2C_Write (This, WM8994_AIF1_ADC1_LMR, 16#0002#); + -- Enable the ADCR(Right) to AIF1 Timeslot 0 (Right) mixer path + I2C_Write (This, WM8994_AIF1_ADC1_RMR, 16#0002#); + -- GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 + -- signal detect + I2C_Write (This, WM8994_GPIO1, 16#000D#); + + -- Enable normal bias generator, Enable VMID + Power_Mgnt_Reg_1 := Power_Mgnt_Reg_1 or 16#0003#; + I2C_Write (This, WM8994_PWR_Management_1, Power_Mgnt_Reg_1); + + -- Disable mute on IN1L, IN1L Volume = +0dB + I2C_Write (This, WM8994_Left_Line_In12_Vol, 16#000B#); + + -- Disable mute on IN1R, IN1R Volume = +0dB + I2C_Write (This, WM8994_Right_Line_In12_Vol, 16#000B#); + + -- Disable mute on IN1L_TO_MIXINL, Gain = +0dB + I2C_Write (This, WM8994_Input_Mixer_3, 16#0025#); + + -- Disable mute on IN1R_TO_MIXINL, Gain = +0dB + I2C_Write (This, WM8994_Input_Mixer_4, 16#0025#); + + -- IN1LN_TO_IN1L, IN1LP_TO_VMID, IN1RN_TO_IN1R, IN1RP_TO_VMID + I2C_Write (This, WM8994_Input_Mixer_2, 16#0011#); + + -- AIF ADC1 HPF enable, HPF cut = hifi mode fc=4Hz at fs=48kHz + I2C_Write (This, WM8994_AIF1_ADC1_Filters, 16#1800#); + end Enable_Line_Input; + end WM8994; From ca40e5d536d75792cacb68cfe06e358559ed35f1 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Wed, 13 May 2026 11:43:52 -0500 Subject: [PATCH 17/22] Minor formatting and formal parameter name changes --- components/src/audio/W8994/wm8994.adb | 28 ++++++++++++++++----------- components/src/audio/W8994/wm8994.ads | 23 +++++++++++++++------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/components/src/audio/W8994/wm8994.adb b/components/src/audio/W8994/wm8994.adb index 38275967c..019709e09 100644 --- a/components/src/audio/W8994/wm8994.adb +++ b/components/src/audio/W8994/wm8994.adb @@ -40,7 +40,7 @@ package body WM8994 is -- must not be called outside of Initialize: each sequence depends on the -- shared Power_Mgnt_Reg_1 accumulator being correct on entry, and on the -- digital path (clocking, DAC routing) having already been configured by - -- Set_Output_Mode and Set_Frequency before any of them are called. + -- Set_Output_Device and Set_Frequency before any of them are called. procedure Enable_Speaker_Output (This : in out Audio_CODEC; @@ -101,7 +101,7 @@ package body WM8994 is This.Current_Output := Output; This.Input_Enabled := Input /= No_Input; - This.Set_Output_Mode (Output); + This.Set_Output_Device (Output); case Input is when No_Input => @@ -214,12 +214,15 @@ package body WM8994 is -- Stop -- ---------- - procedure Stop (This : in out Audio_CODEC; Cmd : Stop_Mode) is + procedure Stop + (This : in out Audio_CODEC; + Mode : Stop_Mode) + is begin if This.Current_Output /= No_Output then This.Set_Mute (Mute_On); - if Cmd = Stop_Power_Down_Sw then + if Mode = Stop_Power_Down_Sw then return; end if; @@ -275,10 +278,13 @@ package body WM8994 is -- Set_Mute -- -------------- - procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Mode) is + procedure Set_Mute + (This : in out Audio_CODEC; + Mode : Mute_Mode) + is begin if This.Current_Output /= No_Output then - case Cmd is + case Mode is when Mute_On => if This.Current_Output in Headphone | Auto | Both then -- Soft Mute the AIF1 Timeslot 0 DAC1 path L&R @@ -301,11 +307,11 @@ package body WM8994 is end if; end Set_Mute; - --------------------- - -- Set_Output_Mode -- - --------------------- + ----------------------- + -- Set_Output_Device -- + ----------------------- - procedure Set_Output_Mode + procedure Set_Output_Device (This : in out Audio_CODEC; Device : Output_Device) is @@ -357,7 +363,7 @@ package body WM8994 is -- Enable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path I2C_Write (This, WM8994_AIF1_DAC2_RMR, 16#0002#); end case; - end Set_Output_Mode; + end Set_Output_Device; ------------------- -- Set_Frequency -- diff --git a/components/src/audio/W8994/wm8994.ads b/components/src/audio/W8994/wm8994.ads index 80fad8ebe..4bbcc0004 100644 --- a/components/src/audio/W8994/wm8994.ads +++ b/components/src/audio/W8994/wm8994.ads @@ -116,6 +116,7 @@ package WM8994 is -- so users should re-initialize the codec. WM8994_ID : constant := 16#8994#; + -- The expected value returned by function Chip_Id function Chip_ID (This : in out Audio_CODEC) return UInt16; @@ -125,17 +126,25 @@ package WM8994 is procedure Resume (This : in out Audio_CODEC); - procedure Stop (This : in out Audio_CODEC; Cmd : Stop_Mode); + procedure Stop + (This : in out Audio_CODEC; + Mode : Stop_Mode); - procedure Set_Volume (This : in out Audio_CODEC; Volume : Volume_Level); + procedure Set_Volume + (This : in out Audio_CODEC; + Volume : Volume_Level); - procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Mode); + procedure Set_Mute + (This : in out Audio_CODEC; + Mode : Mute_Mode); - procedure Set_Output_Mode (This : in out Audio_CODEC; - Device : Output_Device); + procedure Set_Output_Device + (This : in out Audio_CODEC; + Device : Output_Device); - procedure Set_Frequency (This : in out Audio_CODEC; - Freq : Audio_Frequency); + procedure Set_Frequency + (This : in out Audio_CODEC; + Freq : Audio_Frequency); procedure Reset (This : in out Audio_CODEC); From d0e4e90bb3a3ea2c258b4364376f315aa87b7a1f Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Wed, 13 May 2026 12:33:09 -0500 Subject: [PATCH 18/22] Add ability to specify the incoming sample bit width - Procedure Initialize gets an additional formal parameter. - New procedure Set_Sample_Width. - Minor code simplifications in package body. --- components/src/audio/W8994/wm8994.adb | 40 +++++++++++++++++++++------ components/src/audio/W8994/wm8994.ads | 26 +++++++++++++---- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/components/src/audio/W8994/wm8994.adb b/components/src/audio/W8994/wm8994.adb index 019709e09..b337203d2 100644 --- a/components/src/audio/W8994/wm8994.adb +++ b/components/src/audio/W8994/wm8994.adb @@ -77,7 +77,8 @@ package body WM8994 is Input : Input_Device; Output : Output_Device; Volume : Volume_Level; - Frequency : Audio_Frequency) + Frequency : Audio_Frequency; + Bit_Width : Audio_Sample_Width) is Power_Mgnt_Reg_1 : UInt16 := 0; begin @@ -132,8 +133,8 @@ package body WM8994 is This.Set_Frequency (Frequency); - -- AIF1 Word Length = 16-bits, AIF1 Format = I2S (Default Register Value) - I2C_Write (This, WM8994_AIF1_Control1, 16#4010#); + This.Set_Sample_Width (Bit_Width); + -- slave mode I2C_Write (This, WM8994_AIF1_Master_Slave, 16#0000#); @@ -147,11 +148,11 @@ package body WM8994 is -- headphone (if active). Each call ORs its power bits into -- Power_Mgnt_Reg_1 before writing PWR_Management_1, so that both -- sets of bits are present if both outputs are enabled. - if Output = Speaker or else Output = Both then + if Output in Speaker | Both then Enable_Speaker_Output (This, Power_Mgnt_Reg_1); end if; - if Output = Headphone or else Output = Auto or else Output = Both then + if Output in Headphone | Auto | Both then Enable_Headphone_Output (This, Power_Mgnt_Reg_1, Include_Speaker => Output = Both); end if; @@ -179,9 +180,7 @@ package body WM8994 is ------------- function Chip_ID (This : in out Audio_CODEC) return UInt16 is - begin - return I2C_Read (This, WM8994_CHIPID_ADDR); - end Chip_ID; + (I2C_Read (This, WM8994_CHIPID_ADDR)); ---------- -- Play -- @@ -620,4 +619,29 @@ package body WM8994 is I2C_Write (This, WM8994_AIF1_ADC1_Filters, 16#1800#); end Enable_Line_Input; + --------------------- + -- Set_Sample_Width -- + --------------------- + + procedure Set_Sample_Width + (This : in out Audio_CODEC; + Bit_Width : Audio_Sample_Width) + is + -- AIF1_WL[1:0] occupies bits 6:5 of AIF1_Control1 (Register 0300h). + -- All other bits (format, source select, boost, invert) are preserved + -- via read-modify-write. + -- Per WM8994_Rev4.6, p.177, Register R768 (0300h), bits 6:5: + -- 00 = 16 bits, 01 = 20 bits, 10 = 24 bits, 11 = 32 bits. + AIF1_WL_Mask : constant UInt16 := not 16#0060#; + WL_Bits : constant array (Audio_Sample_Width) of UInt16 := + (Audio_16_Bits => 16#0000#, + Audio_20_Bits => 16#0020#, + Audio_24_Bits => 16#0040#, + Audio_32_Bits => 16#0060#); + Current : constant UInt16 := I2C_Read (This, WM8994_AIF1_Control1); + begin + I2C_Write (This, WM8994_AIF1_Control1, + (Current and AIF1_WL_Mask) or WL_Bits (Bit_Width)); + end Set_Sample_Width; + end WM8994; diff --git a/components/src/audio/W8994/wm8994.ads b/components/src/audio/W8994/wm8994.ads index 4bbcc0004..8da1a8cec 100644 --- a/components/src/audio/W8994/wm8994.ads +++ b/components/src/audio/W8994/wm8994.ads @@ -89,6 +89,12 @@ package WM8994 is Audio_Freq_88kHz => 88_200, Audio_Freq_96kHz => 96_000); + type Audio_Sample_Width is + (Audio_16_Bits, + Audio_20_Bits, + Audio_24_Bits, + Audio_32_Bits); + Max_Volume : constant := 16#3F#; subtype Volume_Level is UInt16 range 0 .. Max_Volume; @@ -98,7 +104,8 @@ package WM8994 is Input : Input_Device; Output : Output_Device; Volume : Volume_Level; - Frequency : Audio_Frequency); + Frequency : Audio_Frequency; + Bit_Width : Audio_Sample_Width); type Mute_Mode is (Mute_On, @@ -130,14 +137,19 @@ package WM8994 is (This : in out Audio_CODEC; Mode : Stop_Mode); - procedure Set_Volume - (This : in out Audio_CODEC; - Volume : Volume_Level); - procedure Set_Mute (This : in out Audio_CODEC; Mode : Mute_Mode); + procedure Reset (This : in out Audio_CODEC); + + -- The following procedures allow changes during execution but are not + -- required because procedure Initialize sets their values. + + procedure Set_Volume + (This : in out Audio_CODEC; + Volume : Volume_Level); + procedure Set_Output_Device (This : in out Audio_CODEC; Device : Output_Device); @@ -146,7 +158,9 @@ package WM8994 is (This : in out Audio_CODEC; Freq : Audio_Frequency); - procedure Reset (This : in out Audio_CODEC); + procedure Set_Sample_Width + (This : in out Audio_CODEC; + Bit_Width : Audio_Sample_Width); private From 71923192a1becead098958bc3158e6aea370d549 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Wed, 13 May 2026 13:06:36 -0500 Subject: [PATCH 19/22] Update due to new WM889 CODEC ability to set sample bit width. * In package Audio spec: - Define new type type Audio_Bit_Width - Add comment to the type Audio_Buffer indicating that it is unnecessarily restrictive (to 16 bits) - In procedure Initialize, added new formal parameter for the bit width, and a comment that currently it must be 16 bits, due to component size of Audio_Buffer. * in package Audio body: - Procedure Initialize now passes the new bit width parameter to WM8994.Initialize --- boards/stm32_common/stm32f746disco/audio.adb | 4 +++- boards/stm32_common/stm32f746disco/audio.ads | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/boards/stm32_common/stm32f746disco/audio.adb b/boards/stm32_common/stm32f746disco/audio.adb index 4ab71c0c0..101a3cd84 100644 --- a/boards/stm32_common/stm32f746disco/audio.adb +++ b/boards/stm32_common/stm32f746disco/audio.adb @@ -77,6 +77,7 @@ package body Audio is (This : in out WM8994_Audio_CODEC; Volume : Audio_Volume; Frequency : Audio_Frequency; + Bit_Width : Audio_Bit_Width; Sink : Audio_Outputs) is begin @@ -98,7 +99,8 @@ package body Audio is (Input => WM8994.No_Input, Output => WM8994.Output_Device (Sink), Volume => As_Device_Volume (Volume), - Frequency => WM8994.Audio_Frequency (Frequency)); + Frequency => WM8994.Audio_Frequency (Frequency), + Bit_Width => WM8994.Audio_Sample_Width (Bit_Width)); This.Sink := WM8994.Output_Device (Sink); -- For sake of any individual calls to Set_Frequency, assuming the STM diff --git a/boards/stm32_common/stm32f746disco/audio.ads b/boards/stm32_common/stm32f746disco/audio.ads index e10b9e4e8..a6e1159c5 100644 --- a/boards/stm32_common/stm32f746disco/audio.ads +++ b/boards/stm32_common/stm32f746disco/audio.ads @@ -50,20 +50,25 @@ package Audio is type Audio_Frequency is new WM8994.Audio_Frequency; + type Audio_Bit_Width is new WM8994.Audio_Sample_Width; + type Audio_Volume is range 0 .. 100; -- a percentage type Audio_Buffer is array (Natural range <>) of Integer_16 with Component_Size => 16, Alignment => 2; + -- TODO: change the component to a signed 32-bit quantity, so that any + -- sample bit width up to 32 bits could be supported. Otherwise we can + -- only support 16-bit samples. See procedure Initialize below. procedure Initialize (This : in out WM8994_Audio_CODEC; Volume : Audio_Volume; Frequency : Audio_Frequency; + Bit_Width : Audio_Bit_Width; -- must be 16 bits due to Audio_Buffer component size Sink : Audio_Outputs); - -- This routine initializes the hardware and configures the volume, - -- sampling frequency, and output device (the sink). This routine must be - -- called, before any others. The routines for setting the volume and - -- the output frequency are optional. + -- This routine initializes the hardware and configures the volume, sampling + -- frequency, output device (the sink), and the sampling bit width. This + -- routine must be called, before any others. procedure Play (This : in out WM8994_Audio_CODEC; From c25011bff8884aeb6dc05e5a39d81c75f702546f Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Thu, 14 May 2026 13:12:15 -0500 Subject: [PATCH 20/22] No need for Audio_Device to be aliased --- boards/stm32_common/stm32f746disco/stm32-board.ads | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/stm32_common/stm32f746disco/stm32-board.ads b/boards/stm32_common/stm32f746disco/stm32-board.ads index b1c61221b..3dd3b9fe0 100644 --- a/boards/stm32_common/stm32f746disco/stm32-board.ads +++ b/boards/stm32_common/stm32f746disco/stm32-board.ads @@ -160,7 +160,7 @@ package STM32.Board is Audio_DMA_Out_Stream : DMA_Stream_Selector renames Stream_4; Audio_DMA_Out_Channel : DMA_Channel_Selector renames Channel_3; - Audio_Device : aliased Audio.WM8994_Audio_CODEC (Audio_I2C'Access); + Audio_Device : Audio.WM8994_Audio_CODEC (Audio_I2C'Access); -------------------------- -- micro SD card reader -- From b78d3158635d594eba4ecf01890e3bb6976ddde6 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Thu, 14 May 2026 13:13:30 -0500 Subject: [PATCH 21/22] In Audio.Set_Audio_Clock, add support for missing frequencies Specifically, 12kHz, 24khz, and 88kHz, which were unsupported and signalled as such via explicit Program_Error raise. --- boards/stm32_common/stm32f746disco/audio.adb | 21 ++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/boards/stm32_common/stm32f746disco/audio.adb b/boards/stm32_common/stm32f746disco/audio.adb index 101a3cd84..414fe4cd3 100644 --- a/boards/stm32_common/stm32f746disco/audio.adb +++ b/boards/stm32_common/stm32f746disco/audio.adb @@ -167,17 +167,22 @@ package body Audio is PLLI2SQ => 2, -- SAI Clk(First level) = 214.5 MHz PLLI2SDIVQ => 19); -- I2S Clk = 215.4 / 19 = 11.289 MHz - when Audio_Freq_8kHz | Audio_Freq_16kHz | - Audio_Freq_48kHz | Audio_Freq_96kHz => + when Audio_Freq_8kHz | Audio_Freq_12kHz | Audio_Freq_16kHz | + Audio_Freq_24kHz | Audio_Freq_48kHz | Audio_Freq_96kHz => Configure_SAI_I2S_Clock (Audio_SAI, PLLI2SN => 344, -- VCO Output = 344MHz PLLI2SQ => 7, -- SAI Clk(First level) = 49.142 MHz PLLI2SDIVQ => 1); -- I2S Clk = 49.142 MHz - when Audio_Freq_12kHz | Audio_Freq_24kHz | Audio_Freq_88kHz => - raise Program_Error with "freq not yet implemented"; - -- FIXME! + when Audio_Freq_88kHz => + -- Best rational approx of 88200*256 Hz = 22.5792 MHz from 1 MHz VCO; + -- 271/4/3 = 22.583 MHz, MCKDIV=0 -> FS = 88216 Hz (0.018% error) + Configure_SAI_I2S_Clock + (Audio_SAI, + PLLI2SN => 271, -- VCO Output = 271MHz + PLLI2SQ => 4, -- SAI Clk(First level) = 67.75 MHz + PLLI2SDIVQ => 3); -- SAI Clk = 22.583 MHz end case; end Set_Audio_Clock; @@ -295,9 +300,9 @@ package body Audio is Clock_Speed => 100_000); end Initialize_Audio_I2C; - ------------------- - -- Start_Playing -- - ------------------- + ---------- + -- Play -- + ---------- procedure Play (This : in out WM8994_Audio_CODEC; From 95ac63d96a72b54ecb09a30afb0486a4196e6415 Mon Sep 17 00:00:00 2001 From: Pat Rogers Date: Thu, 14 May 2026 14:53:08 -0500 Subject: [PATCH 22/22] Updates for sake of changes to WM8994 package. * Package spec STM32.Board: - Update the audio device object's type name to WM8994_Audio_CODEC For package Audio: - The package spec is updated and is now identical to that of the 746G, so the primary ADT type name is changed and thus is changed in the parameter profiles. New definitions for frequencies and bit width are given, now based on the WM8994 types. - New frequencies are supported, based on those of the WM8994. - The package body is updated accordingly, but is largely the same as before. --- boards/stm32_common/stm32f769disco/audio.adb | 123 ++++++++++++------ boards/stm32_common/stm32f769disco/audio.ads | 95 ++++++++++---- .../stm32f769disco/stm32-board.ads | 2 +- 3 files changed, 156 insertions(+), 64 deletions(-) diff --git a/boards/stm32_common/stm32f769disco/audio.adb b/boards/stm32_common/stm32f769disco/audio.adb index 8d4be7299..e8c914043 100644 --- a/boards/stm32_common/stm32f769disco/audio.adb +++ b/boards/stm32_common/stm32f769disco/audio.adb @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2016, AdaCore -- +-- Copyright (C) 2016-2026, AdaCore -- -- -- -- Redistribution and use in source and binary forms, with or without -- -- modification, are permitted provided that the following conditions are -- @@ -32,7 +32,6 @@ -- @author MCD Application Team -- ------------------------------------------------------------------------------ -with HAL; use HAL; with STM32; use STM32; with STM32.Device; use STM32.Device; with STM32.Board; use STM32.Board; @@ -62,23 +61,28 @@ package body Audio is -- SAI_In_Block : SAI_Block renames Block_B; procedure Set_Audio_Clock (Freq : Audio_Frequency); + procedure Initialize_Audio_Out_Pins; - procedure Initialize_SAI_Out (Freq : Audio_Frequency); + + procedure Initialize_SAI_Out (Freq : Audio_Frequency; Sink : Audio_Outputs); + + function As_Device_Volume (Input : Audio_Volume) return WM8994.Volume_Level; + -- Converts the percentage input value to the WM8994-specific range + procedure Initialize_Audio_I2C; --------------------- -- Set_Audio_Clock -- --------------------- - procedure Set_Audio_Clock (Freq : Audio_Frequency) - is + procedure Set_Audio_Clock (Freq : Audio_Frequency) is begin - -- Two groups of frequencies: the 44kHz family and the 48kHz family - -- The Actual audio frequency is calculated then with the following + -- Two groups of frequencies: the 44kHz family and the 48kHz family. + -- The actual audio frequency is calculated with the following -- formula: -- Master_Clock = 256 * FS = SAI_CK / Master_Clock_Divider -- We need to find a value of SAI_CK that allows such integer master - -- clock divider + -- clock divider. case Freq is when Audio_Freq_11kHz | Audio_Freq_22kHz | Audio_Freq_32kHz | Audio_Freq_44kHz => @@ -89,13 +93,22 @@ package body Audio is PLLI2SQ => 2, -- SAI Clk(First level) = 214.5 MHz PLLI2SDIVQ => 19); -- I2S Clk = 215.4 / 19 = 11.289 MHz - when Audio_Freq_8kHz | Audio_Freq_16kHz | - Audio_Freq_48kHz | Audio_Freq_96kHz => + when Audio_Freq_8kHz | Audio_Freq_12kHz | Audio_Freq_16kHz | + Audio_Freq_24kHz | Audio_Freq_48kHz | Audio_Freq_96kHz => Configure_SAI_I2S_Clock (Audio_SAI, PLLI2SN => 344, -- VCO Output = 344MHz PLLI2SQ => 7, -- SAI Clk(First level) = 49.142 MHz - PLLI2SDIVQ => 1); -- I2S Clk = 49.142 MHz + PLLI2SDIVQ => 1); -- I2S Clk = 49.142 MHz + + when Audio_Freq_88kHz => + -- Best rational approx of 88200*256 Hz = 22.5792 MHz from 1 MHz VCO; + -- 271/4/3 = 22.583 MHz, MCKDIV=0 -> FS = 88216 Hz (0.018% error) + Configure_SAI_I2S_Clock + (Audio_SAI, + PLLI2SN => 271, -- VCO Output = 271MHz + PLLI2SQ => 4, -- SAI Clk(First level) = 67.75 MHz + PLLI2SDIVQ => 3); -- SAI Clk = 22.583 MHz end case; end Set_Audio_Clock; @@ -103,8 +116,7 @@ package body Audio is -- Initialize_Audio_Out_Pins -- ------------------------------- - procedure Initialize_Audio_Out_Pins - is + procedure Initialize_Audio_Out_Pins is begin Enable_Clock (Audio_SAI); Enable_Clock (SAI_Pins); @@ -143,10 +155,14 @@ package body Audio is -- Initialize_SAI_Out -- ------------------------ - procedure Initialize_SAI_Out (Freq : Audio_Frequency) + procedure Initialize_SAI_Out + (Freq : Audio_Frequency; + Sink : Audio_Outputs) is + Active_Slots : SAI_Slots; begin STM32.SAI.Disable (Audio_SAI, SAI_Out_Block); + STM32.SAI.Configure_Audio_Block (Audio_SAI, SAI_Out_Block, @@ -161,6 +177,7 @@ package body Audio is Synchronization => Asynchronous_Mode, Output_Drive => Drive_Immediate, FIFO_Threshold => FIFO_1_Quarter_Full); + STM32.SAI.Configure_Block_Frame (Audio_SAI, SAI_Out_Block, @@ -169,13 +186,24 @@ package body Audio is Frame_Sync => FS_Frame_And_Channel_Identification, FS_Polarity => FS_Active_Low, FS_Offset => Before_First_Bit); + + case Sink is + when Headphone => + Active_Slots := Slot_0 or Slot_2; + when Speaker => + Active_Slots := Slot_1 or Slot_3; + when Both => + Active_Slots := Slot_0 or Slot_1 or Slot_2 or Slot_3; + end case; + STM32.SAI.Configure_Block_Slot (Audio_SAI, SAI_Out_Block, First_Bit_Offset => 0, Slot_Size => Data_Size, Number_Of_Slots => 4, - Enabled_Slots => Slot_0 or Slot_2); + Enabled_Slots => Active_Slots); + STM32.SAI.Enable (Audio_SAI, SAI_Out_Block); end Initialize_SAI_Out; @@ -183,8 +211,7 @@ package body Audio is -- Initialize_Audio_I2C -- -------------------------- - procedure Initialize_Audio_I2C - is + procedure Initialize_Audio_I2C is begin if not STM32.I2C.Is_Configured (Audio_I2C) then STM32.Setup.Setup_I2C_Master (Port => Audio_I2C, @@ -200,10 +227,12 @@ package body Audio is -- Initialize -- ---------------- - procedure Initialize_Audio_Out - (This : in out WM8994_Audio_Device; + procedure Initialize + (This : in out WM8994_Audio_CODEC; Volume : Audio_Volume; - Frequency : Audio_Frequency) + Frequency : Audio_Frequency; + Bit_Width : Audio_Bit_Width; -- must be 16 bits due to Audio_Buffer component size + Sink : Audio_Outputs) is begin STM32.SAI.Deinitialize (Audio_SAI, SAI_Out_Block); @@ -212,31 +241,36 @@ package body Audio is -- Initialize the SAI Initialize_Audio_Out_Pins; - Initialize_SAI_Out (Frequency); + Initialize_SAI_Out (Frequency, Sink); -- Initialize the I2C Port to send commands to the driver Initialize_Audio_I2C; - if This.Device.Read_ID /= WM8994.WM8994_ID then - raise Constraint_Error with "Invalid ID received from the Audio Code"; + if This.Device.Chip_ID /= WM8994.WM8994_ID then + raise Constraint_Error with "Invalid ID received from the Audio CODEC"; end if; This.Device.Reset; - This.Device.Init + This.Device.Initialize (Input => WM8994.No_Input, Output => WM8994.Auto, - Volume => UInt8 (Volume), - Frequency => - WM8994.Audio_Frequency'Enum_Val - (Audio_Frequency'Enum_Rep (Frequency))); - end Initialize_Audio_Out; + Volume => As_Device_Volume (Volume), + Frequency => WM8994.Audio_Frequency (Frequency), + Bit_Width => WM8994.Audio_Sample_Width (Bit_Width)); + + This.Sink := WM8994.Output_Device (Sink); + -- For sake of any individual calls to Set_Frequency, assuming the STM + -- code is correct that we also need to set the clocks when setting the + -- frequency + + end Initialize; ---------- -- Play -- ---------- procedure Play - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Buffer : Audio_Buffer) is begin @@ -263,7 +297,7 @@ package body Audio is -- Pause -- ----------- - procedure Pause (This : in out WM8994_Audio_Device) is + procedure Pause (This : in out WM8994_Audio_CODEC) is begin This.Device.Pause; DMA_Pause (Audio_SAI, SAI_Out_Block); @@ -273,8 +307,7 @@ package body Audio is -- Resume -- ------------ - procedure Resume (This : in out WM8994_Audio_Device) - is + procedure Resume (This : in out WM8994_Audio_CODEC) is begin This.Device.Resume; DMA_Resume (Audio_SAI, SAI_Out_Block); @@ -284,8 +317,7 @@ package body Audio is -- Stop -- ---------- - procedure Stop (This : in out WM8994_Audio_Device) - is + procedure Stop (This : in out WM8994_Audio_CODEC) is begin This.Device.Stop (WM8994.Stop_Power_Down_Sw); DMA_Stop (Audio_SAI, SAI_Out_Block); @@ -299,11 +331,11 @@ package body Audio is ---------------- procedure Set_Volume - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Volume : Audio_Volume) is begin - This.Device.Set_Volume (UInt8 (Volume)); + This.Device.Set_Volume (As_Device_Volume (Volume)); end Set_Volume; ------------------- @@ -311,15 +343,26 @@ package body Audio is ------------------- procedure Set_Frequency - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Frequency : Audio_Frequency) is - pragma Unreferenced (This); + use type WM8994.Output_Device; begin + if This.Sink = WM8994.No_Output then + raise Constraint_Error with "No prior call to Initialize"; + end if; Set_Audio_Clock (Frequency); STM32.SAI.Disable (Audio_SAI, SAI_Out_Block); - Initialize_SAI_Out (Frequency); + Initialize_SAI_Out (Frequency, Audio_Outputs (This.Sink)); STM32.SAI.Enable (Audio_SAI, SAI_Out_Block); end Set_Frequency; + ---------------------- + -- As_Device_Volume -- + ---------------------- + + function As_Device_Volume (Input : Audio_Volume) return WM8994.Volume_Level is + (if Input = 100 then WM8994.Max_Volume + else UInt16 (Input) * WM8994.Max_Volume / 100); + end Audio; diff --git a/boards/stm32_common/stm32f769disco/audio.ads b/boards/stm32_common/stm32f769disco/audio.ads index 286abddf9..a6e1159c5 100644 --- a/boards/stm32_common/stm32f769disco/audio.ads +++ b/boards/stm32_common/stm32f769disco/audio.ads @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2016, AdaCore -- +-- Copyright (C) 2016-2026, AdaCore -- -- -- -- Redistribution and use in source and binary forms, with or without -- -- modification, are permitted provided that the following conditions are -- @@ -32,50 +32,99 @@ -- @author MCD Application Team -- ------------------------------------------------------------------------------ -with HAL.Audio; use HAL.Audio; -with HAL.I2C; use HAL.I2C; -with Ravenscar_Time; +with HAL; use HAL; +with HAL.I2C; use HAL.I2C; +with Interfaces; use Interfaces; +with WM8994; -private with WM8994; +private with Ravenscar_Time; package Audio is - type WM8994_Audio_Device (Port : not null Any_I2C_Port) is + type WM8994_Audio_CODEC (Port : not null Any_I2C_Port) is tagged limited private; - procedure Initialize_Audio_Out - (This : in out WM8994_Audio_Device; - Volume : Audio_Volume; - Frequency : Audio_Frequency); + type Audio_Outputs is new WM8994.Output_Device with + Static_Predicate => Audio_Outputs in + Headphone | Speaker | Both; - procedure Set_Volume - (This : in out WM8994_Audio_Device; - Volume : Audio_Volume); + type Audio_Frequency is new WM8994.Audio_Frequency; - procedure Set_Frequency - (This : in out WM8994_Audio_Device; - Frequency : Audio_Frequency); + type Audio_Bit_Width is new WM8994.Audio_Sample_Width; + + type Audio_Volume is range 0 .. 100; -- a percentage + + type Audio_Buffer is array (Natural range <>) of Integer_16 + with Component_Size => 16, Alignment => 2; + -- TODO: change the component to a signed 32-bit quantity, so that any + -- sample bit width up to 32 bits could be supported. Otherwise we can + -- only support 16-bit samples. See procedure Initialize below. + + procedure Initialize + (This : in out WM8994_Audio_CODEC; + Volume : Audio_Volume; + Frequency : Audio_Frequency; + Bit_Width : Audio_Bit_Width; -- must be 16 bits due to Audio_Buffer component size + Sink : Audio_Outputs); + -- This routine initializes the hardware and configures the volume, sampling + -- frequency, output device (the sink), and the sampling bit width. This + -- routine must be called, before any others. procedure Play - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Buffer : Audio_Buffer); + -- Start playing content from the specified buffer. The effect is to tell + -- the underlying WM8994 CODEC where the buffer to be played is located, + -- and cause the CODEC to start playing the contents. + -- + -- NB: playing continues after the call returns. An additional mechanism, + -- outside this package, updates the content of the buffer while the CODEC + -- is playing it. That update/play process continues until either there is + -- no more music to be played, or Stop or Pause is called. procedure Pause - (This : in out WM8994_Audio_Device); + (This : in out WM8994_Audio_CODEC); + -- After calling Pause, only Resume should be called for resuming play (do + -- not call Start_Playing again). procedure Resume - (This : in out WM8994_Audio_Device); + (This : in out WM8994_Audio_CODEC); + -- Procedure Resume should be called only when the audio is playing or + -- paused (not stopped). procedure Stop - (This : in out WM8994_Audio_Device); + (This : in out WM8994_Audio_CODEC); + -- Stops the hardware and update/play process. Once called, you must call + -- Start_Playing again if you want to restart the output. + + procedure Set_Volume + (This : in out WM8994_Audio_CODEC; + Volume : Audio_Volume); + + procedure Set_Frequency + (This : in out WM8994_Audio_CODEC; + Frequency : Audio_Frequency); private Audio_I2C_Addr : constant I2C_Address := 16#34#; - type WM8994_Audio_Device (Port : not null Any_I2C_Port) is - tagged limited record - Device : WM8994.WM8994_Device (Port, Audio_I2C_Addr, Ravenscar_Time.Delays); + type WM8994_Audio_CODEC + (Port : not null Any_I2C_Port) + is tagged limited record + Device : WM8994.Audio_CODEC (Port, Audio_I2C_Addr, Ravenscar_Time.Delays); + Sink : WM8994.Output_Device := WM8994.No_Output; + -- The initial value of Sink is overwritten by Initialize. The value + -- No_Output will trigger a C_E if ever referenced, so it is used as a + -- check that Initialize has been called. We need the component Sink + -- itself for the sake of a clean parameter profile for Set_Frequency, + -- otherwise clients would have to pass another parameter to specify + -- the output device selection again (after having done so when calling + -- Initialize). That's because Set_Frequency needs to do enough hardware + -- re-initialization to accommodate the new frequency, but doing so + -- requires the output device selection so that the re-init can select + -- the active slots. Just activating all slots (as is done in the STM + -- C code) doesn't work (at least in the current Ada code). end record; end Audio; diff --git a/boards/stm32_common/stm32f769disco/stm32-board.ads b/boards/stm32_common/stm32f769disco/stm32-board.ads index 71883e7c8..81fce689f 100644 --- a/boards/stm32_common/stm32f769disco/stm32-board.ads +++ b/boards/stm32_common/stm32f769disco/stm32-board.ads @@ -186,7 +186,7 @@ package STM32.Board is Audio_DMA_In_Sream : DMA_Stream_Selector renames Stream_7; Audio_DMA_In_Channel : DMA_Channel_Selector renames Channel_0; - Audio_Device : Audio.WM8994_Audio_Device (Audio_I2C'Access); + Audio_Device : Audio.WM8994_Audio_CODEC (Audio_I2C'Access); ------------- -- SD Card --