diff options
Diffstat (limited to 'src/devices/sound/dac3350a.cpp')
-rw-r--r-- | src/devices/sound/dac3350a.cpp | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/src/devices/sound/dac3350a.cpp b/src/devices/sound/dac3350a.cpp new file mode 100644 index 00000000000..91e0593c358 --- /dev/null +++ b/src/devices/sound/dac3350a.cpp @@ -0,0 +1,332 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/* + Micronas DAC 3550A Stereo Audio DAC + i2c code based on mas3507d + + Accepts 8-bit and 16-bit I2C write operations and has no read operations +*/ + +#include "emu.h" +#include "dac3350a.h" + +#define VERBOSE (LOG_GENERAL) + +#include "logmacro.h" + +enum +{ + CMD_DEV_WRITE = 0x9a, +}; + +enum +{ + IDLE = 0, + STARTED, + NAK, + ACK, + ACK2, +}; + +enum +{ + UNKNOWN = 0, + VALIDATED, + WRONG, +}; + +enum +{ + REG_UNKNOWN = 0, + REG_SR = 1, + REG_AVOL, + REG_GCFG, +}; + +enum +{ + // sample rate control register + SR_LR_SEL_BIT = 4, // L/R-bit + SR_LR_SEL_LEFT = 0, // (WSI = 0 -> left channel + SR_LR_SEL_RIGHT = 1, // (WSI = 0 -> right channel + + SR_SP_SEL_BIT = 3, // delay bit + SR_SP_SEL_NO_DELAY = 0, + SR_SP_SEL_1_BIT_DELAY = 1, + + SR_SRC_BIT = 0, // 3 bits wide, sample rate control + SR_SRC_48 = 0, // 32-48 kHz + SR_SRC_32, // 26-32 kHz + SR_SRC_24, // 20-26 kHz + SR_SRC_16, // 14-20 kHz + SR_SRC_12, // 10-14 kHz + SR_SRC_8, // 8-10 kHz + SR_SRC_A, // autoselect +}; + +enum +{ + // analog volume register + AVOL_DEEM_BIT = 14, // deemphasis on/off + AVOL_DEEM_OFF = 0, + AVOL_DEEM_ON = 1, + + AVOL_L_BIT = 8, // 6 bits wide, analog audio volume level left + AVOL_R_BIT = 0, // 6 bits wide, analog audio volume level right +}; + +enum +{ + // global configuration register + GCFG_SEL_53V_BIT = 6, // select 3V-5V mode + GCFG_SEL_53V_3V = 0, + GCFG_SEL_53V_5V = 1, + + GCFG_PWMD_BIT = 5, // power-mode + GCFG_PWMD_NORMAL = 0, + GCFG_PWMD_LOW_POWER = 1, + + GCFG_INSEL_AUX2_BIT = 4, // AUX2 select + GCFG_INSEL_AUX2_OFF = 0, + GCFG_INSEL_AUX2_ON = 1, + + GCFG_INSEL_AUX1_BIT = 3, // AUX1 select + GCFG_INSEL_AUX1_OFF = 0, + GCFG_INSEL_AUX1_ON = 1, + + GCFG_INSEL_DAC_BIT = 2, // DAC select + GCFG_INSEL_DAC_OFF = 0, + GCFG_INSEL_DAC_ON = 1, + + GCFG_AUX_MS_BIT = 1, // aux-mono/stereo + GCFG_AUX_MS_STEREO = 0, + GCFG_AUX_MS_MONO = 1, + + GCFG_IPRA_BIT = 0, // invert right power amplifier + GCFG_IPRA_NOT_INVERTED = 0, + GCFG_IPRA_INVERTED = 1, +}; + + +DEFINE_DEVICE_TYPE(DAC3350A, dac3350a_device, "dac3350a", "Micronas DAC 3550A Stereo Audio DAC") + +dac3350a_device::dac3350a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, DAC3350A, tag, owner, clock) + , device_sound_interface(mconfig, *this) +{ +} + +void dac3350a_device::device_start() +{ + // TODO: use configured clock for sample rate and respond to device_clock_changed + m_stream = stream_alloc(2, 2, 44100); + + save_item(NAME(m_i2c_bus_state)); + save_item(NAME(m_i2c_bus_address)); + save_item(NAME(m_i2c_scli)); + save_item(NAME(m_i2c_sdai)); + save_item(NAME(m_i2c_bus_curbit)); + save_item(NAME(m_i2c_bus_curval)); + save_item(NAME(m_i2c_bytecount)); + + save_item(NAME(m_i2c_subadr)); + save_item(NAME(m_i2c_data)); + + save_item(NAME(m_dac_enable)); + save_item(NAME(m_volume)); +} + +void dac3350a_device::device_reset() +{ + m_i2c_scli = m_i2c_sdai = 1; + m_i2c_bus_state = IDLE; + m_i2c_bus_address = UNKNOWN; + m_i2c_bus_curbit = -1; + m_i2c_bus_curval = 0; + + m_i2c_subadr = 0; + m_i2c_data = 0; + + m_dac_enable = true; + + std::fill(std::begin(m_volume), std::end(m_volume), 1.0); +} + +void dac3350a_device::i2c_scl_w(int line) +{ + if (line == m_i2c_scli) + return; + + m_i2c_scli = line; + + if (m_i2c_scli) + { + if (m_i2c_bus_state == STARTED) + { + m_i2c_bus_curval |= m_i2c_sdai << m_i2c_bus_curbit; + m_i2c_bus_curbit--; + + if (m_i2c_bus_curbit == -1) + { + if (m_i2c_bus_address == UNKNOWN) + { + if (m_i2c_bus_curval == CMD_DEV_WRITE) + { + m_i2c_bus_state = ACK; + m_i2c_bus_address = VALIDATED; + m_i2c_bus_curval = 0; + } + else + { + m_i2c_bus_state = NAK; + m_i2c_bus_address = WRONG; + } + } + else if (m_i2c_bus_address == VALIDATED) + { + if (m_i2c_bytecount < 3) + { + m_i2c_bus_state = ACK; + + if (m_i2c_bytecount == 0) + { + m_i2c_subadr = m_i2c_bus_curval; + } + else + { + m_i2c_data <<= 8; + m_i2c_data |= m_i2c_bus_curval; + } + + m_i2c_bytecount++; + } + else + { + // Only accept 8-bit and 16-bit writes + m_i2c_bus_state = NAK; + } + } + } + } + else if (m_i2c_bus_state == ACK) + { + m_i2c_bus_state = ACK2; + } + } + else + { + if (m_i2c_bus_state == ACK2) + { + m_i2c_bus_state = STARTED; + m_i2c_bus_curbit = 7; + m_i2c_bus_curval = 0; + } + } +} + +void dac3350a_device::i2c_sda_w(int line) +{ + if (line == m_i2c_sdai) + return; + + m_i2c_sdai = line; + + if (m_i2c_scli) + { + if (m_i2c_sdai) + i2c_device_handle_write(); + + m_i2c_bus_state = m_i2c_sdai ? IDLE : STARTED; + m_i2c_bus_address = UNKNOWN; + m_i2c_bus_curbit = 7; + m_i2c_bus_curval = 0; + } +} + +void dac3350a_device::i2c_device_handle_write() +{ + const auto bytecount = m_i2c_bytecount; + + m_i2c_bytecount = 0; + + if (bytecount < 2) // not a valid command, ignore + return; + + // const int mcs = BIT(m_i2c_subadr, 6, 2); // chip select, 3 = MPEG mode + const int reg_adr = BIT(m_i2c_subadr, 0, 2); + + if (reg_adr == REG_SR) + { + // sample rate control + const int sample_rate = BIT(m_i2c_data, SR_SRC_BIT, 3); + const int delay = BIT(m_i2c_data, SR_SP_SEL_BIT); + const int lr = BIT(m_i2c_data, SR_LR_SEL_BIT); + + LOG("DAC: SR register %d %d %d\n", sample_rate, delay, lr); + } + else if (reg_adr == REG_AVOL) + { + // set volume, requires 2 bytes + if (bytecount <= 2) + logerror("AVOL command requires 2 bytes of data, found %d\n", bytecount); + + const int vol_r = BIT(m_i2c_data, AVOL_R_BIT, 6); + const int vol_l = BIT(m_i2c_data, AVOL_L_BIT, 6); + const int deemph = BIT(m_i2c_data, AVOL_DEEM_BIT); + + m_volume[0] = calculate_volume(vol_l); + m_volume[1] = calculate_volume(vol_r); + + LOG("DAC: AVOL register %d %d %d (%f %f)\n", vol_r, vol_l, deemph, m_volume[0], m_volume[1]); + } + else if (reg_adr == REG_GCFG) + { + // global configuration + const int ipra = BIT(m_i2c_data, GCFG_IPRA_BIT); + const int aux_ms = BIT(m_i2c_data, GCFG_AUX_MS_BIT); + const int insel_dac = BIT(m_i2c_data, GCFG_INSEL_DAC_BIT); + const int insel_aux1 = BIT(m_i2c_data, GCFG_INSEL_AUX1_BIT); + const int insel_aux2 = BIT(m_i2c_data, GCFG_INSEL_AUX2_BIT); + const int pwmd = BIT(m_i2c_data, GCFG_PWMD_BIT); + const int sel_53v = BIT(m_i2c_data, GCFG_SEL_53V_BIT); + + LOG("DAC: GCFG register %d %d %d %d %d %d %d\n", ipra, aux_ms, insel_dac, insel_aux1, insel_aux2, pwmd, sel_53v); + } + else + { + LOG("DAC: Unknown register selected! %d\n", reg_adr); + } +} + +float dac3350a_device::calculate_volume(int val) +{ + if (val == 0) + return 0.0; // mute + + double db = 0; + if (val <= 7) + { + // -75 dB ... -54 dB: 3 dB steps + db = -54 - ((8 - val) * 3); + } + else + { + // -54 dB ... +18 dB: 1.5 dB steps + db = -54 + ((val - 8) * 1.5); + } + + db = std::clamp(db, -75.0, 18.0); // range is +18 dB to -75 dB + + return powf(10.0, db / 20.0); +} + +void dac3350a_device::sound_stream_update(sound_stream &stream) +{ + const sound_stream::sample_t enable_scale = m_dac_enable ? 1.0 : 0.0; + + for (int channel = 0; channel < 2 && channel < stream.output_count(); channel++) + { + for (int sampindex = 0; sampindex < stream.samples(); sampindex++) + stream.put(channel, sampindex, stream.get(channel, sampindex) * enable_scale * m_volume[channel]); + } +} |