/* * 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 __WM8731_H__ #define __WM8731_H__ #include #include "i2c_pp.hpp" #include "wm8731.hpp" #include "utility.hpp" #include "volume.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, }; using address_t = uint8_t; using reg_t = uint16_t; constexpr size_t reg_count = 10; enum class Register : address_t { LeftLineIn = 0x00, RightLineIn = 0x01, LeftHeadphoneOut = 0x02, RightHeadphoneOut = 0x03, AnalogAudioPathControl = 0x04, DigitalAudioPathControl = 0x05, PowerDownControl = 0x06, DigitalAudioInterfaceFormat = 0x07, SamplingControl = 0x08, ActiveControl = 0x09, }; struct LeftLineIn { reg_t linvol : 5; reg_t reserved0 : 2; reg_t linmute : 1; reg_t lrinboth : 1; reg_t reserved1 : 7; }; static_assert(sizeof(LeftLineIn) == sizeof(reg_t), "LeftLineIn type wrong size"); struct RightLineIn { reg_t rinvol : 5; reg_t reserved0 : 2; reg_t rinmute : 1; reg_t rlinboth : 1; reg_t reserved1 : 7; }; static_assert(sizeof(RightLineIn) == sizeof(reg_t), "RightLineIn type wrong size"); struct LeftHeadphoneOut { reg_t lhpvol : 7; reg_t lzcen : 1; reg_t lrhpboth : 1; reg_t reserved0 : 7; }; static_assert(sizeof(LeftHeadphoneOut) == sizeof(reg_t), "LeftHeadphoneOut type wrong size"); struct RightHeadphoneOut { reg_t rhpvol : 7; reg_t rzcen : 1; reg_t rlhpboth : 1; reg_t reserved0 : 7; }; static_assert(sizeof(RightHeadphoneOut) == sizeof(reg_t), "RightHeadphoneOut type wrong size"); struct AnalogAudioPathControl { reg_t micboost : 1; reg_t mutemic : 1; reg_t insel : 1; reg_t bypass : 1; reg_t dacsel : 1; reg_t sidetone : 1; reg_t sideatt : 2; reg_t reserved0 : 8; }; static_assert(sizeof(AnalogAudioPathControl) == sizeof(reg_t), "AnalogAudioPathControl type wrong size"); struct DigitalAudioPathControl { reg_t adchpd : 1; reg_t deemp : 2; reg_t dacmu : 1; reg_t hpor : 1; reg_t reserved0 : 11; }; static_assert(sizeof(DigitalAudioPathControl) == sizeof(reg_t), "DigitalAudioPathControl type wrong size"); struct PowerDownControl { reg_t lineinpd : 1; reg_t micpd : 1; reg_t adcpd : 1; reg_t dacpd : 1; reg_t outpd : 1; reg_t oscpd : 1; reg_t clkoutpd : 1; reg_t poweroff : 1; reg_t reserved0 : 8; }; static_assert(sizeof(PowerDownControl) == sizeof(reg_t), "PowerDownControl type wrong size"); struct DigitalAudioInterfaceFormat { reg_t format : 2; reg_t iwl : 2; reg_t lrp : 1; reg_t lrswap : 1; reg_t ms : 1; reg_t bclkinv : 1; reg_t reserved0 : 8; }; static_assert(sizeof(DigitalAudioInterfaceFormat) == sizeof(reg_t), "DigitalAudioInterfaceFormat type wrong size"); struct SamplingControl { reg_t usb_normal : 1; reg_t bosr : 1; reg_t sr : 4; reg_t clkidiv2 : 1; reg_t clkodiv2 : 1; reg_t reserved0 : 8; }; static_assert(sizeof(SamplingControl) == sizeof(reg_t), "SamplingControl type wrong size"); struct ActiveControl { reg_t active : 1; reg_t reserved0 : 15; }; static_assert(sizeof(ActiveControl) == sizeof(reg_t), "ActiveControl type wrong size"); struct Reset { reg_t reset : 9; reg_t reserved0 : 7; }; static_assert(sizeof(Reset) == sizeof(reg_t), "Reset type wrong size"); struct Register_Type { LeftLineIn left_line_in; RightLineIn right_line_in; LeftHeadphoneOut left_headphone_out; RightHeadphoneOut right_headphone_out; AnalogAudioPathControl analog_audio_path_control; DigitalAudioPathControl digital_audio_path_control; PowerDownControl power_down_control; DigitalAudioInterfaceFormat digital_audio_interface_format; SamplingControl sampling_control; ActiveControl active_control; }; 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 w; }; }; static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size"); constexpr RegisterMap default_after_reset { Register_Type { .left_line_in = { .linvol = 0b10111, .reserved0 = 0b00, .linmute = 0b1, .lrinboth = 0b0, .reserved1 = 0, }, .right_line_in = { .rinvol = 0b10111, .reserved0 = 0b00, .rinmute = 0b1, .rlinboth = 0b0, .reserved1 = 0, }, .left_headphone_out = { .lhpvol = 0b1111001, .lzcen = 0b0, .lrhpboth = 0b0, .reserved0 = 0, }, .right_headphone_out = { .rhpvol = 0b1111001, .rzcen = 0b0, .rlhpboth = 0b0, .reserved0 = 0, }, .analog_audio_path_control = { .micboost = 0b0, .mutemic = 0b1, .insel = 0b0, .bypass = 0b1, .dacsel = 0b0, .sidetone = 0b0, .sideatt = 0b00, .reserved0 = 0, }, .digital_audio_path_control = { .adchpd = 0b0, .deemp = 0b00, .dacmu = 0b1, .hpor = 0b0, .reserved0 = 0, }, .power_down_control = { .lineinpd = 0b1, .micpd = 0b1, .adcpd = 0b1, .dacpd = 0b1, .outpd = 0b1, .oscpd = 0b0, .clkoutpd = 0b0, .poweroff = 0b1, .reserved0 = 0, }, .digital_audio_interface_format = { .format = 0b10, .iwl = 0b10, .lrp = 0b0, .lrswap = 0b0, .ms = 0b0, .bclkinv = 0b0, .reserved0 = 0, }, .sampling_control = { .usb_normal = 0b0, .bosr = 0b0, .sr = 0b0000, .clkidiv2 = 0b0, .clkodiv2 = 0b0, .reserved0 = 0, }, .active_control = { .active = 0b0, .reserved0 = 0, }, } }; class WM8731 { public: constexpr WM8731( I2C& bus, const I2C::address_t bus_address ) : bus(bus), bus_address(bus_address) { } void init() { reset(); write(PowerDownControl { .lineinpd = 1, .micpd = 0, .adcpd = 0, .dacpd = 0, .outpd = 0, .oscpd = 1, .clkoutpd = 1, .poweroff = 0, .reserved0 = 0, }); // write(SamplingControl { // .usb_normal = 0, // .bosr = 0, // .sr = 0, // .clkidiv2 = 0, // .clkodiv2 = 0, // .reserved0 = 0, // }); write(DigitalAudioInterfaceFormat { .format = 2, .iwl = 0, .lrp = 0, .lrswap = 0, .ms = 0, .bclkinv = 0, .reserved0 = 0, }); write(DigitalAudioPathControl { .adchpd = 0, .deemp = 0, .dacmu = 0, .hpor = 0, .reserved0 = 0, }); write(AnalogAudioPathControl { .micboost = 1, // Enable 20dB boost .mutemic = 0, // Disable mute (unmute) .insel = 1, // Microphone input to ADC .bypass = 0, .dacsel = 1, .sidetone = 0, .sideatt = 0, .reserved0 = 0, }); write(ActiveControl { .active = 1, .reserved0 = 0, }); set_line_in_volume(0.0_dB); headphone_mute(); } void reset() { write(0x0f, 0); } void set_line_in_volume(const volume_t volume) { const auto normalized = line_in_gain_range.normalize(volume); auto n = normalized.centibel() / 15; write(LeftLineIn { .linvol = static_cast(n), .reserved0 = 0, .linmute = 0, .lrinboth = 1, .reserved1 = 0, }); } void set_headphone_volume(const volume_t volume) { const auto normalized = headphone_gain_range.normalize(volume); auto n = normalized.centibel() / 10; write(LeftHeadphoneOut { .lhpvol = static_cast(n), .lzcen = 1, .lrhpboth = 1, .reserved0 = 0, }); } void headphone_mute() { set_headphone_volume(headphone_gain_range.min); } // void microphone_mute(const bool mute) { // map.r.analog_audio_path_control.mutemic = (mute ? 0 : 1); // write(Register::AnalogAudioPathControl); // } // void set_adc_source(const ADCSource adc_source) { // map.r.analog_audio_path_control.insel = toUType(adc_source); // write(Register::AnalogAudioPathControl); // } reg_t read(const address_t reg_address); private: I2C& bus; const I2C::address_t bus_address; RegisterMap map { default_after_reset }; void write(const Register reg) { write(toUType(reg), map.w[toUType(reg)]); } void write(const address_t reg_address, const reg_t value); void write(const LeftLineIn value) { map.r.left_line_in = value; write(Register::LeftLineIn); } void write(const RightLineIn value) { map.r.right_line_in = value; write(Register::RightLineIn); } void write(const LeftHeadphoneOut value) { map.r.left_headphone_out = value; write(Register::LeftHeadphoneOut); } void write(const RightHeadphoneOut value) { map.r.right_headphone_out = value; write(Register::RightHeadphoneOut); } void write(const AnalogAudioPathControl value) { map.r.analog_audio_path_control = value; write(Register::AnalogAudioPathControl); } void write(const DigitalAudioPathControl value) { map.r.digital_audio_path_control = value; write(Register::DigitalAudioPathControl); } void write(const PowerDownControl value) { map.r.power_down_control = value; write(Register::PowerDownControl); } void write(const DigitalAudioInterfaceFormat value) { map.r.digital_audio_interface_format = value; write(Register::DigitalAudioInterfaceFormat); } void write(const SamplingControl value) { map.r.sampling_control = value; write(Register::SamplingControl); } void write(const ActiveControl value) { map.r.active_control = value; write(Register::ActiveControl); } }; } /* namespace wm8731 */ } /* namespace wolfson */ #endif/*__WM8731_H__*/