From 60254a38ed578c0aafb31792fb661e299f78436e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 31 May 2022 13:15:17 +0200 Subject: [PATCH] experiments/ohmmeter.py: Make CS42L84 your ohmmeter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add script which prints out the (rough) measured impedance of connected headphones on machines with the CS42L84 jack codec (2021 Macbook Pros). $ proxyclient/experiments/ohmmeter.py Waiting for plug... measuring... 64.3 ohms... yanked Waiting for plug... measuring... 697.1 ohms... Signed-off-by: Martin PoviĊĦer --- proxyclient/experiments/ohmmeter.py | 260 ++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100755 proxyclient/experiments/ohmmeter.py diff --git a/proxyclient/experiments/ohmmeter.py b/proxyclient/experiments/ohmmeter.py new file mode 100755 index 00000000..bd342c1e --- /dev/null +++ b/proxyclient/experiments/ohmmeter.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT +import sys, pathlib +import time +sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) + +from m1n1.utils import * +from enum import IntEnum +from m1n1.setup import * +from m1n1.hw.i2c import I2C + + +class R_IRQ_MASK1(Register8): + RING_PLUG = 0 + RING_UNPLUG = 1 + TIP_PLUG = 2 + TIP_UNPLUG = 3 + +class E_DCID_GND_SEL(IntEnum): + NONE = 0 + HS3 = 1 + HS4 = 2 + +class E_DCID_Z_RANGE(IntEnum): + NONE = 0 + UNK2 = 2 + UNK3 = 3 + +class R_DCID_CTRL1(Register8): + Z_RANGE = 2, 0, E_DCID_Z_RANGE + +class R_DCID_CTRL2(Register8): + GND_SEL = 6, 4, E_DCID_GND_SEL + +class R_DCID_CTRL3(Register8): + START = 0 + +class R_DCID_STATUS(Register32): + OVERALL = 9, 0 + DONE = 10 + U = 20, 11 + D = 30, 21 + +class E_DEBOUNCE_TIME(IntEnum): + T_0MS = 0b000 + T_125MS = 0b001 + T_250MS = 0b010 + T_500MS = 0b011 + T_750MS = 0b100 + T_1S = 0b101 + +class R_TR_SENSE_CTRL(Register8): + INV = 7 + UNK1 = 6 + FALLTIME = 5, 3, E_DEBOUNCE_TIME + RISETIME = 2, 0, E_DEBOUNCE_TIME + +class R_TR_SENSE_STATUS(Register8): + RING_PLUG = 0 + RING_UNPLUG = 1 + TIP_PLUG = 2 + TIP_UNPLUG = 3 + +class R_MSM_BLOCK_EN3(Register8): + TR_SENSE_EN = 3 + DCID_EN = 4 + +class R_MSM_CLAMP_CTRL(Register8): + HS_CLAMP_DISABLE = 0 + +class E_SAMP_RATE(IntEnum): + S_16KHZ = 1 + S_24KHZ = 2 + S_32KHZ = 3 + S_48KHZ = 4 + S_96KHZ = 5 + S_192KHZ = 6 + S_22K05HZ = 10 + S_44K1HZ = 12 + S_88K2HZ = 13 + S_176K4HZ = 14 + +class R_CCM_SAMP_RATE(Register8): + RATE = 7, 0, E_SAMP_RATE + +class R_DAC_CTRL1(Register8): + HP_LOAD = 2 # maybe + UNK2 = 4 + UNK3 = 5 # always set + HIGH_V = 6 + +class E_PULLDOWN_R(IntEnum): + NONE = 0x0 + R_UNK8 = 0x8 + R_1K1OHMS = 0xc + +class R_DAC_CTRL2(Register8): + PULLDOWN_R = 3, 0, E_PULLDOWN_R + +class CS42L84Registers(RegMap): + DEVID = irange(0x0, 5), Register8 + FREEZE = 0x6, Register8 + + SW_RESET = 0x203, Register8 + + IRQ_STATUS = irange(0x400, 3), Register8 + IRQ_MASK1 = 0x418, R_IRQ_MASK1 + IRQ_MASK2 = 0x419, Register8 + IRQ_MASK3 = 0x41a, Register8 + + CCM_CTRL = irange(0x600, 4), Register8 + CCM_SAMP_RATE = 0x601, R_CCM_SAMP_RATE + CCM_ASP_CLK_CTRL = 0x608, Register8 + + PLL_CTRL = 0x800, Register8 + PLL_DIV_FRAC = irange(0x804, 3), Register8 + PLL_DIV_INT = 0x807, Register8 + PLL_DIVOUT = 0x808, Register8 + + DCID_CTRL1 = 0x1200, R_DCID_CTRL1 + DCID_CTRL2 = 0x1201, R_DCID_CTRL2 + DCID_CTRL3 = 0x1202, R_DCID_CTRL3 + DCID_TRIM_OFFSET = 0x1207, Register8 + DCID_TRIM_SLOPE = 0x120a, Register8 + + # R_pull = 1100 - (regval - 128)*2 + DCID_PULLDOWN_TRIM = 0x120b, Register8 + DCID_STATUS = 0x120c, R_DCID_STATUS + + # tip/ring sense + TR_SENSE_CTRL1 = 0x1280, Register8 + TR_SENSE_CTRL2 = 0x1281, Register8 + RING_SENSE_CTRL = 0x1282, R_TR_SENSE_CTRL + TIP_SENSE_CTRL = 0x1283, R_TR_SENSE_CTRL + TR_SENSE_STATUS = 0x1288, R_TR_SENSE_STATUS + + MIKEY_CTRL1 = 0x1472 + MIKEY_CTRL2 = 0x1473, Register8 + + MSM_BLOCK_EN3 = 0x1802, R_MSM_BLOCK_EN3 + MSM_CLAMP_CTRL = 0x1813, R_MSM_CLAMP_CTRL + ADC_CTRL = irange(0x2000, 4), Register8 + + DAC_CTRL1 = 0x3000, R_DAC_CTRL1 + DAC_CTRL2 = 0x3001, R_DAC_CTRL2 + DACA_VOL_LSB = 0x3004, Register8 + DACA_VOL_MSB = 0x3005, Register8 # sign bit + DACB_VOL_LSB = 0x3006, Register8 + DACB_VOL_MSB = 0x3007, Register8 # sign bit + HP_VOL_CTRL = 0x3020, Register8 + HP_CLAMP_CTRL = 0x3123, Register8 + + ASP_CTRL = 0x5000, Register8 + ASP_FSYNC_CTRL = irange(0x500f, 3), Register8 + ASP_DATA_CTRL = 0x5018, Register8 + + ASP_RX_EN = 0x5020, Register8 + ASP_TX_EN = 0x5024, Register8 + + ASP_RXSLOT_CH1_LSB = 0x5028, Register8 + ASP_RXSLOT_CH1_MSB = 0x5029, Register8 + + ASP_RXSLOT_CH2_LSB = 0x502c, Register8 + ASP_RXSLOT_CH2_MSB = 0x502d, Register8 + + ASP_TXSLOT_CH1_LSB = 0x5068, Register8 + ASP_TXSLOT_CH1_MSB = 0x5068, Register8 + + +class CS42L84: + def __init__(self, bus, addr): + self.regs = CS42L84Registers(self, 0) + self.bus = bus + self.addr = addr + + def write(self, regaddr, val, width=8): + valbytes = val.to_bytes(width//8, byteorder="little") + self.bus.write_reg(self.addr, regaddr, valbytes, regaddrlen=2) + + def read(self, regaddr, width=8): + read = self.bus.read_reg(self.addr, regaddr, width//8, regaddrlen=2) + return int.from_bytes(read, byteorder='little') + + +def read_devid(): + pass + +def sense_Z(): + r = l84.regs + + r.MSM_CLAMP_CTRL.set(HS_CLAMP_DISABLE=1) + r.DAC_CTRL2.set(PULLDOWN_R=E_PULLDOWN_R.R_1K1OHMS) + r.DCID_CTRL1.set(Z_RANGE=E_DCID_Z_RANGE.UNK2) + r.DCID_CTRL2.set(GND_SEL=E_DCID_GND_SEL.HS3) + r.MSM_BLOCK_EN3.set(DCID_EN=1) + r.DCID_CTRL3.set(START=0) + r.DCID_CTRL3.set(START=1) + + while not r.DCID_STATUS.reg.DONE: + pass + + reading = r.DCID_STATUS.reg.OVERALL + offset_trim = r.DCID_TRIM_OFFSET.val - 128 + slope_trim = r.DCID_TRIM_SLOPE.val - 128 + pulldown_trim = r.DCID_PULLDOWN_TRIM.val - 128 + + Y_overall = ((reading + 0.5) * 0.01086 - 1.0 / (1 - slope_trim * 0.001375)) \ + / (614.0 + offset_trim * 0.125) + Y_pulldown = 1.0 / (1100 - pulldown_trim*2) + + if Y_overall > Y_pulldown: + Z_headphones = 1.0 / (Y_overall - Y_pulldown) + else: + Z_headphones = float('inf') + + r.MSM_BLOCK_EN3.set(DCID_EN=0) + r.DAC_CTRL2.set(PULLDOWN_R=E_PULLDOWN_R.NONE) + + return Z_headphones + +def init_ring_tip_sense(): + l84.write(0x1471, 0xe0) + l84.write(0x1473, 0xc0) + l84.write(0x1477, 0x02) + l84.write(0x1478, 0x8f) + + l84.regs.RING_SENSE_CTRL.set(INV=1, UNK1=1, + RISETIME=E_DEBOUNCE_TIME.T_125MS, FALLTIME=E_DEBOUNCE_TIME.T_125MS) + l84.regs.TIP_SENSE_CTRL.set(INV=1, + RISETIME=E_DEBOUNCE_TIME.T_500MS, FALLTIME=E_DEBOUNCE_TIME.T_125MS) + l84.regs.MSM_BLOCK_EN3.set(TR_SENSE_EN=1) + +def wait_for_plug(): + while not l84.regs.TR_SENSE_STATUS.reg.TIP_PLUG: + time.sleep(0.001) + +def wait_for_unplug(): + while l84.regs.TR_SENSE_STATUS.reg.TIP_UNPLUG: + time.sleep(0.001) + + +p.pmgr_adt_clocks_enable("/arm-io/i2c2") +i2c2 = I2C(u, "/arm-io/i2c2") + +p.write32(0x2921f0010, 0x76a02) # invoke reset +p.write32(0x2921f0010, 0x76a03) # out of reset + +l84 = CS42L84(i2c2, 0x4b) + +init_ring_tip_sense() + +while True: + print("Waiting for plug... ", end=""); sys.stdout.flush() + wait_for_plug() + + print("measuring... ", end=""); sys.stdout.flush() + print(f"{sense_Z():.1f} ohms... ", end=""); sys.stdout.flush() + + wait_for_unplug() + print("yanked")