diff --git a/arch/ARM/STM32/drivers/sai/stm32-sai.ads b/arch/ARM/STM32/drivers/sai/stm32-sai.ads index db2b2692c..afb371f36 100644 --- a/arch/ARM/STM32/drivers/sai/stm32-sai.ads +++ b/arch/ARM/STM32/drivers/sai/stm32-sai.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 -- @@ -188,7 +188,7 @@ package STM32.SAI is (FS_Frame, FS_Frame_And_Channel_Identification) with Size => 1; - -- Meaningless and is not used in AC’97 or SPDIF audio block + -- Meaningless and is not used in AC'97 or SPDIF audio block -- configuration. It must be configured when the audio block is disabled. -- -- In case of FS_Frame_And_Channel_Identification, the number of slots @@ -206,7 +206,7 @@ package STM32.SAI is FS_Active_High) with Size => 1; -- It is used to configure the level of the start of frame on the FS - -- signal. It is meaningless and is not used in AC’97 or SPDIF audio + -- signal. It is meaningless and is not used in AC'97 or SPDIF audio -- block configuration. -- -- FS_Active_Low: FS is active low (falling edge) @@ -216,7 +216,7 @@ package STM32.SAI is (First_Bit, Before_First_Bit) with Size => 1; - -- Meaningless and is not used in AC’97 or SPDIF audio block + -- Meaningless and is not used in AC'97 or SPDIF audio block -- configuration. This bit must be configured when the audio block -- is disabled. -- First_Bit: FS is asserted on the first bit of the slot 0. @@ -231,7 +231,7 @@ package STM32.SAI is -- The slot size must be higher or equal to the data size. If this -- condition is not respected, the behavior of the SAI will be -- undetermined. - -- Ignored in AC’97 or SPDIF mode. + -- Ignored in AC'97 or SPDIF mode. -- Data_Size: The slot size is equivalent to the data size (specified in -- DS[3:0] in the SAI_xCR1 register). diff --git a/arch/ARM/STM32/drivers/stm32-dac.adb b/arch/ARM/STM32/drivers/stm32-dac.adb index 390be52ff..60d50d140 100644 --- a/arch/ARM/STM32/drivers/stm32-dac.adb +++ b/arch/ARM/STM32/drivers/stm32-dac.adb @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015, 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 -- @@ -115,11 +115,9 @@ package body STM32.DAC is when DAC_Resolution_12_Bits => case Alignment is when Left_Aligned => - This.DHR12L1.DACC1DHR := - UInt12 (Value and Max_12bit_Resolution); + This.DHR12L1.DACC1DHR := UInt12 (Value and Max_12bit_Resolution); when Right_Aligned => - This.DHR12R1.DACC1DHR := - UInt12 (Value and Max_12bit_Resolution); + This.DHR12R1.DACC1DHR := UInt12 (Value and Max_12bit_Resolution); end case; when DAC_Resolution_8_Bits => This.DHR8R1.DACC1DHR := UInt8 (Value and Max_8bit_Resolution); @@ -130,11 +128,9 @@ package body STM32.DAC is when DAC_Resolution_12_Bits => case Alignment is when Left_Aligned => - This.DHR12L2.DACC2DHR := - UInt12 (Value and Max_12bit_Resolution); + This.DHR12L2.DACC2DHR := UInt12 (Value and Max_12bit_Resolution); when Right_Aligned => - This.DHR12R2.DACC2DHR := - UInt12 (Value and Max_12bit_Resolution); + This.DHR12R2.DACC2DHR := UInt12 (Value and Max_12bit_Resolution); end case; when DAC_Resolution_8_Bits => This.DHR8R2.DACC2DHR := UInt8 (Value and Max_8bit_Resolution); @@ -143,6 +139,35 @@ package body STM32.DAC is end case; end Set_Output; + ----------------------------- + -- Set_Dual_Channel_Output -- + ----------------------------- + + procedure Set_Dual_Channel_Output + (This : in out Digital_To_Analog_Converter; + Channel_2_Data : UInt16; + Channel_1_Data : UInt16; + Resolution : DAC_Resolution; + Alignment : Data_Alignment) + is + begin + -- See RM0385 Rev 8, sections 16.5.9 .. 16.5.11 for these registers + case Resolution is + when DAC_Resolution_12_Bits => + case Alignment is + when Left_Aligned => + This.DHR12LD.DACC2DHR := UInt12 (Channel_2_Data and Max_12bit_Resolution); + This.DHR12LD.DACC1DHR := UInt12 (Channel_1_Data and Max_12bit_Resolution); + when Right_Aligned => + This.DHR12RD.DACC2DHR := UInt12 (Channel_2_Data and Max_12bit_Resolution); + This.DHR12RD.DACC1DHR := UInt12 (Channel_1_Data and Max_12bit_Resolution); + end case; + when DAC_Resolution_8_Bits => + This.DHR8RD.DACC2DHR := UInt8 (Channel_2_Data and Max_8bit_Resolution); + This.DHR8RD.DACC1DHR := UInt8 (Channel_1_Data and Max_8bit_Resolution); + end case; + end Set_Dual_Channel_Output; + ------------------------------------ -- Trigger_Conversion_By_Software -- ------------------------------------ @@ -194,21 +219,15 @@ package body STM32.DAC is when DAC_Resolution_12_Bits => case Alignment is when Left_Aligned => - This.DHR12LD.DACC1DHR := - UInt12 (Channel_1_Value and Max_12bit_Resolution); - This.DHR12LD.DACC2DHR := - UInt12 (Channel_2_Value and Max_12bit_Resolution); + This.DHR12LD.DACC1DHR := UInt12 (Channel_1_Value and Max_12bit_Resolution); + This.DHR12LD.DACC2DHR := UInt12 (Channel_2_Value and Max_12bit_Resolution); when Right_Aligned => - This.DHR12RD.DACC1DHR := - UInt12 (Channel_1_Value and Max_12bit_Resolution); - This.DHR12RD.DACC2DHR := - UInt12 (Channel_2_Value and Max_12bit_Resolution); + This.DHR12RD.DACC1DHR := UInt12 (Channel_1_Value and Max_12bit_Resolution); + This.DHR12RD.DACC2DHR := UInt12 (Channel_2_Value and Max_12bit_Resolution); end case; when DAC_Resolution_8_Bits => - This.DHR8RD.DACC1DHR := - UInt8 (Channel_1_Value and Max_8bit_Resolution); - This.DHR8RD.DACC2DHR := - UInt8 (Channel_2_Value and Max_8bit_Resolution); + This.DHR8RD.DACC1DHR := UInt8 (Channel_1_Value and Max_8bit_Resolution); + This.DHR8RD.DACC2DHR := UInt8 (Channel_2_Value and Max_8bit_Resolution); end case; end Set_Dual_Output_Voltages; @@ -682,10 +701,36 @@ package body STM32.DAC is when DAC_Resolution_8_Bits => Result := This.DHR8R2'Address; end case; - end case; return Result; end Data_Address; + ---------------------------------- + -- Data_Address_Dual_Conversion -- + ---------------------------------- + + function Data_Address_Dual_Conversion + (This : Digital_To_Analog_Converter; + Resolution : DAC_Resolution; + Alignment : Data_Alignment) + return Address + is + Result : Address; + begin + case Resolution is + when DAC_Resolution_12_Bits => + case Alignment is + when Left_Aligned => + Result := This.DHR12LD'Address; + when Right_Aligned => + Result := This.DHR12RD'Address; + end case; + when DAC_Resolution_8_Bits => + Result := This.DHR8RD'Address; + end case; + + return Result; + end Data_Address_Dual_Conversion; + end STM32.DAC; diff --git a/arch/ARM/STM32/drivers/stm32-dac.ads b/arch/ARM/STM32/drivers/stm32-dac.ads index dcaccb354..41f41a549 100644 --- a/arch/ARM/STM32/drivers/stm32-dac.ads +++ b/arch/ARM/STM32/drivers/stm32-dac.ads @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015, 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 -- @@ -51,7 +51,9 @@ package STM32.DAC is type DAC_Channel is (Channel_1, Channel_2); - -- Note that Channel 1 is tied to GPIO pin PA4, and Channel 2 to PA5 + -- Note that Channel 1 is tied to GPIO pin PA4, and Channel 2 to PA5. + -- Note that Channel 1 is mapped to DMA1 Stream 5 channel 7. + -- Note that Channel 2 is mapped on DMA1 Stream 6 channel 7. procedure Enable (This : in out Digital_To_Analog_Converter; @@ -88,6 +90,8 @@ package STM32.DAC is Max_8bit_Resolution : constant := 16#00FF#; type Data_Alignment is (Left_Aligned, Right_Aligned); + -- These only apply when using 12-bit resolution. For 8-bit resolution the + -- alignment is always right-aligned. procedure Set_Output (This : in out Digital_To_Analog_Converter; @@ -102,6 +106,18 @@ package STM32.DAC is -- the reference input voltage and the 'n' of Max_nbit_Counts is either 12 -- or 8. + procedure Set_Dual_Channel_Output + (This : in out Digital_To_Analog_Converter; + Channel_2_Data : UInt16; + Channel_1_Data : UInt16; + Resolution : DAC_Resolution; + Alignment : Data_Alignment); + -- This routine writes the 32-bit value, composed from the two 16-bit + -- values, with the necessary layout (8/12left-right alignment) to + -- the appropriate output register for conversion on both channels + -- simultaneously. DMA is the alternative to software calling this + -- procedure. + procedure Trigger_Conversion_By_Software (This : in out Digital_To_Analog_Converter; Channel : DAC_Channel) @@ -328,7 +344,17 @@ package STM32.DAC is Alignment : Data_Alignment) return Address; -- Returns the address of the Data Holding register within This, for the - -- specified Channel, at the specified Resolution and Alignment. + -- specified Channel, given the specified Resolution and Alignment. + -- + -- This function is stricly for use with DMA, all others use the API above. + + function Data_Address_Dual_Conversion + (This : Digital_To_Analog_Converter; + Resolution : DAC_Resolution; + Alignment : Data_Alignment) + return Address; + -- Returns the address of the Data Holding register within This, for the + -- dual conversion case, given the specified Resolution and Alignment. -- -- This function is stricly for use with DMA, all others use the API above. diff --git a/arch/ARM/STM32/drivers/stm32-rng-polling.adb b/arch/ARM/STM32/drivers/stm32-rng-polling.adb index 29282b9f4..47f367eff 100644 --- a/arch/ARM/STM32/drivers/stm32-rng-polling.adb +++ b/arch/ARM/STM32/drivers/stm32-rng-polling.adb @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015, 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 -- @@ -50,9 +50,7 @@ package body STM32.RNG.Polling is begin Enable_RNG_Clock; Enable_RNG; - - -- Discard the first randomly generated number, according to STM32F4 - -- docs. + -- Discard the first generated number, according to STM32F4 docs. Discard := Random; end Initialize_RNG; @@ -61,10 +59,17 @@ package body STM32.RNG.Polling is ------------ function Random return UInt32 is + Max_Attempts : constant := 1000; + -- The above is arbitrary. It needs to be long enough that normal + -- behavior will not be flagged. Longer than that is fine. The point + -- is to not hang forever. begin - while not RNG_Data_Ready loop - null; - end loop; + Await_Ready : for K in 1 .. Max_Attempts loop + exit when RNG_Data_Ready; + if K = Max_Attempts then + raise Program_Error with "RNG data not ready"; + end if; + end loop Await_Ready; return RNG_Data; end Random; diff --git a/arch/ARM/STM32/svd/stm32f7x/stm32_svd-ethernet.ads b/arch/ARM/STM32/svd/stm32f7x/stm32_svd-ethernet.ads index c3366b409..95b7a35ce 100644 --- a/arch/ARM/STM32/svd/stm32f7x/stm32_svd-ethernet.ads +++ b/arch/ARM/STM32/svd/stm32f7x/stm32_svd-ethernet.ads @@ -411,11 +411,11 @@ package STM32_SVD.Ethernet is -- no description available DAIF : Boolean := False; -- no description available - RAM : Boolean := False; + PAM : Boolean := False; -- was RAM, incorrectly named in original SVD (see corrected SVD file) -- no description available BFD : Boolean := False; -- no description available - PCF : Boolean := False; + PCF : HAL.UInt2 := 0; -- incorrectly set to 1 bit in original SVD (see corrected SVD file) -- no description available SAIF : Boolean := False; -- no description available @@ -423,7 +423,7 @@ package STM32_SVD.Ethernet is -- no description available HPF : Boolean := False; -- unspecified - Reserved_10_30 : HAL.UInt21 := 16#0#; + Reserved_11_30 : HAL.UInt20 := 16#0#; -- incorrectly set to 21 bits in original SVD (see corrected SVD file) -- no description available RA : Boolean := False; end record @@ -435,13 +435,13 @@ package STM32_SVD.Ethernet is HU at 0 range 1 .. 1; HM at 0 range 2 .. 2; DAIF at 0 range 3 .. 3; - RAM at 0 range 4 .. 4; + PAM at 0 range 4 .. 4; BFD at 0 range 5 .. 5; - PCF at 0 range 6 .. 6; - SAIF at 0 range 7 .. 7; - SAF at 0 range 8 .. 8; - HPF at 0 range 9 .. 9; - Reserved_10_30 at 0 range 10 .. 30; + PCF at 0 range 6 .. 7; + SAIF at 0 range 8 .. 8; + SAF at 0 range 9 .. 9; + HPF at 0 range 10 .. 10; + Reserved_11_30 at 0 range 11 .. 30; RA at 0 range 31 .. 31; end record; @@ -484,7 +484,7 @@ package STM32_SVD.Ethernet is -- Ethernet MAC MII data register type MACMIIDR_Register is record -- no description available - TD : MACMIIDR_TD_Field := 16#0#; + MD : MACMIIDR_TD_Field := 16#0#; -- was TD, incorrectly named in original SVD (see corrected SVD file) -- unspecified Reserved_16_31 : HAL.UInt16 := 16#0#; end record @@ -492,7 +492,7 @@ package STM32_SVD.Ethernet is Bit_Order => System.Low_Order_First; for MACMIIDR_Register use record - TD at 0 range 0 .. 15; + MD at 0 range 0 .. 15; Reserved_16_31 at 0 range 16 .. 31; end record; @@ -594,34 +594,51 @@ package STM32_SVD.Ethernet is WFFRPR at 0 range 31 .. 31; end record; - -- Ethernet MAC debug register + -- Ethernet MAC debug register. The package from SVD does not correspond + -- to the documentation. Therefore we change this register definition + -- manually. See RM0385 Rev 8 pages 1610 and 1611. + -- TODO: update the SVD file so this manual change isn't necessary! type MACDBGR_Register is record - -- Read-only. CR - CR : Boolean; - -- Read-only. CSR - CSR : Boolean; - -- Read-only. ROR - ROR : Boolean; - -- Read-only. MCF - MCF : Boolean; - -- Read-only. MCP - MCP : Boolean; - -- Read-only. MCFHP - MCFHP : Boolean; - -- unspecified - Reserved_6_31 : HAL.UInt26; + Reserved_26_31 : HAL.UInt6; + Tx_FIFO_Full : Boolean; -- TFF + Tx_FIFO_Not_Empty : Boolean; -- TFNE + Reserved_23 : HAL.Bit := 0; + Tx_FIFO_Write_Active : Boolean; -- TFWA + Tx_FIFO_Read_Status : HAL.UInt2; -- TFRS + MAC_Transmitter_Paused : Boolean; -- MTP + MAC_Transmit_Frame_Controller_Status : HAL.UInt2; -- MTFCS + MAC_MII_Transmit_Engine_Active : Boolean; -- MMTEA + Reserved_10_15 : HAL.UInt6 := 0; + Rx_FIFO_Fill_Level : HAL.UInt2; -- RFFL + Reserved_7 : HAL.Bit := 0; + Rx_FIFO_Read_Controller_Status : HAL.UInt2; -- RFRCS + Rx_FIFO_Write_Controller_Active : Boolean; -- RFWRA + Reserved_3 : HAL.Bit := 0; + MAC_Small_FIFO_RW_Controller_Status : HAL.UInt2; -- MSFRWCS + MAC_MII_Receive_Protocol_Engine_Active : Boolean; -- MMRPEA end record - with Volatile_Full_Access, Size => 32, + with Volatile_Full_Access, + Size => 32, Bit_Order => System.Low_Order_First; for MACDBGR_Register use record - CR at 0 range 0 .. 0; - CSR at 0 range 1 .. 1; - ROR at 0 range 2 .. 2; - MCF at 0 range 3 .. 3; - MCP at 0 range 4 .. 4; - MCFHP at 0 range 5 .. 5; - Reserved_6_31 at 0 range 6 .. 31; + Reserved_26_31 at 0 range 26 .. 31; + Tx_FIFO_Full at 0 range 25 .. 25; + Tx_FIFO_Not_Empty at 0 range 24 .. 24; + Reserved_23 at 0 range 23 .. 23; + Tx_FIFO_Write_Active at 0 range 22 .. 22; + Tx_FIFO_Read_Status at 0 range 20 .. 21; + MAC_Transmitter_Paused at 0 range 19 .. 19; + MAC_Transmit_Frame_Controller_Status at 0 range 17 .. 18; + MAC_MII_Transmit_Engine_Active at 0 range 16 .. 16; + Reserved_10_15 at 0 range 10 .. 15; + Rx_FIFO_Fill_Level at 0 range 8 .. 9; + Reserved_7 at 0 range 7 .. 7; + Rx_FIFO_Read_Controller_Status at 0 range 5 .. 6; + Rx_FIFO_Write_Controller_Active at 0 range 4 .. 4; + Reserved_3 at 0 range 3 .. 3; + MAC_Small_FIFO_RW_Controller_Status at 0 range 1 .. 2; + MAC_MII_Receive_Protocol_Engine_Active at 0 range 0 .. 0; end record; -- Ethernet MAC interrupt status register diff --git a/arch/ARM/cortex_m/src/cache/cortex_m-cache.adb b/arch/ARM/cortex_m/src/cache/cortex_m-cache.adb index eada3cf3c..b8238d680 100644 --- a/arch/ARM/cortex_m/src/cache/cortex_m-cache.adb +++ b/arch/ARM/cortex_m/src/cache/cortex_m-cache.adb @@ -1,7 +1,7 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015-2016, 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 -- @@ -90,24 +90,24 @@ package body Cortex_M.Cache is if not D_Cache_Enabled then return; end if; - declare function To_U32 is new Ada.Unchecked_Conversion (System.Address, UInt32); - - Op_Size : Integer_32 := Integer_32 (Len); - Op_Addr : UInt32 := To_U32 (Start); - Reg : UInt32 with Volatile, Address => Reg_Address; - + Aligned_Addr : constant UInt32 := To_U32 (Start) and not (Data_Cache_Line_Size - 1); + Aligned_Size : constant UInt32 := UInt32 (Len) + (To_U32 (Start) - Aligned_Addr); + Op_Size : Integer_32 := Integer_32 (Aligned_Size); + -- Op_Size is extended by the number of bytes trimmed from the start, + -- ensuring the final partial line is covered + Op_Addr : UInt32 := Aligned_Addr; + -- Op_Addr starts at Start rounded down to the nearest cache line + Reg : UInt32 with Volatile, Address => Reg_Address; begin DSB; - while Op_Size > 0 loop Reg := Op_Addr; Op_Addr := Op_Addr + Data_Cache_Line_Size; Op_Size := Op_Size - Integer_32 (Data_Cache_Line_Size); end loop; - DSB; ISB; end; diff --git a/boards/stm32_common/dma2d/stm32-dma2d_bitmap.adb b/boards/stm32_common/dma2d/stm32-dma2d_bitmap.adb index aa7b44ee7..6c2e3d2be 100644 --- a/boards/stm32_common/dma2d/stm32-dma2d_bitmap.adb +++ b/boards/stm32_common/dma2d/stm32-dma2d_bitmap.adb @@ -153,7 +153,8 @@ package body STM32.DMA2D_Bitmap is X => Area.Position.X, Y => Area.Position.Y, Width => Area.Width, - Height => Area.Height); + Height => Area.Height, + Synchronous => True); else DMA2D_Fill_Rect (DMA_Buf, @@ -161,7 +162,8 @@ package body STM32.DMA2D_Bitmap is X => Area.Position.Y, Y => Buffer.Width - Area.Position.X - Area.Width, Width => Area.Height, - Height => Area.Width); + Height => Area.Width, + Synchronous => True); end if; else Parent (Buffer).Fill_Rect (Area); diff --git a/boards/stm32_common/ltdc/framebuffer_ltdc.adb b/boards/stm32_common/ltdc/framebuffer_ltdc.adb index b27d5540a..c49af8282 100644 --- a/boards/stm32_common/ltdc/framebuffer_ltdc.adb +++ b/boards/stm32_common/ltdc/framebuffer_ltdc.adb @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015-2016, 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 -- @@ -32,6 +32,7 @@ with STM32.DMA2D.Interrupt; with STM32.DMA2D.Polling; with STM32.SDRAM; use STM32.SDRAM; +with Cortex_M.Cache; package body Framebuffer_LTDC is @@ -377,12 +378,18 @@ package body Framebuffer_LTDC is null; when 1 => Display.Buffers (LCD_Layer, 2).Wait_Transfer; + Cortex_M.Cache.Clean_DCache + (Display.Buffers (LCD_Layer, 2).Addr, + Display.Buffers (LCD_Layer, 2).Buffer_Size); STM32.LTDC.Set_Frame_Buffer (Layer => LCD_Layer, Addr => Display.Buffers (LCD_Layer, 2).Addr); Display.Current (LCD_Layer) := 2; when 2 => Display.Buffers (LCD_Layer, 1).Wait_Transfer; + Cortex_M.Cache.Clean_DCache + (Display.Buffers (LCD_Layer, 1).Addr, + Display.Buffers (LCD_Layer, 1).Buffer_Size); STM32.LTDC.Set_Frame_Buffer (Layer => LCD_Layer, Addr => Display.Buffers (LCD_Layer, 1).Addr); diff --git a/boards/stm32_common/sdram/stm32-sdram.adb b/boards/stm32_common/sdram/stm32-sdram.adb index 5e40c73bf..9b7250a24 100644 --- a/boards/stm32_common/sdram/stm32-sdram.adb +++ b/boards/stm32_common/sdram/stm32-sdram.adb @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015-2016, 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 -- @@ -141,9 +141,7 @@ package body STM32.SDRAM is SDRAM_Conf : FMC_SDRAM_Init_Config; SDCLK : constant Unsigned_32 := Unsigned_32 (STM32.Device.System_Clock_Frequencies.SYSCLK / 2); - SDPeriod_In_ns : constant Unsigned_32 := - 1_000_000_000 / SDCLK; - Refresh_Delay : Unsigned_32; + SDPeriod_In_Ns : constant Unsigned_32 := 1_000_000_000 / SDCLK; begin if Initialized then @@ -171,22 +169,19 @@ package body STM32.SDRAM is -- 100 MHz of SD clock frequency (200MHz / 2) -- 1 Clock cycle = 1 / 100MHz = 10ns - Refresh_Delay := - (SDRAM_Min_Delay_In_ns - SDPeriod_In_ns + 1) / SDPeriod_In_ns; - Timing_Conf := ( -- 2 Clock cycles for Load to Active delay LoadToActiveDelay => 2, - -- min = 60ns: 6 * 10.0 - ExitSelfRefreshDelay => FMC_SDRAM_Timing (Refresh_Delay), - - -- in range [42ns, 120k ns] => using 4 * 11.1 ns - SelfRefreshTime => 4, - - -- min = 60ns - RowCycleDelay => FMC_SDRAM_Timing (Refresh_Delay), + -- Each timing parameter is computed as a cycle count by dividing the + -- required minimum time (in ns) by the SDCLK period, using ceiling + -- division to ensure the minimum is always satisfied regardless of + -- clock frequency. The timing constants are defined in STM32.Board + -- and reflect the IS42S32400F SDRAM datasheet specifications. + ExitSelfRefreshDelay => FMC_SDRAM_Timing ((SDRAM_TXSR_In_Ns + SDPeriod_In_Ns - 1) / SDPeriod_In_Ns), + SelfRefreshTime => FMC_SDRAM_Timing ((SDRAM_TRAS_In_Ns + SDPeriod_In_Ns - 1) / SDPeriod_In_Ns), + RowCycleDelay => FMC_SDRAM_Timing ((SDRAM_TRC_In_Ns + SDPeriod_In_Ns - 1) / SDPeriod_In_Ns), -- min = 20ns WriteRecoveryTime => 2, @@ -241,8 +236,7 @@ package body STM32.SDRAM is begin Initialize; Rounded_Size := Amount + Align; - Rounded_Size := - Rounded_Size - Rounded_Size rem Align; + Rounded_Size := Rounded_Size - Rounded_Size rem Align; G_Base_Addr := G_Base_Addr + Rounded_Size; diff --git a/boards/stm32_common/stm32f746disco/audio.adb b/boards/stm32_common/stm32f746disco/audio.adb index fa481d98b..cefb2a307 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 -- @@ -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,105 @@ 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_Outputs); + 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_CODEC; + Volume : Audio_Volume; + Frequency : Audio_Frequency; + Sink : Audio_Outputs) + is + begin + STM32.SAI.Deinitialize (Audio_SAI, SAI_Out_Block); + + Set_Audio_Clock (Frequency); + + 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.Analog_Outputs (Sink), + Volume => As_Device_Volume (Volume), + Frequency => WM8994.Audio_Frequency (Frequency)); + + 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 + end Initialize; + + ---------------- + -- Set_Volume -- + ---------------- + + procedure Set_Volume + (This : in out WM8994_Audio_CODEC; + 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_CODEC; + Frequency : Audio_Frequency) + is + use type WM8994.Analog_Outputs; + begin + if This.Sink = 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 + -- 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, Audio_Outputs (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 +171,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 +203,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_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, @@ -158,6 +249,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 +258,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; @@ -180,8 +283,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,47 +293,12 @@ 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 -- - ---------- + ------------------- + -- Start_Playing -- + ------------------- procedure Play - (This : in out WM8994_Audio_Device; + (This : in out WM8994_Audio_CODEC; Buffer : Audio_Buffer) is begin @@ -258,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); @@ -268,8 +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); @@ -279,8 +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); @@ -289,32 +354,12 @@ package body Audio is STM32.DMA.Clear_All_Status (Audio_DMA, Audio_DMA_Out_Stream); end Stop; - ---------------- - -- Set_Volume -- - ---------------- + ---------------------- + -- As_Device_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 -- - ------------------- - - 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..d56ed81c7 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-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,94 @@ -- @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.Analog_Outputs 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_Volume is range 0 .. 100; -- a percentage + + type Audio_Buffer is array (Natural range <>) of Integer_16 + with Component_Size => 16, Alignment => 2; + + procedure Initialize + (This : in out WM8994_Audio_CODEC; + Volume : Audio_Volume; + Frequency : Audio_Frequency; + 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 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.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 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/stm32f746disco/stm32-board.adb b/boards/stm32_common/stm32f746disco/stm32-board.adb index 12fce7f83..9d37c76b7 100644 --- a/boards/stm32_common/stm32f746disco/stm32-board.adb +++ b/boards/stm32_common/stm32f746disco/stm32-board.adb @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015, 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 -- @@ -103,4 +103,30 @@ package body STM32.Board is Configure_IO (User_Button_Point, (Mode_In, Resistors => Floating)); end Configure_User_Button_GPIO; + ----------------------------- + -- Enable_RMII_Pins_Clocks -- + ----------------------------- + + procedure Enable_RMII_Pins_Clocks is + begin + Enable_Clock (GPIO_A); + Enable_Clock (GPIO_C); + Enable_Clock (GPIO_G); + end Enable_RMII_Pins_Clocks; + + ------------------------- + -- Configure_RMII_Pins -- + ------------------------- + + procedure Configure_RMII_Pins is + begin + Configure_IO (All_RMII_Pins, + Config => (Mode => Mode_AF, + Resistors => Floating, + AF_Output_Type => Push_Pull, + AF_Speed => Speed_100MHz, + AF => GPIO_AF_ETH_11)); + -- All are AF_11, per RM0385 Rev 8, pg 1531 + end Configure_RMII_Pins; + end STM32.Board; diff --git a/boards/stm32_common/stm32f746disco/stm32-board.ads b/boards/stm32_common/stm32f746disco/stm32-board.ads index 3bb8eb2f3..0d81b29f3 100644 --- a/boards/stm32_common/stm32f746disco/stm32-board.ads +++ b/boards/stm32_common/stm32f746disco/stm32-board.ads @@ -1,6 +1,6 @@ ------------------------------------------------------------------------------ -- -- --- Copyright (C) 2015-2018, 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 -- @@ -116,7 +116,9 @@ package STM32.Board is SDRAM_Read_Pipe : constant STM32.FMC.FMC_SDRAM_Read_Pipe_Delay := STM32.FMC.FMC_ReadPipe_Delay_0; SDRAM_Refresh_Cnt : constant := 1539; - SDRAM_Min_Delay_In_ns : constant := 60; + SDRAM_TRAS_In_Ns : constant := 42; -- IS42S32400F min self-refresh time + SDRAM_TRC_In_Ns : constant := 60; -- IS42S32400F min row cycle time + SDRAM_TXSR_In_Ns : constant := 70; -- IS42S32400F min exit self-refresh delay --------- -- I2C -- @@ -156,7 +158,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 -- @@ -204,4 +206,44 @@ package STM32.Board is -- for polling the button, and necessary for having the button generate -- interrupts. + -------------------------------------- + -- Ethernet Physical Layer I/O Pins -- + -------------------------------------- + + -- Serial Management Interface (SMI) comm pins for connecting the MAC with + -- the PHY for RMII. See RM0385 Rev 8, pg 1533. + RMII_REF_CLK : GPIO_Point renames PA1; + RMII_MDIO : GPIO_Point renames PA2; + RMII_MDC : GPIO_Point renames PC1; + + -- RMII pins. See RM0385 Rev 8, pp. 1536 and 1538 for overview, and the + -- actual board's User Manual (eg, UM1907 STM32F746G-DISCO Appendix A: + -- IO Assignment) for the pins. + RMII_CRS_DV : GPIO_Point renames PA7; + RMII_RXD0 : GPIO_Point renames PC4; + RMII_RXD1 : GPIO_Point renames PC5; + RMII_RXER : GPIO_Point renames PG2; + RMII_TX_EN : GPIO_Point renames PG11; + RMII_TXD0 : GPIO_Point renames PG13; + RMII_TXD1 : GPIO_Point renames PG14; + + -- Note: On this board, these are the only pins that can perform these + -- functions. + + All_RMII_Pins : constant GPIO_Points := (RMII_REF_CLK, + RMII_MDIO, + RMII_CRS_DV, + RMII_MDC, + RMII_RXD0, + RMII_RXD1, + RMII_RXER, + RMII_TX_EN, + RMII_TXD0, + RMII_TXD1); + + -- These need to be two separate routines due to hardware initialization + -- issues with the Ethernet device. + procedure Enable_RMII_Pins_Clocks; + procedure Configure_RMII_Pins; + end STM32.Board; diff --git a/components/src/audio/W8994/wm8994-io.adb b/components/src/audio/W8994/wm8994-io.adb new file mode 100644 index 000000000..0b4d8953f --- /dev/null +++ b/components/src/audio/W8994/wm8994-io.adb @@ -0,0 +1,86 @@ +------------------------------------------------------------------------------ +-- -- +-- 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 + + --------------- + -- I2C_Write -- + --------------- + + procedure I2C_Write + (This : in out Audio_CODEC; + 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 Audio_CODEC; + 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..5901cd273 --- /dev/null +++ b/components/src/audio/W8994/wm8994-io.ads @@ -0,0 +1,281 @@ +------------------------------------------------------------------------------ +-- -- +-- 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 Audio_CODEC; + Register : Register_Address; + Value : UInt16); + + function I2C_Read + (This : in out Audio_CODEC; + 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 860fe601d..12acfd7d0 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-2026, AdaCore -- -- -- -- Redistribution and use in source and binary forms, with or without -- -- modification, are permitted provided that the following conditions are -- @@ -29,90 +29,43 @@ -- -- ------------------------------------------------------------------------------ +with WM8994.IO; use WM8994.IO; + 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); + ---------------- + -- Initialize -- + ---------------- - --------------- - -- I2C_Write -- - --------------- - - procedure I2C_Write (This : in out WM8994_Device; - Reg : 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 => Reg, - Mem_Addr_Size => Memory_Size_16b, - Data => Data, - Status => Status); - - if Reg /= 0 then - Check := I2C_Read (This, Reg); - end if; - end I2C_Write; - - -------------- - -- I2C_Read -- - -------------- - - function I2C_Read (This : in out WM8994_Device; - Reg : UInt16) - return UInt16 - is - Status : I2C_Status; - Data : I2C_Data (1 .. 2); - Ret : UInt16; - begin - This.Port.Mem_Read - (Addr => This.I2C_Addr, - Mem_Addr => Reg, - Mem_Addr_Size => Memory_Size_16b, - Data => Data, - Status => Status); - Ret := Shift_Left (UInt16 (Data (1)), 8) or UInt16 (Data (2)); - - return Ret; - end I2C_Read; - - ---------- - -- Init -- - ---------- - - procedure Init - (This : in out WM8994_Device; + procedure Initialize + (This : in out Audio_CODEC; Input : Input_Device; - Output : Output_Device; - Volume : UInt8; + Output : Analog_Outputs; + 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 + -- 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#); -- 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 +82,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 +138,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,57 +220,57 @@ 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 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 Audio_CODEC) return UInt16 is begin - return This.I2C_Read (WM8994_CHIPID_ADDR); - end Read_ID; + return I2C_Read (This, WM8994_CHIPID_ADDR); + end Chip_ID; ---------- -- 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; @@ -326,20 +279,20 @@ 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); -- CODEC in powersave mode - I2C_Write (This, 16#02#, 16#01#); + I2C_Write (This, WM8994_PWR_Management_2, 16#01#); end Pause; ------------ -- 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; @@ -348,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 @@ -361,22 +314,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 0x0000 address register - I2C_Write (This, 16#0000#, 16#0000#); + -- Reset Codec + I2C_Write (This, WM8994_SW_Reset, 16#0000#); end if; end Stop; @@ -384,13 +337,10 @@ package body WM8994 is -- Set_Volume -- ---------------- - procedure Set_Volume (This : in out WM8994_Device; Volume : Volume_Level) + procedure Set_Volume + (This : in out Audio_CODEC; + 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 +349,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, WM8994_Left_Output_Vol, Volume or 16#140#); -- Right Headphone volume - I2C_Write (This, 16#1D#, Converted_Volume or 16#140#); + I2C_Write (This, WM8994_Right_Output_Vol, Volume or 16#140#); -- Left Speaker volume - I2C_Write (This, 16#26#, Converted_Volume or 16#140#); + I2C_Write (This, WM8994_SPK_Left_Vol, Volume or 16#140#); -- Right Speaker volume - I2C_Write (This, 16#27#, Converted_Volume or 16#140#); + I2C_Write (This, WM8994_SPK_Right_Vol, Volume or 16#140#); end if; end Set_Volume; @@ -416,20 +366,20 @@ 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 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; @@ -438,56 +388,57 @@ 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 Audio_CODEC; + Device : Analog_Outputs) is begin 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; @@ -495,32 +446,62 @@ package body WM8994 is -- Set_Frequency -- ------------------- - procedure Set_Frequency (This : in out WM8994_Device; - Freq : Audio_Frequency) + procedure Set_Frequency + (This : in out Audio_CODEC; + 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#); + 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, WM8994_AIF1_Rate, 16#0023#); + + when Audio_Freq_16kHz => + -- AIF1 Sample Rate = 16 (kHz), ratio=256 + 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, WM8994_AIF1_Rate, 16#0053#); + + when Audio_Freq_32kHz => + -- AIF1 Sample Rate = 32 (kHz), ratio=256 + 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, WM8994_AIF1_Rate, 16#0083#); + + when Audio_Freq_88kHz => + -- AIF1 Sample Rate = 88.2 (kHz), ratio=256 + I2C_Write (This, WM8994_AIF1_Rate, 16#0093#); + + when Audio_Freq_96kHz => + -- AIF1 Sample Rate = 96 (kHz), ratio=256 + I2C_Write (This, WM8994_AIF1_Rate, 16#00A3#); end case; end Set_Frequency; @@ -528,9 +509,9 @@ 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, 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 fe55d45b5..37bc3c6b0 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-2026, 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; @@ -37,90 +38,113 @@ with HAL.Time; package WM8994 is - type Output_Device is + type Audio_CODEC + (Port : not null Any_I2C_Port; + I2C_Addr : UInt10; + Time : not null HAL.Time.Any_Delays) + is tagged limited private; + + type Analog_Outputs is (No_Output, Speaker, Headphone, Both, Auto); + type Input_Device is (No_Input, Microphone, Input_Line); - WM8994_ID : constant := 16#8994#; - 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); - 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); 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). - - subtype Volume_Level is UInt8 range 0 .. 100; - - type WM8994_Device - (Port : not null Any_I2C_Port; - I2C_Addr : UInt10; - Time : not null HAL.Time.Any_Delays) - is tagged limited private; + -- 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. - procedure Init (This : in out WM8994_Device; - Input : Input_Device; - Output : Output_Device; - Volume : UInt8; - Frequency : Audio_Frequency); - - 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; + WM8994_ID : constant := 16#8994#; + + function Chip_ID (This : in out Audio_CODEC) return UInt16; + + procedure Play (This : in out Audio_CODEC); + + procedure Pause (This : in out Audio_CODEC); + + procedure Resume (This : in out Audio_CODEC); + + procedure Stop (This : in out Audio_CODEC; Cmd : Stop_Mode); + + procedure Set_Volume (This : in out Audio_CODEC; Volume : Volume_Level); + + procedure Set_Mute (This : in out Audio_CODEC; Cmd : Mute_Modes); + + procedure Set_Output_Mode (This : in out Audio_CODEC; + Device : Analog_Outputs); + + 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 (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) - return UInt16; + + type Audio_CODEC + (Port : not null Any_I2C_Port; + I2C_Addr : UInt10; + Time : not null HAL.Time.Any_Delays) + is tagged limited null record; end WM8994;