Add WM8960 support (#133)

Patch in WM8960 support by using Circle ae22928 from the develop branch
Thanks @matemaciek and @rsta2
pull/159/head
probonopd 2 years ago committed by GitHub
parent 901151a001
commit addbd45282
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .github/workflows/build.yml
  2. 182
      src/patches/WM8960.diff

@ -20,6 +20,12 @@ jobs:
run: |
set -ex
git submodule update --init --recursive
- name: Use Circle develop branch for WM8960 support until it is merged upstream
run: |
set -ex
cd circle-stdlib/libs/circle
git checkout ae22928 # develop
cd -
- name: Install toolchains
run: |
set -ex

@ -0,0 +1,182 @@
diff --git a/include/circle/i2ssoundbasedevice.h b/include/circle/i2ssoundbasedevice.h
index 3cfe21a3..39dea460 100644
--- a/include/circle/i2ssoundbasedevice.h
+++ b/include/circle/i2ssoundbasedevice.h
@@ -26,6 +26,7 @@
#include <circle/gpiopin.h>
#include <circle/gpioclock.h>
#include <circle/dmasoundbuffers.h>
+#include <circle/logger.h>
#include <circle/types.h>
class CI2SSoundBaseDevice : public CSoundBaseDevice /// Low level access to the I2S sound device
@@ -98,7 +99,10 @@ class CI2SSoundBaseDevice : public CSoundBaseDevice /// Low level access to the
static unsigned RXCompletedHandler (boolean bStatus, u32 *pBuffer,
unsigned nChunkSize, void *pParam);
- boolean InitPCM51xx (u8 ucI2CAddress);
+ void LogWrite (TLogSeverity Severity, const char *pMessage, ...);
+ void DetectDAC ();
+ boolean InitDAC ();
+ template <size_t N> boolean SendAll (const u8 (&initBytes)[N][2]);
private:
CInterruptSystem *m_pInterruptSystem;
diff --git a/lib/i2ssoundbasedevice.cpp b/lib/i2ssoundbasedevice.cpp
index c5df60a1..0825031b 100644
--- a/lib/i2ssoundbasedevice.cpp
+++ b/lib/i2ssoundbasedevice.cpp
@@ -164,21 +164,13 @@ boolean CI2SSoundBaseDevice::Start (void)
&& m_pI2CMaster != 0
&& !m_bI2CInited)
{
- if (m_ucI2CAddress != 0)
- {
- if (!InitPCM51xx (m_ucI2CAddress)) // fixed address, must succeed
- {
- m_bError = TRUE;
+ DetectDAC ();
- return FALSE;
- }
- }
- else
+ if (!InitDAC ())
{
- if (!InitPCM51xx (0x4C)) // auto probing, ignore failure
- {
- InitPCM51xx (0x4D);
- }
+ m_bError = TRUE;
+
+ return FALSE;
}
m_bI2CInited = TRUE;
@@ -422,17 +414,51 @@ unsigned CI2SSoundBaseDevice::RXCompletedHandler (boolean bStatus, u32 *pBuffer,
return 0;
}
+void CI2SSoundBaseDevice::LogWrite (TLogSeverity Severity, const char *pMessage, ...)
+{
+ va_list var;
+ va_start (var, pMessage);
+ CLogger::Get ()->WriteV ("CI2SSoundBaseDevice", Severity, pMessage, var);
+ va_end (var);
+}
+
+void CI2SSoundBaseDevice::DetectDAC () {
+ if (m_ucI2CAddress != 0)
+ {
+ return; // No need to guess if address is provided
+ }
+ static const u8 knownAddresses[] = {0x1A, 0x4C, 0x4D};
+ for (auto &address : knownAddresses)
+ {
+ int written = m_pI2CMaster->Write (address, nullptr, 0);
+ LogWrite (LogNotice, "Scan result at i2c address %u: %d", address, written);
+ if (written == 0) {
+ m_ucI2CAddress = address;
+ return;
+ }
+ }
+}
+
+// For WM8960 i2c register is 7 bits and value is 9 bits,
+// so let's have a helper for packing this into two bytes
+#define SHIFT_BIT(r, v) {((v&0x0100)>>8) | (r<<1), (v&0xff)}
+
//
-// Taken from the file mt32pi.cpp from this project:
+// Based on the file mt32pi.cpp from this project:
//
// mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi
// Copyright (C) 2020-2021 Dale Whinham <daleyo@gmail.com>
//
// Licensed under GPLv3
//
-boolean CI2SSoundBaseDevice::InitPCM51xx (u8 ucI2CAddress)
+boolean CI2SSoundBaseDevice::InitDAC ()
{
- static const u8 initBytes[][2] =
+ if (m_ucI2CAddress == 0)
+ {
+ return TRUE; // No DAC, no need to init
+ }
+
+ static const u8 initBytesPCM51xx[][2] =
{
// Set PLL reference clock to BCK (set SREF to 001b)
{ 0x0d, 0x10 },
@@ -444,9 +470,70 @@ boolean CI2SSoundBaseDevice::InitPCM51xx (u8 ucI2CAddress)
{ 0x41, 0x04 }
};
+ // based on https://github.com/RASPIAUDIO/ULTRA/blob/main/ultra.c
+ static const u8 initBytesWM8960[][2] =
+ {
+ // reset
+ SHIFT_BIT(15, 0x000),
+ // Power
+ SHIFT_BIT(25, 0x1FC),
+ SHIFT_BIT(26, 0x1F9),
+ SHIFT_BIT(47, 0x03C),
+ // Clock PLL
+ SHIFT_BIT(4, 0x001),
+ SHIFT_BIT(52, 0x027),
+ SHIFT_BIT(53, 0x086),
+ SHIFT_BIT(54, 0x0C2),
+ SHIFT_BIT(55, 0x026),
+ // ADC/DAC
+ SHIFT_BIT(5, 0x000),
+ SHIFT_BIT(7, 0x002),
+ // ALC and Noise control
+ SHIFT_BIT(20, 0x0F9),
+ SHIFT_BIT(17, 0x1FB),
+ SHIFT_BIT(18, 0x000),
+ SHIFT_BIT(19, 0x032),
+ // OUT1 volume
+ SHIFT_BIT(2, 0x16F),
+ SHIFT_BIT(3, 0x16F),
+ //SPK volume
+ SHIFT_BIT(40, 0x17F),
+ SHIFT_BIT(41, 0x178),
+ SHIFT_BIT(51, 0x08D),
+ // input volume
+ SHIFT_BIT(0, 0x13F),
+ SHIFT_BIT(1, 0x13F),
+ // INPUTS
+ SHIFT_BIT(32, 0x138),
+ SHIFT_BIT(33, 0x138),
+ // OUTPUTS
+ SHIFT_BIT(49, 0x0F7),
+ SHIFT_BIT(10, 0x1FF),
+ SHIFT_BIT(11, 0x1FF),
+ SHIFT_BIT(34, 0x100),
+ SHIFT_BIT(37, 0x100)
+ };
+
+ switch (m_ucI2CAddress)
+ {
+ case 0x4C:
+ case 0x4D:
+ return SendAll(initBytesPCM51xx);
+
+ case 0x1A:
+ return SendAll(initBytesWM8960);
+
+ default:
+ LogWrite (LogError, "Don't know how to init device at i2c address %u", m_ucI2CAddress);
+ return FALSE;
+ }
+}
+
+template <size_t N> boolean CI2SSoundBaseDevice::SendAll (const u8 (&initBytes)[N][2])
+{
for (auto &command : initBytes)
{
- if ( m_pI2CMaster->Write (ucI2CAddress, &command, sizeof (command))
+ if ( m_pI2CMaster->Write (m_ucI2CAddress, &command, sizeof (command))
!= sizeof (command))
{
return FALSE;
Loading…
Cancel
Save