mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-25 18:47:49 +00:00
Merge remote-tracking branch 'upstream/master'
Base class for text entry
This commit is contained in:
312
firmware/common/ak4951.cpp
Normal file
312
firmware/common/ak4951.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ak4951.hpp"
|
||||
|
||||
#include "portapack_io.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include <ch.h>
|
||||
|
||||
namespace asahi_kasei {
|
||||
namespace ak4951 {
|
||||
|
||||
void AK4951::configure_digital_interface() {
|
||||
// Configure for external slave mode.
|
||||
map.r.mode_control_1.DIF = 0b11; // I2S compatible
|
||||
map.r.mode_control_1.BCKO = 0; // BICK = 32fs
|
||||
update(Register::ModeControl1);
|
||||
|
||||
map.r.mode_control_2.CM = 0b00; // MCKI = 256fs
|
||||
map.r.mode_control_2.FS = 0b1011; // fs = 48kHz
|
||||
update(Register::ModeControl2);
|
||||
|
||||
// map.r.mode_control_3.DVOLC = 1; // Control L/R channels with DVL (LchDigitalVolumeControl)
|
||||
// update(Register::ModeControl3);
|
||||
|
||||
map.r.power_management_2.MS = 0; // Slave mode
|
||||
map.r.power_management_2.PMPLL = 0; // EXT mode
|
||||
update(Register::PowerManagement2);
|
||||
}
|
||||
|
||||
void AK4951::init() {
|
||||
reset();
|
||||
|
||||
// Write dummy address to "release" the reset.
|
||||
write(0x00, 0x00);
|
||||
|
||||
configure_digital_interface();
|
||||
|
||||
map.r.power_management_1.PMVCM = 1;
|
||||
update(Register::PowerManagement1);
|
||||
|
||||
// Headphone output is hi-Z when not active, reduces crosstalk from speaker output.
|
||||
map.r.beep_control.HPZ = 1;
|
||||
update(Register::BeepControl);
|
||||
|
||||
// Pause for VCOM and REGFIL pins to stabilize.
|
||||
chThdSleepMilliseconds(2);
|
||||
|
||||
headphone_mute();
|
||||
|
||||
// SPK-Amp gain setting: SPKG1-0 bits = “00” → “01”
|
||||
map.r.signal_select_2.SPKG = 0b01;
|
||||
update(Register::SignalSelect2);
|
||||
|
||||
map.r.signal_select_3.MONO = 0b00;
|
||||
update(Register::SignalSelect3);
|
||||
|
||||
map.r.digital_filter_mode.PFSDO = 0; // ADC bypass digital filter block.
|
||||
map.r.digital_filter_mode.ADCPF = 1; // ADC output
|
||||
map.r.digital_filter_mode.PFDAC = 0b00; // SDTI
|
||||
update(Register::DigitalFilterMode);
|
||||
|
||||
// Set up FRN, FRATT and ADRST1-0 bits (Addr = 09H)
|
||||
// map.r.timer_select.FRN = 0;
|
||||
// map.r.timer_select.FRATT = 0;
|
||||
// map.r.timer_select.ADRST = 0b00;
|
||||
// update(Register::TimerSelect);
|
||||
|
||||
// Set up ALC mode (Addr = 0AH, 0BH)
|
||||
// map.r.alc_timer_select. = ;
|
||||
// update(Register::ALCTimerSelect);
|
||||
// map.r.alc_mode_control_1. = ;
|
||||
// update(Register::ALCModeControl1);
|
||||
|
||||
// Set up REF value of ALC (Addr = 0CH)
|
||||
// map.r.alc_mode_control_2. = ;
|
||||
// update(Register::ALCModeControl2);
|
||||
|
||||
// Set up IVOL value of ALC operation start (Addr = 0DH)
|
||||
// map.r.l_ch_input_volume_control. = ;
|
||||
// update(Register::LchInputVolumeControl);
|
||||
// map.r.r_ch_input_volume_control. = ;
|
||||
// update(Register::RchInputVolumeControl);
|
||||
|
||||
// Set up the output digital volume. (Addr = 13H)
|
||||
// set_headphone_volume(...);
|
||||
|
||||
// Set up Programmable Filter Path: PFDAC1-0 bits=“01”, PFSDO=ADCPF bits=“0” (Addr = 1DH)
|
||||
// map.r.digital_filter_mode.PFDAC = 0b01;
|
||||
// update(Register::DigitalFilterMode);
|
||||
}
|
||||
|
||||
bool AK4951::reset() {
|
||||
io.audio_reset_state(true);
|
||||
|
||||
// PDN# pulse must be >200ns
|
||||
chThdSleepMicroseconds(10);
|
||||
|
||||
io.audio_reset_state(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AK4951::set_digtal_volume_control(const reg_t value) {
|
||||
map.r.l_ch_digital_volume_control.DV = value;
|
||||
update(Register::LchDigitalVolumeControl);
|
||||
}
|
||||
|
||||
void AK4951::set_headphone_volume(const volume_t volume) {
|
||||
const auto normalized = headphone_gain_range().normalize(volume);
|
||||
auto n = normalized.centibel() / 5;
|
||||
set_digtal_volume_control(0xcb - n);
|
||||
}
|
||||
|
||||
void AK4951::headphone_mute() {
|
||||
set_digtal_volume_control(0xff);
|
||||
}
|
||||
|
||||
void AK4951::set_dac_power(const bool enable) {
|
||||
map.r.power_management_1.PMDAC = enable;
|
||||
update(Register::PowerManagement1);
|
||||
}
|
||||
|
||||
void AK4951::set_headphone_power(const bool enable) {
|
||||
map.r.power_management_2.PMHPL = map.r.power_management_2.PMHPR = enable;
|
||||
update(Register::PowerManagement2);
|
||||
}
|
||||
|
||||
void AK4951::set_speaker_power(const bool enable) {
|
||||
map.r.power_management_2.PMSL = enable;
|
||||
update(Register::PowerManagement2);
|
||||
}
|
||||
|
||||
void AK4951::select_line_out(const LineOutSelect value) {
|
||||
map.r.power_management_2.LOSEL = (value == LineOutSelect::Line) ? 1 : 0;
|
||||
update(Register::PowerManagement2);
|
||||
}
|
||||
|
||||
void AK4951::headphone_enable() {
|
||||
set_dac_power(true);
|
||||
set_headphone_power(true);
|
||||
|
||||
// Wait for headphone amplifier charge pump power-up.
|
||||
chThdSleepMilliseconds(35);
|
||||
}
|
||||
|
||||
void AK4951::headphone_disable() {
|
||||
set_headphone_power(false);
|
||||
set_dac_power(false);
|
||||
}
|
||||
|
||||
void AK4951::speaker_enable() {
|
||||
// Set up the path of DAC → SPK-Amp: DACS bit = “0” → “1”
|
||||
map.r.signal_select_1.DACS = 1;
|
||||
update(Register::SignalSelect1);
|
||||
|
||||
// Enter Speaker-Amp Output Mode: LOSEL bit = “0”
|
||||
select_line_out(LineOutSelect::Speaker);
|
||||
|
||||
// Power up DAC, Programmable Filter and Speaker-Amp: PMDAC=PMPFIL=PMSL bits=“0”→“1”
|
||||
set_dac_power(true);
|
||||
// map.r.power_management_1.PMPFIL = 1;
|
||||
// update(Register::PowerManagement1);
|
||||
set_speaker_power(true);
|
||||
|
||||
// Time from PMSL=1 to SLPSN=1.
|
||||
chThdSleepMilliseconds(1);
|
||||
|
||||
// Exit the power-save mode of Speaker-Amp: SLPSN bit = “0” → “1”
|
||||
map.r.signal_select_1.SLPSN = 1;
|
||||
update(Register::SignalSelect1);
|
||||
}
|
||||
|
||||
void AK4951::speaker_disable() {
|
||||
// Enter Speaker-Amp Power Save Mode: SLPSN bit = “1” → “0”
|
||||
map.r.signal_select_1.SLPSN = 0;
|
||||
update(Register::SignalSelect1);
|
||||
|
||||
// Disable the path of DAC → SPK-Amp: DACS bit = “1” → “0”
|
||||
map.r.signal_select_1.DACS = 0;
|
||||
update(Register::SignalSelect1);
|
||||
|
||||
// Power down DAC, Programmable Filter and speaker: PMDAC=PMPFIL=PMSL bits= “1”→“0”
|
||||
set_dac_power(false);
|
||||
// map.r.power_management_1.PMPFIL = 0;
|
||||
// update(Register::PowerManagement1);
|
||||
set_speaker_power(false);
|
||||
}
|
||||
|
||||
void AK4951::microphone_enable() {
|
||||
// map.r.digital_mic.DMIC = 0;
|
||||
// update(Register::DigitalMic);
|
||||
|
||||
map.r.signal_select_1.MGAIN20 = 0b110;
|
||||
map.r.signal_select_1.PMMP = 1;
|
||||
map.r.signal_select_1.MPSEL = 1; // MPWR2 pin
|
||||
map.r.signal_select_1.MGAIN3 = 0b0;
|
||||
update(Register::SignalSelect1);
|
||||
|
||||
map.r.signal_select_2.INL = 0b01; // Lch input signal = LIN2
|
||||
map.r.signal_select_2.INR = 0b01; // Rch input signal = RIN2
|
||||
map.r.signal_select_2.MICL = 0; // MPWR = 2.4V
|
||||
update(Register::SignalSelect2);
|
||||
|
||||
// map.r.r_ch_mic_gain_setting.MGR = 0x80; // Microphone sensitivity correction = 0dB.
|
||||
// update(Register::RchMicGainSetting);
|
||||
/*
|
||||
map.r.timer_select.FRN = ?;
|
||||
map.r.timer_select.FRATT = ?;
|
||||
map.r.timer_select.ADRST = 0b??;
|
||||
update(Register::TimerSelect);
|
||||
|
||||
map.r.alc_timer_select. = ?;
|
||||
update(Register::ALCTimerSelect);
|
||||
map.r.alc_mode_control_1. = ?;
|
||||
map.r.alc_mode_control_1.ALC = 1;
|
||||
update(Register::ALCModeControl1);
|
||||
|
||||
map.r.alc_mode_control_2.REF = ?;
|
||||
update(Register::ALCModeControl2);
|
||||
*/
|
||||
// map.r.l_ch_input_volume_control.IV = 0xe1;
|
||||
// update(Register::LchInputVolumeControl);
|
||||
// map.r.r_ch_input_volume_control.IV = 0xe1;
|
||||
// update(Register::RchInputVolumeControl);
|
||||
/*
|
||||
map.r.auto_hpf_control.STG = 0b00;
|
||||
map.r.auto_hpf_control.SENC = 0b011;
|
||||
map.r.auto_hpf_control.AHPF = 0;
|
||||
update(Register::AutoHPFControl);
|
||||
*/
|
||||
map.r.digital_filter_select_1.HPFAD = 1; // HPF1 (after ADC) = on
|
||||
map.r.digital_filter_select_1.HPFC = 0b11; // 2336.8 Hz @ fs=48k
|
||||
update(Register::DigitalFilterSelect1);
|
||||
/*
|
||||
map.r.digital_filter_select_2.HPF = 0;
|
||||
map.r.digital_filter_select_2.LPF = 0;
|
||||
map.r.digital_filter_select_2.FIL3 = 0;
|
||||
map.r.digital_filter_select_2.EQ0 = 0;
|
||||
map.r.digital_filter_select_2.GN = 0b00;
|
||||
update(Register::DigitalFilterSelect2);
|
||||
|
||||
map.r.digital_filter_select_3.EQ1 = 0;
|
||||
map.r.digital_filter_select_3.EQ2 = 0;
|
||||
map.r.digital_filter_select_3.EQ3 = 0;
|
||||
map.r.digital_filter_select_3.EQ4 = 0;
|
||||
map.r.digital_filter_select_3.EQ5 = 0;
|
||||
update(Register::DigitalFilterSelect3);
|
||||
*/
|
||||
map.r.digital_filter_mode.PFSDO = 0; // ADC (+ 1st order HPF) Output
|
||||
map.r.digital_filter_mode.ADCPF = 1; // ADC Output (default)
|
||||
update(Register::DigitalFilterMode);
|
||||
|
||||
// ... Set coefficients ...
|
||||
|
||||
map.r.power_management_1.PMADL = 1; // ADC Lch = Lch input signal
|
||||
map.r.power_management_1.PMADR = 1; // ADC Rch = Rch input signal
|
||||
map.r.power_management_1.PMPFIL = 0; // Programmable filter unused, routed around.
|
||||
update(Register::PowerManagement1);
|
||||
|
||||
// 1059/fs, 22ms @ 48kHz
|
||||
chThdSleepMilliseconds(22);
|
||||
}
|
||||
|
||||
void AK4951::microphone_disable() {
|
||||
map.r.power_management_1.PMADL = 0;
|
||||
map.r.power_management_1.PMADR = 0;
|
||||
map.r.power_management_1.PMPFIL = 0;
|
||||
update(Register::PowerManagement1);
|
||||
|
||||
map.r.alc_mode_control_1.ALC = 0;
|
||||
update(Register::ALCModeControl1);
|
||||
}
|
||||
|
||||
reg_t AK4951::read(const address_t reg_address) {
|
||||
const std::array<uint8_t, 1> tx { reg_address };
|
||||
std::array<uint8_t, 1> rx { 0x00 };
|
||||
bus.transmit(bus_address, tx.data(), tx.size());
|
||||
bus.receive(bus_address, rx.data(), rx.size());
|
||||
return rx[0];
|
||||
}
|
||||
|
||||
void AK4951::update(const Register reg) {
|
||||
write(toUType(reg), map.w[toUType(reg)]);
|
||||
}
|
||||
|
||||
void AK4951::write(const address_t reg_address, const reg_t value) {
|
||||
const std::array<uint8_t, 2> tx { reg_address, value };
|
||||
bus.transmit(bus_address, tx.data(), tx.size());
|
||||
}
|
||||
|
||||
} /* namespace ak4951 */
|
||||
} /* namespace asahi_kasei */
|
882
firmware/common/ak4951.hpp
Normal file
882
firmware/common/ak4951.hpp
Normal file
@@ -0,0 +1,882 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __AK4951_H__
|
||||
#define __AK4951_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "i2c_pp.hpp"
|
||||
|
||||
#include "audio.hpp"
|
||||
|
||||
namespace asahi_kasei {
|
||||
namespace ak4951 {
|
||||
|
||||
using address_t = uint8_t;
|
||||
using reg_t = uint8_t;
|
||||
|
||||
constexpr size_t reg_count = 0x50;
|
||||
|
||||
enum class Register : address_t {
|
||||
PowerManagement1 = 0x00,
|
||||
PowerManagement2 = 0x01,
|
||||
SignalSelect1 = 0x02,
|
||||
SignalSelect2 = 0x03,
|
||||
SignalSelect3 = 0x04,
|
||||
ModeControl1 = 0x05,
|
||||
ModeControl2 = 0x06,
|
||||
ModeControl3 = 0x07,
|
||||
DigitalMic = 0x08,
|
||||
TimerSelect = 0x09,
|
||||
ALCTimerSelect = 0x0a,
|
||||
ALCModeControl1 = 0x0b,
|
||||
ALCModeControl2 = 0x0c,
|
||||
LchInputVolumeControl = 0x0d,
|
||||
RchInputVolumeControl = 0x0e,
|
||||
ALCVolume = 0x0f,
|
||||
_Reserved_0x10 = 0x10,
|
||||
RchMicGainSetting = 0x11,
|
||||
BeepControl = 0x12,
|
||||
LchDigitalVolumeControl = 0x13,
|
||||
RchDigitalVolumeControl = 0x14,
|
||||
EQCommonGainSelect = 0x15,
|
||||
EQ2CommonGainSetting = 0x16,
|
||||
EQ3CommonGainSetting = 0x17,
|
||||
EQ4CommonGainSetting = 0x18,
|
||||
EQ5CommonGainSetting = 0x19,
|
||||
AutoHPFControl = 0x1a,
|
||||
DigitalFilterSelect1 = 0x1b,
|
||||
DigitalFilterSelect2 = 0x1c,
|
||||
DigitalFilterMode = 0x1d,
|
||||
HPF2Coefficient0 = 0x1e,
|
||||
HPF2Coefficient1 = 0x1f,
|
||||
HPF2Coefficient2 = 0x20,
|
||||
HPF2Coefficient3 = 0x21,
|
||||
LPFCoefficient0 = 0x22,
|
||||
LPFCoefficient1 = 0x23,
|
||||
LPFCoefficient2 = 0x24,
|
||||
LPFCoefficient3 = 0x25,
|
||||
FIL3Coefficient0 = 0x26,
|
||||
FIL3Coefficient1 = 0x27,
|
||||
FIL3Coefficient2 = 0x28,
|
||||
FIL3Coefficient3 = 0x29,
|
||||
EQCoefficient0 = 0x2a,
|
||||
EQCoefficient1 = 0x2b,
|
||||
EQCoefficient2 = 0x2c,
|
||||
EQCoefficient3 = 0x2d,
|
||||
EQCoefficient4 = 0x2e,
|
||||
EQCoefficient5 = 0x2f,
|
||||
DigitalFilterSelect3 = 0x30,
|
||||
DeviceInformation = 0x31,
|
||||
E1Coefficient0 = 0x32,
|
||||
E1Coefficient1 = 0x33,
|
||||
E1Coefficient2 = 0x34,
|
||||
E1Coefficient3 = 0x35,
|
||||
E1Coefficient4 = 0x36,
|
||||
E1Coefficient5 = 0x37,
|
||||
E2Coefficient0 = 0x38,
|
||||
E2Coefficient1 = 0x39,
|
||||
E2Coefficient2 = 0x3a,
|
||||
E2Coefficient3 = 0x3b,
|
||||
E2Coefficient4 = 0x3c,
|
||||
E2Coefficient5 = 0x3d,
|
||||
E3Coefficient0 = 0x3e,
|
||||
E3Coefficient1 = 0x3f,
|
||||
E3Coefficient2 = 0x40,
|
||||
E3Coefficient3 = 0x41,
|
||||
E3Coefficient4 = 0x42,
|
||||
E3Coefficient5 = 0x43,
|
||||
E4Coefficient0 = 0x44,
|
||||
E4Coefficient1 = 0x45,
|
||||
E4Coefficient2 = 0x46,
|
||||
E4Coefficient3 = 0x47,
|
||||
E4Coefficient4 = 0x48,
|
||||
E4Coefficient5 = 0x49,
|
||||
E5Coefficient0 = 0x4a,
|
||||
E5Coefficient1 = 0x4b,
|
||||
E5Coefficient2 = 0x4c,
|
||||
E5Coefficient3 = 0x4d,
|
||||
E5Coefficient4 = 0x4e,
|
||||
E5Coefficient5 = 0x4f,
|
||||
_count,
|
||||
};
|
||||
|
||||
static_assert(toUType(Register::_count) == reg_count, "Register::_count != reg_count");
|
||||
|
||||
struct PowerManagement1 {
|
||||
reg_t PMADL : 1;
|
||||
reg_t PMADR : 1;
|
||||
reg_t PMDAC : 1;
|
||||
reg_t reserved0 : 2;
|
||||
reg_t PMBP : 1;
|
||||
reg_t PMVCM : 1;
|
||||
reg_t PMPFIL : 1;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PowerManagement1) == sizeof(reg_t), "wrong size `struct");
|
||||
|
||||
struct PowerManagement2 {
|
||||
reg_t LOSEL : 1;
|
||||
reg_t PMSL : 1;
|
||||
reg_t PMPLL : 1;
|
||||
reg_t MS : 1;
|
||||
reg_t PMHPL : 1;
|
||||
reg_t PMHPR : 1;
|
||||
reg_t reserved0 : 1;
|
||||
reg_t PMOSC : 1;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PowerManagement2) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct SignalSelect1 {
|
||||
reg_t MGAIN20 : 3;
|
||||
reg_t PMMP : 1;
|
||||
reg_t MPSEL : 1;
|
||||
reg_t DACS : 1;
|
||||
reg_t MGAIN3 : 1;
|
||||
reg_t SLPSN : 1;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SignalSelect1) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct SignalSelect2 {
|
||||
reg_t INR : 2;
|
||||
reg_t INL : 2;
|
||||
reg_t MICL : 1;
|
||||
reg_t reserved0 : 1;
|
||||
reg_t SPKG : 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SignalSelect2) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct SignalSelect3 {
|
||||
reg_t MONO : 2;
|
||||
reg_t PTS : 2;
|
||||
reg_t reserved0 : 1;
|
||||
reg_t DACL : 1;
|
||||
reg_t LVCM : 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SignalSelect3) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct ModeControl1 {
|
||||
reg_t DIF : 2;
|
||||
reg_t CKOFF : 1;
|
||||
reg_t BCKO : 1;
|
||||
reg_t PLL : 4;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ModeControl1) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct ModeControl2 {
|
||||
reg_t FS : 4;
|
||||
reg_t reserved0 : 2;
|
||||
reg_t CM : 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ModeControl2) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct ModeControl3 {
|
||||
reg_t reserved0 : 2;
|
||||
reg_t IVOLC : 1;
|
||||
reg_t reserved1 : 1;
|
||||
reg_t DVOLC : 1;
|
||||
reg_t SMUTE : 1;
|
||||
reg_t THDET : 1;
|
||||
reg_t TSDSEL : 1;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ModeControl3) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct DigitalMIC {
|
||||
reg_t DMIC : 1;
|
||||
reg_t DCLKP : 1;
|
||||
reg_t reserved0 : 1;
|
||||
reg_t DCLKE : 1;
|
||||
reg_t PMDML : 1;
|
||||
reg_t PMDMR : 1;
|
||||
reg_t reserved1 : 1;
|
||||
reg_t READ : 1;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DigitalMIC) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct TimerSelect {
|
||||
reg_t DVTM : 1;
|
||||
reg_t MOFF : 1;
|
||||
reg_t reserved0 : 2;
|
||||
reg_t FRN : 1;
|
||||
reg_t FRATT : 1;
|
||||
reg_t ADRST : 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TimerSelect) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct ALCTimerSelect {
|
||||
reg_t RFST : 2;
|
||||
reg_t WTM : 2;
|
||||
reg_t EQFC : 2;
|
||||
reg_t IVTM : 1;
|
||||
reg_t reserved0 : 1;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ALCTimerSelect) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct ALCModeControl1 {
|
||||
reg_t LMTH10 : 2;
|
||||
reg_t RGAIN : 3;
|
||||
reg_t ALC : 1;
|
||||
reg_t LMTH2 : 1;
|
||||
reg_t ALCEQN : 1;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ALCModeControl1) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct ALCModeControl2 {
|
||||
reg_t REF : 8;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ALCModeControl2) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct InputVolumeControl {
|
||||
reg_t IV : 8;
|
||||
};
|
||||
|
||||
static_assert(sizeof(InputVolumeControl) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
using LchInputVolumeControl = InputVolumeControl;
|
||||
using RchInputVolumeControl = InputVolumeControl;
|
||||
|
||||
struct ALCVolume {
|
||||
reg_t VOL : 8;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ALCVolume) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct RchMICGainSetting {
|
||||
reg_t MGR : 8;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RchMICGainSetting) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct BeepControl {
|
||||
reg_t BPLVL : 4;
|
||||
reg_t BEEPH : 1;
|
||||
reg_t BEEPS : 1;
|
||||
reg_t BPVCM : 1;
|
||||
reg_t HPZ : 1;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BeepControl) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct DigitalVolumeControl {
|
||||
reg_t DV : 8;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DigitalVolumeControl) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
using LchDigitalVolumeControl = DigitalVolumeControl;
|
||||
using RchDigitalVolumeControl = DigitalVolumeControl;
|
||||
|
||||
struct EQCommonGainSelect {
|
||||
reg_t reserved0 : 1;
|
||||
reg_t EQC2 : 1;
|
||||
reg_t EQC3 : 1;
|
||||
reg_t EQC4 : 1;
|
||||
reg_t EQC5 : 1;
|
||||
reg_t reserved1 : 3;
|
||||
};
|
||||
|
||||
static_assert(sizeof(EQCommonGainSelect) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct EQCommonGainSetting {
|
||||
reg_t EQnT : 2;
|
||||
reg_t EQnG : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(EQCommonGainSetting) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
using EQ2CommonGainSetting = EQCommonGainSetting;
|
||||
using EQ3CommonGainSetting = EQCommonGainSetting;
|
||||
using EQ4CommonGainSetting = EQCommonGainSetting;
|
||||
using EQ5CommonGainSetting = EQCommonGainSetting;
|
||||
|
||||
struct AutoHPFControl {
|
||||
reg_t STG : 2;
|
||||
reg_t SENC : 3;
|
||||
reg_t AHPF : 1;
|
||||
reg_t reserved0 : 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(AutoHPFControl) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct DigitalFilterSelect1 {
|
||||
reg_t HPFAD : 1;
|
||||
reg_t HPFC : 2;
|
||||
reg_t reserved0 : 5;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DigitalFilterSelect1) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct DigitalFilterSelect2 {
|
||||
reg_t HPF : 1;
|
||||
reg_t LPF : 1;
|
||||
reg_t reserved0 : 2;
|
||||
reg_t FIL3 : 1;
|
||||
reg_t EQ0 : 1;
|
||||
reg_t GN : 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DigitalFilterSelect2) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct DigitalFilterMode {
|
||||
reg_t PFSDO : 1;
|
||||
reg_t ADCPF : 1;
|
||||
reg_t PFDAC : 2;
|
||||
reg_t PFVOL : 2;
|
||||
reg_t reserved0 : 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DigitalFilterMode) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct Coefficient14L {
|
||||
reg_t l : 8;
|
||||
};
|
||||
|
||||
struct Coefficient14H {
|
||||
reg_t h : 6;
|
||||
reg_t reserved0 : 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Coefficient14L) == sizeof(reg_t), "wrong size struct");
|
||||
static_assert(sizeof(Coefficient14H) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
using Coefficient16L = Coefficient14L;
|
||||
|
||||
struct Coefficient16H {
|
||||
reg_t h : 8;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Coefficient16H) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
using HPF2Coefficient0 = Coefficient14L;
|
||||
using HPF2Coefficient1 = Coefficient14H;
|
||||
using HPF2Coefficient2 = Coefficient14L;
|
||||
using HPF2Coefficient3 = Coefficient14H;
|
||||
|
||||
using LPFCoefficient0 = Coefficient14L;
|
||||
using LPFCoefficient1 = Coefficient14H;
|
||||
using LPFCoefficient2 = Coefficient14L;
|
||||
using LPFCoefficient3 = Coefficient14H;
|
||||
|
||||
using FIL3Coefficient0 = Coefficient14L;
|
||||
|
||||
struct FIL3Coefficient1 {
|
||||
reg_t h : 6;
|
||||
reg_t reserved0 : 1;
|
||||
reg_t s : 1;
|
||||
};
|
||||
|
||||
static_assert(sizeof(FIL3Coefficient1) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
using FIL3Coefficient2 = Coefficient14L;
|
||||
using FIL3Coefficient3 = Coefficient14H;
|
||||
|
||||
using EQCoefficient0 = Coefficient16L;
|
||||
using EQCoefficient1 = Coefficient16H;
|
||||
using EQCoefficient2 = Coefficient14L;
|
||||
using EQCoefficient3 = Coefficient14H;
|
||||
using EQCoefficient4 = Coefficient16L;
|
||||
using EQCoefficient5 = Coefficient16H;
|
||||
|
||||
struct DigitalFilterSelect3 {
|
||||
reg_t EQ1 : 1;
|
||||
reg_t EQ2 : 1;
|
||||
reg_t EQ3 : 1;
|
||||
reg_t EQ4 : 1;
|
||||
reg_t EQ5 : 1;
|
||||
reg_t reserved0 : 3;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DigitalFilterSelect3) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
struct DeviceInformation {
|
||||
reg_t DVN : 4;
|
||||
reg_t REV : 4;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DeviceInformation) == sizeof(reg_t), "wrong size struct");
|
||||
|
||||
using E1Coefficient0 = Coefficient16L;
|
||||
using E1Coefficient1 = Coefficient16H;
|
||||
using E1Coefficient2 = Coefficient16L;
|
||||
using E1Coefficient3 = Coefficient16H;
|
||||
using E1Coefficient4 = Coefficient16L;
|
||||
using E1Coefficient5 = Coefficient16H;
|
||||
|
||||
using E2Coefficient0 = Coefficient16L;
|
||||
using E2Coefficient1 = Coefficient16H;
|
||||
using E2Coefficient2 = Coefficient16L;
|
||||
using E2Coefficient3 = Coefficient16H;
|
||||
using E2Coefficient4 = Coefficient16L;
|
||||
using E2Coefficient5 = Coefficient16H;
|
||||
|
||||
using E3Coefficient0 = Coefficient16L;
|
||||
using E3Coefficient1 = Coefficient16H;
|
||||
using E3Coefficient2 = Coefficient16L;
|
||||
using E3Coefficient3 = Coefficient16H;
|
||||
using E3Coefficient4 = Coefficient16L;
|
||||
using E3Coefficient5 = Coefficient16H;
|
||||
|
||||
using E4Coefficient0 = Coefficient16L;
|
||||
using E4Coefficient1 = Coefficient16H;
|
||||
using E4Coefficient2 = Coefficient16L;
|
||||
using E4Coefficient3 = Coefficient16H;
|
||||
using E4Coefficient4 = Coefficient16L;
|
||||
using E4Coefficient5 = Coefficient16H;
|
||||
|
||||
using E5Coefficient0 = Coefficient16L;
|
||||
using E5Coefficient1 = Coefficient16H;
|
||||
using E5Coefficient2 = Coefficient16L;
|
||||
using E5Coefficient3 = Coefficient16H;
|
||||
using E5Coefficient4 = Coefficient16L;
|
||||
using E5Coefficient5 = Coefficient16H;
|
||||
|
||||
struct Register_Type {
|
||||
PowerManagement1 power_management_1;
|
||||
PowerManagement2 power_management_2;
|
||||
SignalSelect1 signal_select_1;
|
||||
SignalSelect2 signal_select_2;
|
||||
SignalSelect3 signal_select_3;
|
||||
ModeControl1 mode_control_1;
|
||||
ModeControl2 mode_control_2;
|
||||
ModeControl3 mode_control_3;
|
||||
DigitalMIC digital_mic;
|
||||
TimerSelect timer_select;
|
||||
ALCTimerSelect alc_timer_select;
|
||||
ALCModeControl1 alc_mode_control_1;
|
||||
ALCModeControl2 alc_mode_control_2;
|
||||
LchInputVolumeControl l_ch_input_volume_control;
|
||||
RchInputVolumeControl r_ch_input_volume_control;
|
||||
ALCVolume alc_volume;
|
||||
reg_t _reserved_0x10;
|
||||
RchMICGainSetting r_ch_mic_gain_setting;
|
||||
BeepControl beep_control;
|
||||
LchDigitalVolumeControl l_ch_digital_volume_control;
|
||||
RchDigitalVolumeControl r_ch_digital_volume_control;
|
||||
EQCommonGainSelect eq_common_gain_select;
|
||||
EQ2CommonGainSetting eq2_common_gain_setting;
|
||||
EQ3CommonGainSetting eq3_common_gain_setting;
|
||||
EQ4CommonGainSetting eq4_common_gain_setting;
|
||||
EQ5CommonGainSetting eq5_common_gain_setting;
|
||||
AutoHPFControl auto_hpf_control;
|
||||
DigitalFilterSelect1 digital_filter_select_1;
|
||||
DigitalFilterSelect2 digital_filter_select_2;
|
||||
DigitalFilterMode digital_filter_mode;
|
||||
HPF2Coefficient0 hpf_2_coefficient_0;
|
||||
HPF2Coefficient1 hpf_2_coefficient_1;
|
||||
HPF2Coefficient2 hpf_2_coefficient_2;
|
||||
HPF2Coefficient3 hpf_2_coefficient_3;
|
||||
LPFCoefficient0 lpf_coefficient_0;
|
||||
LPFCoefficient1 lpf_coefficient_1;
|
||||
LPFCoefficient2 lpf_coefficient_2;
|
||||
LPFCoefficient3 lpf_coefficient_3;
|
||||
FIL3Coefficient0 fil_3_coefficient_0;
|
||||
FIL3Coefficient1 fil_3_coefficient_1;
|
||||
FIL3Coefficient2 fil_3_coefficient_2;
|
||||
FIL3Coefficient3 fil_3_coefficient_3;
|
||||
EQCoefficient0 eq_coefficient_0;
|
||||
EQCoefficient1 eq_coefficient_1;
|
||||
EQCoefficient2 eq_coefficient_2;
|
||||
EQCoefficient3 eq_coefficient_3;
|
||||
EQCoefficient4 eq_coefficient_4;
|
||||
EQCoefficient5 eq_coefficient_5;
|
||||
DigitalFilterSelect3 digital_filter_select_3;
|
||||
DeviceInformation device_information;
|
||||
E1Coefficient0 e1_coefficient_0;
|
||||
E1Coefficient1 e1_coefficient_1;
|
||||
E1Coefficient2 e1_coefficient_2;
|
||||
E1Coefficient3 e1_coefficient_3;
|
||||
E1Coefficient4 e1_coefficient_4;
|
||||
E1Coefficient5 e1_coefficient_5;
|
||||
E2Coefficient0 e2_coefficient_0;
|
||||
E2Coefficient1 e2_coefficient_1;
|
||||
E2Coefficient2 e2_coefficient_2;
|
||||
E2Coefficient3 e2_coefficient_3;
|
||||
E2Coefficient4 e2_coefficient_4;
|
||||
E2Coefficient5 e2_coefficient_5;
|
||||
E3Coefficient0 e3_coefficient_0;
|
||||
E3Coefficient1 e3_coefficient_1;
|
||||
E3Coefficient2 e3_coefficient_2;
|
||||
E3Coefficient3 e3_coefficient_3;
|
||||
E3Coefficient4 e3_coefficient_4;
|
||||
E3Coefficient5 e3_coefficient_5;
|
||||
E4Coefficient0 e4_coefficient_0;
|
||||
E4Coefficient1 e4_coefficient_1;
|
||||
E4Coefficient2 e4_coefficient_2;
|
||||
E4Coefficient3 e4_coefficient_3;
|
||||
E4Coefficient4 e4_coefficient_4;
|
||||
E4Coefficient5 e4_coefficient_5;
|
||||
E5Coefficient0 e5_coefficient_0;
|
||||
E5Coefficient1 e5_coefficient_1;
|
||||
E5Coefficient2 e5_coefficient_2;
|
||||
E5Coefficient3 e5_coefficient_3;
|
||||
E5Coefficient4 e5_coefficient_4;
|
||||
E5Coefficient5 e5_coefficient_5;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size");
|
||||
|
||||
struct RegisterMap {
|
||||
constexpr RegisterMap(
|
||||
Register_Type values
|
||||
) : r(values)
|
||||
{
|
||||
}
|
||||
|
||||
union {
|
||||
Register_Type r;
|
||||
std::array<reg_t, reg_count> w;
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size");
|
||||
|
||||
constexpr RegisterMap default_after_reset { Register_Type {
|
||||
.power_management_1 = {
|
||||
.PMADL = 0,
|
||||
.PMADR = 0,
|
||||
.PMDAC = 0,
|
||||
.reserved0 = 0,
|
||||
.PMBP = 0,
|
||||
.PMVCM = 0,
|
||||
.PMPFIL = 0,
|
||||
},
|
||||
.power_management_2 = {
|
||||
.LOSEL = 0,
|
||||
.PMSL = 0,
|
||||
.PMPLL = 0,
|
||||
.MS = 0,
|
||||
.PMHPL = 0,
|
||||
.PMHPR = 0,
|
||||
.reserved0 = 0,
|
||||
.PMOSC = 0,
|
||||
},
|
||||
.signal_select_1 = {
|
||||
.MGAIN20 = 0b110,
|
||||
.PMMP = 0,
|
||||
.MPSEL = 0,
|
||||
.DACS = 0,
|
||||
.MGAIN3 = 0,
|
||||
.SLPSN = 0,
|
||||
},
|
||||
.signal_select_2 = {
|
||||
.INR = 0b00,
|
||||
.INL = 0b00,
|
||||
.MICL = 0,
|
||||
.reserved0 = 0,
|
||||
.SPKG = 0b00,
|
||||
},
|
||||
.signal_select_3 = {
|
||||
.MONO = 0b00,
|
||||
.PTS = 0b01,
|
||||
.reserved0 = 0,
|
||||
.DACL = 0,
|
||||
.LVCM = 0b01,
|
||||
},
|
||||
.mode_control_1 = {
|
||||
.DIF = 0b10,
|
||||
.CKOFF = 0,
|
||||
.BCKO = 0,
|
||||
.PLL = 0b0101,
|
||||
},
|
||||
.mode_control_2 = {
|
||||
.FS = 0b1011,
|
||||
.reserved0 = 0,
|
||||
.CM = 0b00,
|
||||
},
|
||||
.mode_control_3 = {
|
||||
.reserved0 = 0,
|
||||
.IVOLC = 1,
|
||||
.reserved1 = 0,
|
||||
.DVOLC = 1,
|
||||
.SMUTE = 0,
|
||||
.THDET = 0,
|
||||
.TSDSEL = 0,
|
||||
},
|
||||
.digital_mic = {
|
||||
.DMIC = 0,
|
||||
.DCLKP = 0,
|
||||
.reserved0 = 0,
|
||||
.DCLKE = 0,
|
||||
.PMDML = 0,
|
||||
.PMDMR = 1,
|
||||
.reserved1 = 0,
|
||||
.READ = 0,
|
||||
},
|
||||
.timer_select = {
|
||||
.DVTM = 0,
|
||||
.MOFF = 0,
|
||||
.reserved0 = 0,
|
||||
.FRN = 0,
|
||||
.FRATT = 0,
|
||||
.ADRST = 0b00,
|
||||
},
|
||||
.alc_timer_select = {
|
||||
.RFST = 0b00,
|
||||
.WTM = 0b00,
|
||||
.EQFC = 0b10,
|
||||
.IVTM = 1,
|
||||
.reserved0 = 0,
|
||||
},
|
||||
.alc_mode_control_1 = {
|
||||
.LMTH10 = 0b00,
|
||||
.RGAIN = 0b000,
|
||||
.ALC = 0,
|
||||
.LMTH2 = 0,
|
||||
.ALCEQN = 0,
|
||||
},
|
||||
.alc_mode_control_2 = {
|
||||
.REF = 0xe1,
|
||||
},
|
||||
.l_ch_input_volume_control = {
|
||||
.IV = 0xe1,
|
||||
},
|
||||
.r_ch_input_volume_control = {
|
||||
.IV = 0xe1,
|
||||
},
|
||||
.alc_volume = {
|
||||
.VOL = 0x00, // Read-only.
|
||||
},
|
||||
._reserved_0x10 = 0x80,
|
||||
.r_ch_mic_gain_setting = {
|
||||
.MGR = 0x80,
|
||||
},
|
||||
.beep_control = {
|
||||
.BPLVL = 0b0000,
|
||||
.BEEPH = 0,
|
||||
.BEEPS = 0,
|
||||
.BPVCM = 0,
|
||||
.HPZ = 0,
|
||||
},
|
||||
.l_ch_digital_volume_control = {
|
||||
.DV = 0x18,
|
||||
},
|
||||
.r_ch_digital_volume_control = {
|
||||
.DV = 0x18,
|
||||
},
|
||||
.eq_common_gain_select = {
|
||||
.reserved0 = 0,
|
||||
.EQC2 = 0,
|
||||
.EQC3 = 0,
|
||||
.EQC4 = 0,
|
||||
.EQC5 = 0,
|
||||
.reserved1 = 0,
|
||||
},
|
||||
.eq2_common_gain_setting = {
|
||||
.EQnT = 0b00,
|
||||
.EQnG = 0b000000,
|
||||
},
|
||||
.eq3_common_gain_setting = {
|
||||
.EQnT = 0b00,
|
||||
.EQnG = 0b000000,
|
||||
},
|
||||
.eq4_common_gain_setting = {
|
||||
.EQnT = 0b00,
|
||||
.EQnG = 0b000000,
|
||||
},
|
||||
.eq5_common_gain_setting = {
|
||||
.EQnT = 0b00,
|
||||
.EQnG = 0b000000,
|
||||
},
|
||||
.auto_hpf_control = {
|
||||
.STG = 0b00,
|
||||
.SENC = 0b011,
|
||||
.AHPF = 0,
|
||||
.reserved0 = 0,
|
||||
},
|
||||
.digital_filter_select_1 = {
|
||||
.HPFAD = 1,
|
||||
.HPFC = 0b00,
|
||||
.reserved0 = 0,
|
||||
},
|
||||
.digital_filter_select_2 = {
|
||||
.HPF = 0,
|
||||
.LPF = 0,
|
||||
.reserved0 = 0,
|
||||
.FIL3 = 0,
|
||||
.EQ0 = 0,
|
||||
.GN = 0b00,
|
||||
},
|
||||
.digital_filter_mode = {
|
||||
.PFSDO = 1,
|
||||
.ADCPF = 1,
|
||||
.PFDAC = 0b00,
|
||||
.PFVOL = 0b00,
|
||||
.reserved0 = 0,
|
||||
},
|
||||
|
||||
.hpf_2_coefficient_0 = { .l = 0xb0 },
|
||||
.hpf_2_coefficient_1 = { .h = 0x1f, .reserved0 = 0 },
|
||||
.hpf_2_coefficient_2 = { .l = 0x9f },
|
||||
.hpf_2_coefficient_3 = { .h = 0x20, .reserved0 = 0 },
|
||||
|
||||
.lpf_coefficient_0 = { .l = 0x00 },
|
||||
.lpf_coefficient_1 = { .h = 0x00, .reserved0 = 0 },
|
||||
.lpf_coefficient_2 = { .l = 0x00 },
|
||||
.lpf_coefficient_3 = { .h = 0x00, .reserved0 = 0 },
|
||||
|
||||
.fil_3_coefficient_0 = { .l = 0x00 },
|
||||
.fil_3_coefficient_1 = { .h = 0x00, .reserved0 = 0, .s = 0 },
|
||||
.fil_3_coefficient_2 = { .l = 0x00 },
|
||||
.fil_3_coefficient_3 = { .h = 0x00, .reserved0 = 0 },
|
||||
|
||||
.eq_coefficient_0 = { .l = 0x00 },
|
||||
.eq_coefficient_1 = { .h = 0x00 },
|
||||
.eq_coefficient_2 = { .l = 0x00 },
|
||||
.eq_coefficient_3 = { .h = 0x00, .reserved0 = 0 },
|
||||
.eq_coefficient_4 = { .l = 0x00 },
|
||||
.eq_coefficient_5 = { .h = 0x00 },
|
||||
|
||||
.digital_filter_select_3 = {
|
||||
.EQ1 = 0,
|
||||
.EQ2 = 0,
|
||||
.EQ3 = 0,
|
||||
.EQ4 = 0,
|
||||
.EQ5 = 0,
|
||||
.reserved0 = 0,
|
||||
},
|
||||
.device_information = {
|
||||
.DVN = 0b0001,
|
||||
.REV = 0b1100,
|
||||
},
|
||||
|
||||
.e1_coefficient_0 = { .l = 0x00 },
|
||||
.e1_coefficient_1 = { .h = 0x00 },
|
||||
.e1_coefficient_2 = { .l = 0x00 },
|
||||
.e1_coefficient_3 = { .h = 0x00 },
|
||||
.e1_coefficient_4 = { .l = 0x00 },
|
||||
.e1_coefficient_5 = { .h = 0x00 },
|
||||
|
||||
.e2_coefficient_0 = { .l = 0x00 },
|
||||
.e2_coefficient_1 = { .h = 0x00 },
|
||||
.e2_coefficient_2 = { .l = 0x00 },
|
||||
.e2_coefficient_3 = { .h = 0x00 },
|
||||
.e2_coefficient_4 = { .l = 0x00 },
|
||||
.e2_coefficient_5 = { .h = 0x00 },
|
||||
|
||||
.e3_coefficient_0 = { .l = 0x00 },
|
||||
.e3_coefficient_1 = { .h = 0x00 },
|
||||
.e3_coefficient_2 = { .l = 0x00 },
|
||||
.e3_coefficient_3 = { .h = 0x00 },
|
||||
.e3_coefficient_4 = { .l = 0x00 },
|
||||
.e3_coefficient_5 = { .h = 0x00 },
|
||||
|
||||
.e4_coefficient_0 = { .l = 0x00 },
|
||||
.e4_coefficient_1 = { .h = 0x00 },
|
||||
.e4_coefficient_2 = { .l = 0x00 },
|
||||
.e4_coefficient_3 = { .h = 0x00 },
|
||||
.e4_coefficient_4 = { .l = 0x00 },
|
||||
.e4_coefficient_5 = { .h = 0x00 },
|
||||
|
||||
.e5_coefficient_0 = { .l = 0x00 },
|
||||
.e5_coefficient_1 = { .h = 0x00 },
|
||||
.e5_coefficient_2 = { .l = 0x00 },
|
||||
.e5_coefficient_3 = { .h = 0x00 },
|
||||
.e5_coefficient_4 = { .l = 0x00 },
|
||||
.e5_coefficient_5 = { .h = 0x00 },
|
||||
} };
|
||||
|
||||
class AK4951 : public audio::Codec {
|
||||
public:
|
||||
constexpr AK4951(
|
||||
I2C& bus,
|
||||
const I2C::address_t bus_address
|
||||
) : bus(bus),
|
||||
bus_address(bus_address)
|
||||
{
|
||||
}
|
||||
|
||||
std::string name() const override {
|
||||
return "AK4951";
|
||||
}
|
||||
|
||||
void init() override;
|
||||
bool reset() override;
|
||||
|
||||
volume_range_t headphone_gain_range() const override {
|
||||
return { -89.5_dB, 12.0_dB };
|
||||
}
|
||||
|
||||
void headphone_enable() override;
|
||||
void headphone_disable() override;
|
||||
|
||||
void speaker_enable();
|
||||
void speaker_disable();
|
||||
|
||||
void set_headphone_volume(const volume_t volume) override;
|
||||
void headphone_mute();
|
||||
|
||||
void microphone_enable();
|
||||
void microphone_disable();
|
||||
|
||||
size_t reg_count() const override {
|
||||
return asahi_kasei::ak4951::reg_count;
|
||||
}
|
||||
|
||||
size_t reg_bits() const override {
|
||||
return 8;
|
||||
}
|
||||
|
||||
uint32_t reg_read(const size_t reg_address) override {
|
||||
return read(reg_address);
|
||||
}
|
||||
|
||||
private:
|
||||
I2C& bus;
|
||||
const I2C::address_t bus_address;
|
||||
RegisterMap map { default_after_reset };
|
||||
|
||||
enum class LineOutSelect {
|
||||
Speaker,
|
||||
Line,
|
||||
};
|
||||
|
||||
void configure_digital_interface();
|
||||
void set_digtal_volume_control(const reg_t value);
|
||||
void set_dac_power(const bool enable);
|
||||
void set_headphone_power(const bool enable);
|
||||
void set_speaker_power(const bool enable);
|
||||
void select_line_out(const LineOutSelect value);
|
||||
|
||||
reg_t read(const address_t reg_address);
|
||||
void update(const Register reg);
|
||||
void write(const address_t reg_address, const reg_t value);
|
||||
};
|
||||
|
||||
} /* namespace ak4951 */
|
||||
} /* namespace asahi_kasei */
|
||||
|
||||
#endif/*__AK4951_H__*/
|
38
firmware/common/baseband_cpld.cpp
Normal file
38
firmware/common/baseband_cpld.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "baseband_cpld.hpp"
|
||||
|
||||
#include "hackrf_gpio.hpp"
|
||||
using namespace hackrf::one;
|
||||
|
||||
namespace baseband {
|
||||
|
||||
void CPLD::init() {
|
||||
set_invert(false);
|
||||
gpio_baseband_invert.output();
|
||||
}
|
||||
|
||||
void CPLD::set_invert(const bool invert) {
|
||||
gpio_baseband_invert.write(invert);
|
||||
}
|
||||
|
||||
}
|
40
firmware/common/baseband_cpld.hpp
Normal file
40
firmware/common/baseband_cpld.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BASEBAND_CPLD_H__
|
||||
#define __BASEBAND_CPLD_H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace baseband {
|
||||
|
||||
class CPLD {
|
||||
public:
|
||||
void init();
|
||||
|
||||
void set_invert(const bool invert);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__BASEBAND_CPLD_H__*/
|
@@ -33,6 +33,11 @@
|
||||
namespace cpld {
|
||||
namespace max5 {
|
||||
|
||||
struct Config {
|
||||
const std::array<uint16_t, 3328>& block_0;
|
||||
const std::array<uint16_t, 512>& block_1;
|
||||
};
|
||||
|
||||
class CPLD {
|
||||
public:
|
||||
constexpr CPLD(
|
||||
|
130
firmware/common/cpld_update.cpp
Normal file
130
firmware/common/cpld_update.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack_hal.hpp"
|
||||
|
||||
#include "jtag_target_gpio.hpp"
|
||||
#include "cpld_max5.hpp"
|
||||
#include "cpld_xilinx.hpp"
|
||||
#include "portapack_cpld_data.hpp"
|
||||
#include "hackrf_cpld_data.hpp"
|
||||
|
||||
namespace portapack {
|
||||
namespace cpld {
|
||||
|
||||
bool update_if_necessary(
|
||||
const Config config
|
||||
) {
|
||||
jtag::GPIOTarget target {
|
||||
portapack::gpio_cpld_tck,
|
||||
portapack::gpio_cpld_tms,
|
||||
portapack::gpio_cpld_tdi,
|
||||
portapack::gpio_cpld_tdo
|
||||
};
|
||||
jtag::JTAG jtag { target };
|
||||
CPLD cpld { jtag };
|
||||
|
||||
/* Unknown state */
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
|
||||
/* Run-Test/Idle */
|
||||
if( !cpld.idcode_ok() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Enter ISP:
|
||||
* Ensures that the I/O pins transition smoothly from user mode to ISP
|
||||
* mode. All pins are tri-stated.
|
||||
*/
|
||||
cpld.enter_isp();
|
||||
|
||||
/* If silicon ID doesn't match, there's a serious problem. Leave CPLD
|
||||
* in passive state.
|
||||
*/
|
||||
if( !cpld.silicon_id_ok() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Verify CPLD contents against current bitstream. */
|
||||
auto ok = cpld.verify(config.block_0, config.block_1);
|
||||
|
||||
/* CPLD verifies incorrectly. Erase and program with current bitstream. */
|
||||
if( !ok ) {
|
||||
ok = cpld.program(config.block_0, config.block_1);
|
||||
}
|
||||
|
||||
/* If programming OK, reset CPLD to user mode. Otherwise leave it in
|
||||
* passive (ISP) state.
|
||||
*/
|
||||
if( ok ) {
|
||||
cpld.exit_isp();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
} /* namespace cpld */
|
||||
} /* namespace portapack */
|
||||
|
||||
namespace hackrf {
|
||||
namespace cpld {
|
||||
|
||||
static jtag::GPIOTarget jtag_target_hackrf() {
|
||||
return {
|
||||
hackrf::one::gpio_cpld_tck,
|
||||
hackrf::one::gpio_cpld_tms,
|
||||
hackrf::one::gpio_cpld_tdi,
|
||||
hackrf::one::gpio_cpld_tdo,
|
||||
};
|
||||
}
|
||||
|
||||
bool load_sram() {
|
||||
auto jtag_target_hackrf_cpld = jtag_target_hackrf();
|
||||
hackrf::one::cpld::CPLD hackrf_cpld { jtag_target_hackrf_cpld };
|
||||
|
||||
hackrf_cpld.write_sram(hackrf::one::cpld::verify_blocks);
|
||||
const auto ok = hackrf_cpld.verify_sram(hackrf::one::cpld::verify_blocks);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool verify_eeprom() {
|
||||
auto jtag_target_hackrf_cpld = jtag_target_hackrf();
|
||||
hackrf::one::cpld::CPLD hackrf_cpld { jtag_target_hackrf_cpld };
|
||||
|
||||
const auto ok = hackrf_cpld.verify_eeprom(hackrf::one::cpld::verify_blocks);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void init_from_eeprom() {
|
||||
auto jtag_target_hackrf_cpld = jtag_target_hackrf();
|
||||
hackrf::one::cpld::CPLD hackrf_cpld { jtag_target_hackrf_cpld };
|
||||
|
||||
hackrf_cpld.init_from_eeprom();
|
||||
}
|
||||
|
||||
} /* namespace cpld */
|
||||
} /* namespace hackrf */
|
47
firmware/common/cpld_update.hpp
Normal file
47
firmware/common/cpld_update.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __CPLD_UPDATE_H__
|
||||
#define __CPLD_UPDATE_H__
|
||||
|
||||
#include "portapack_cpld_data.hpp"
|
||||
|
||||
namespace portapack {
|
||||
namespace cpld {
|
||||
|
||||
bool update_if_necessary(
|
||||
const Config config
|
||||
);
|
||||
|
||||
} /* namespace cpld */
|
||||
} /* namespace portapack */
|
||||
|
||||
namespace hackrf {
|
||||
namespace cpld {
|
||||
|
||||
bool load_sram();
|
||||
bool verify_eeprom();
|
||||
void init_from_eeprom();
|
||||
|
||||
} /* namespace cpld */
|
||||
} /* namespace hackrf */
|
||||
|
||||
#endif/*__CPLD_UPDATE_H__*/
|
39
firmware/common/hackrf_cpld_data.hpp
Normal file
39
firmware/common/hackrf_cpld_data.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __HACKRF_CPLD_DATA_H__
|
||||
#define __HACKRF_CPLD_DATA_H__
|
||||
|
||||
#include "cpld_xilinx.hpp"
|
||||
|
||||
namespace hackrf {
|
||||
namespace one {
|
||||
namespace cpld {
|
||||
|
||||
using CPLD = ::cpld::xilinx::XC2C64A;
|
||||
|
||||
extern const CPLD::verify_blocks_t verify_blocks;
|
||||
|
||||
} /* namespace hackrf */
|
||||
} /* namespace one */
|
||||
} /* namespace cpld */
|
||||
|
||||
#endif/*__HACKRF_CPLD_DATA_H__*/
|
@@ -79,7 +79,7 @@ constexpr Pin pins[] = {
|
||||
[P2_12] = { 2, 12, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* !RX_AMP_PWR/P52: 10K PU, Q1.G(I), power to U13 (RX amp) */
|
||||
[P2_13] = { 2, 13, { .mode=0, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* P2_13: PortaPack P2_13/DIR */
|
||||
[P3_0] = { 3, 0, { .mode=2, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_TX_SCK: PortaPack I2S0_TX_SCK(I) */
|
||||
[P3_1] = { 3, 1, { .mode=0, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_RX_WS: PortaPack I2S0_TX_WS(I) */
|
||||
[P3_1] = { 3, 1, { .mode=0, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* I2S0_RX_WS: PortaPack I2S0_TX_WS(I). Input enabled to fold back into RX. */
|
||||
[P3_2] = { 3, 2, { .mode=0, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_RX_SDA: PortaPack I2S0_TX_SDA(I) */
|
||||
//[P3_3] = { 3, 3, { .mode=3, .pd=1, .pu=0, .fast=1, .input=1, .ifilt=0 } }, /* SPIFI_SCK: W25Q80BV.CLK(I), enable input buffer for timing feedback */
|
||||
//[P3_4] = { 3, 4, { .mode=3, .pd=0, .pu=1, .fast=1, .input=1, .ifilt=0 } }, /* SPIFI_SIO3/P82: W25Q80BV.HOLD(IO) */
|
||||
|
@@ -22,15 +22,35 @@
|
||||
#ifndef __PORTAPACK_CPLD_DATA_H__
|
||||
#define __PORTAPACK_CPLD_DATA_H__
|
||||
|
||||
#include "cpld_max5.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
namespace portapack {
|
||||
namespace cpld {
|
||||
|
||||
using CPLD = ::cpld::max5::CPLD;
|
||||
using Config = ::cpld::max5::Config;
|
||||
|
||||
namespace rev_20150901 {
|
||||
|
||||
extern const std::array<uint16_t, 3328> block_0;
|
||||
extern const std::array<uint16_t, 512> block_1;
|
||||
|
||||
const Config config { block_0, block_1 };
|
||||
|
||||
} /* namespace rev_20150901 */
|
||||
|
||||
namespace rev_20170522 {
|
||||
|
||||
extern const std::array<uint16_t, 3328> block_0;
|
||||
extern const std::array<uint16_t, 512> block_1;
|
||||
|
||||
const Config config { block_0, block_1 };
|
||||
|
||||
} /* namespace rev_20170522 */
|
||||
|
||||
} /* namespace cpld */
|
||||
} /* namespace portapack */
|
||||
|
||||
|
@@ -56,6 +56,12 @@ constexpr GPIO gpio_cpld_tdo = gpio[GPIO1_8]; // P1_5
|
||||
constexpr GPIO gpio_cpld_tck = gpio[GPIO3_0]; // P6_1
|
||||
constexpr GPIO gpio_cpld_tdi = gpio[GPIO3_1]; // P6_2
|
||||
|
||||
constexpr auto pin_i2s0_mclk = pins[CLK2];
|
||||
constexpr auto pin_i2s0_sck = pins[P3_0];
|
||||
constexpr auto pin_i2s0_ws = pins[P3_1];
|
||||
constexpr auto pin_i2s0_tx_sda = pins[P3_2];
|
||||
constexpr auto pin_i2s0_rx_sda = pins[P6_2];
|
||||
|
||||
} /* namespace portapack */
|
||||
|
||||
#endif/*__PORTAPACK_HAL_H__*/
|
||||
|
@@ -64,6 +64,17 @@ void IO::lcd_reset_state(const bool active) {
|
||||
io_write(1, io_reg);
|
||||
}
|
||||
|
||||
void IO::audio_reset_state(const bool active) {
|
||||
/* Reset signal for audio codec. Some audio codecs (e.g. WM8731) do not
|
||||
* implement reset signal, only soft reset via I2C.
|
||||
*/
|
||||
/* NOTE: This overwrites the contents of the IO register, which for now
|
||||
* have no significance. But someday...?
|
||||
*/
|
||||
io_reg = (io_reg & 0xfd) | ((active ? 1 : 0) << 1);
|
||||
io_write(1, io_reg);
|
||||
}
|
||||
|
||||
uint32_t IO::io_update(const TouchPinsConfig write_value) {
|
||||
/* Very touchy code to save context of PortaPack data bus while the
|
||||
* resistive touch pin drive is changed. Order of operations is
|
||||
|
@@ -100,6 +100,7 @@ public:
|
||||
|
||||
void lcd_backlight(const bool value);
|
||||
void lcd_reset_state(const bool active);
|
||||
void audio_reset_state(const bool active);
|
||||
|
||||
void lcd_data_write_command_and_data(
|
||||
const uint_fast8_t command,
|
||||
@@ -198,7 +199,7 @@ private:
|
||||
static constexpr size_t gpio_data_shift = 8;
|
||||
static constexpr ioportmask_t gpio_data_mask = 0xffU << gpio_data_shift;
|
||||
|
||||
uint8_t io_reg { 0x01 };
|
||||
uint8_t io_reg { 0x03 };
|
||||
|
||||
void lcd_rd_assert() {
|
||||
gpio_lcd_rd.set();
|
||||
|
@@ -28,30 +28,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
template<typename T>
|
||||
struct simd32_t {
|
||||
union {
|
||||
uint32_t raw;
|
||||
T vec;
|
||||
};
|
||||
|
||||
operator uint32_t() const {
|
||||
return raw;
|
||||
}
|
||||
|
||||
simd32_t& operator=(uint32_t v) {
|
||||
raw = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static_assert(sizeof(raw) == sizeof(vec), "simd32_t types are not the same size.");
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
simd32_t<T>* simd32_ptr(T* const p) {
|
||||
return reinterpret_cast<simd32_t<T>*>(p);
|
||||
}
|
||||
|
||||
struct vec4_s8 {
|
||||
union {
|
||||
int8_t v[4];
|
||||
|
@@ -87,24 +87,28 @@ void WM8731::init() {
|
||||
headphone_mute();
|
||||
}
|
||||
|
||||
void WM8731::reset() {
|
||||
write(0x0f, 0);
|
||||
bool WM8731::detected() {
|
||||
return reset();
|
||||
}
|
||||
|
||||
void WM8731::write(const Register reg) {
|
||||
write(toUType(reg), map.w[toUType(reg)]);
|
||||
bool WM8731::reset() {
|
||||
return write(0x0f, 0);
|
||||
}
|
||||
|
||||
void WM8731::write(const address_t reg_address, const reg_t value) {
|
||||
bool WM8731::write(const Register reg) {
|
||||
return write(toUType(reg), map.w[toUType(reg)]);
|
||||
}
|
||||
|
||||
bool WM8731::write(const address_t reg_address, const reg_t value) {
|
||||
const uint16_t word = (reg_address << 9) | value;
|
||||
const std::array<uint8_t, 2> values {
|
||||
static_cast<uint8_t>(word >> 8),
|
||||
static_cast<uint8_t>(word & 0xff),
|
||||
};
|
||||
bus.transmit(bus_address, values.data(), values.size());
|
||||
return bus.transmit(bus_address, values.data(), values.size());
|
||||
}
|
||||
|
||||
reg_t WM8731::read(const address_t reg_address) {
|
||||
uint32_t WM8731::reg_read(const size_t reg_address) {
|
||||
return map.w[reg_address];
|
||||
}
|
||||
|
||||
|
@@ -27,14 +27,11 @@
|
||||
|
||||
#include "i2c_pp.hpp"
|
||||
|
||||
#include "volume.hpp"
|
||||
#include "audio.hpp"
|
||||
|
||||
namespace wolfson {
|
||||
namespace wm8731 {
|
||||
|
||||
constexpr volume_range_t headphone_gain_range { -121.0_dB, 6.0_dB };
|
||||
constexpr volume_range_t line_in_gain_range { -34.5_dB, 12.0_dB };
|
||||
|
||||
enum class ADCSource {
|
||||
Line = 0,
|
||||
Microphone = 1,
|
||||
@@ -278,7 +275,7 @@ constexpr RegisterMap default_after_reset { Register_Type {
|
||||
},
|
||||
} };
|
||||
|
||||
class WM8731 {
|
||||
class WM8731 : public audio::Codec {
|
||||
public:
|
||||
constexpr WM8731(
|
||||
I2C& bus,
|
||||
@@ -288,12 +285,18 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void init();
|
||||
void init() override;
|
||||
|
||||
bool reset() override;
|
||||
|
||||
void reset();
|
||||
std::string name() const override {
|
||||
return "WM8731";
|
||||
}
|
||||
|
||||
bool detected();
|
||||
|
||||
void set_line_in_volume(const volume_t volume) {
|
||||
const auto normalized = line_in_gain_range.normalize(volume);
|
||||
const auto normalized = line_in_gain_range().normalize(volume);
|
||||
auto n = normalized.centibel() / 15;
|
||||
|
||||
write(LeftLineIn {
|
||||
@@ -305,8 +308,9 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
void set_headphone_volume(const volume_t volume) {
|
||||
const auto normalized = headphone_gain_range.normalize(volume);
|
||||
void set_headphone_volume(const volume_t volume) override {
|
||||
headphone_volume = volume;
|
||||
const auto normalized = headphone_gain_range().normalize(volume);
|
||||
auto n = normalized.centibel() / 10;
|
||||
|
||||
write(LeftHeadphoneOut {
|
||||
@@ -317,8 +321,32 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
volume_range_t headphone_gain_range() const override {
|
||||
return { -121.0_dB, 6.0_dB };
|
||||
}
|
||||
|
||||
volume_range_t line_in_gain_range() const {
|
||||
return { -34.5_dB, 12.0_dB };
|
||||
}
|
||||
|
||||
void headphone_mute() {
|
||||
set_headphone_volume(headphone_gain_range.min);
|
||||
set_headphone_volume(headphone_gain_range().min);
|
||||
}
|
||||
|
||||
void headphone_enable() override {
|
||||
set_headphone_volume(headphone_volume);
|
||||
}
|
||||
|
||||
void headphone_disable() override {
|
||||
headphone_mute();
|
||||
}
|
||||
|
||||
void microphone_enable() override {
|
||||
// TODO: Implement
|
||||
}
|
||||
|
||||
void microphone_disable() override {
|
||||
// TODO: Implement
|
||||
}
|
||||
|
||||
// void microphone_mute(const bool mute) {
|
||||
@@ -331,16 +359,25 @@ public:
|
||||
// write(Register::AnalogAudioPathControl);
|
||||
// }
|
||||
|
||||
reg_t read(const address_t reg_address);
|
||||
size_t reg_count() const override {
|
||||
return wolfson::wm8731::reg_count;
|
||||
}
|
||||
|
||||
size_t reg_bits() const override {
|
||||
return 9;
|
||||
}
|
||||
|
||||
uint32_t reg_read(const size_t reg_address) override;
|
||||
|
||||
private:
|
||||
I2C& bus;
|
||||
const I2C::address_t bus_address;
|
||||
RegisterMap map { default_after_reset };
|
||||
volume_t headphone_volume = -60.0_dB;
|
||||
|
||||
void write(const Register reg);
|
||||
bool write(const Register reg);
|
||||
|
||||
void write(const address_t reg_address, const reg_t value);
|
||||
bool write(const address_t reg_address, const reg_t value);
|
||||
|
||||
void write(const LeftLineIn value);
|
||||
void write(const RightLineIn value);
|
||||
|
Reference in New Issue
Block a user