Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: cs35l56: Handle OTP read latency over SoundWire

Use the late-read buffer in the CS35L56 SoundWire interface to
read OTP memory.

The OTP memory has a longer access latency than chip registers
and cannot guarantee to return the data value in the SoundWire
control response if the bus clock is >4.8 MHz. The Cirrus
SoundWire peripheral IP exposes the bridge-to-bus read buffer
and status bits. For a read from OTP the bridge status bits are
polled to wait for the OTP data to be loaded into the read buffer
and the data is then read from there.

Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Fixes: e1830f66f6c6 ("ASoC: cs35l56: Add helper functions for amp calibration")
Link: https://patch.msgid.link/20240805140839.26042-1-rf@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Richard Fitzgerald and committed by
Mark Brown
e42066df 9a1af1e2

+82
+5
include/sound/cs35l56.h
··· 277 277 return 0; 278 278 } 279 279 280 + static inline bool cs35l56_is_otp_register(unsigned int reg) 281 + { 282 + return (reg >> 16) == 3; 283 + } 284 + 280 285 extern struct regmap_config cs35l56_regmap_i2c; 281 286 extern struct regmap_config cs35l56_regmap_spi; 282 287 extern struct regmap_config cs35l56_regmap_sdw;
+77
sound/soc/codecs/cs35l56-sdw.c
··· 23 23 /* Register addresses are offset when sent over SoundWire */ 24 24 #define CS35L56_SDW_ADDR_OFFSET 0x8000 25 25 26 + /* Cirrus bus bridge registers */ 27 + #define CS35L56_SDW_MEM_ACCESS_STATUS 0xd0 28 + #define CS35L56_SDW_MEM_READ_DATA 0xd8 29 + 30 + #define CS35L56_SDW_LAST_LATE BIT(3) 31 + #define CS35L56_SDW_CMD_IN_PROGRESS BIT(2) 32 + #define CS35L56_SDW_RDATA_RDY BIT(0) 33 + 34 + #define CS35L56_LATE_READ_POLL_US 10 35 + #define CS35L56_LATE_READ_TIMEOUT_US 1000 36 + 37 + static int cs35l56_sdw_poll_mem_status(struct sdw_slave *peripheral, 38 + unsigned int mask, 39 + unsigned int match) 40 + { 41 + int ret, val; 42 + 43 + ret = read_poll_timeout(sdw_read_no_pm, val, 44 + (val < 0) || ((val & mask) == match), 45 + CS35L56_LATE_READ_POLL_US, CS35L56_LATE_READ_TIMEOUT_US, 46 + false, peripheral, CS35L56_SDW_MEM_ACCESS_STATUS); 47 + if (ret < 0) 48 + return ret; 49 + 50 + if (val < 0) 51 + return val; 52 + 53 + return 0; 54 + } 55 + 56 + static int cs35l56_sdw_slow_read(struct sdw_slave *peripheral, unsigned int reg, 57 + u8 *buf, size_t val_size) 58 + { 59 + int ret, i; 60 + 61 + reg += CS35L56_SDW_ADDR_OFFSET; 62 + 63 + for (i = 0; i < val_size; i += sizeof(u32)) { 64 + /* Poll for bus bridge idle */ 65 + ret = cs35l56_sdw_poll_mem_status(peripheral, 66 + CS35L56_SDW_CMD_IN_PROGRESS, 67 + 0); 68 + if (ret < 0) { 69 + dev_err(&peripheral->dev, "!CMD_IN_PROGRESS fail: %d\n", ret); 70 + return ret; 71 + } 72 + 73 + /* Reading LSByte triggers read of register to holding buffer */ 74 + sdw_read_no_pm(peripheral, reg + i); 75 + 76 + /* Wait for data available */ 77 + ret = cs35l56_sdw_poll_mem_status(peripheral, 78 + CS35L56_SDW_RDATA_RDY, 79 + CS35L56_SDW_RDATA_RDY); 80 + if (ret < 0) { 81 + dev_err(&peripheral->dev, "RDATA_RDY fail: %d\n", ret); 82 + return ret; 83 + } 84 + 85 + /* Read data from buffer */ 86 + ret = sdw_nread_no_pm(peripheral, CS35L56_SDW_MEM_READ_DATA, 87 + sizeof(u32), &buf[i]); 88 + if (ret) { 89 + dev_err(&peripheral->dev, "Late read @%#x failed: %d\n", reg + i, ret); 90 + return ret; 91 + } 92 + 93 + swab32s((u32 *)&buf[i]); 94 + } 95 + 96 + return 0; 97 + } 98 + 26 99 static int cs35l56_sdw_read_one(struct sdw_slave *peripheral, unsigned int reg, void *buf) 27 100 { 28 101 int ret; ··· 121 48 int ret; 122 49 123 50 reg = le32_to_cpu(*(const __le32 *)reg_buf); 51 + 52 + if (cs35l56_is_otp_register(reg)) 53 + return cs35l56_sdw_slow_read(peripheral, reg, buf8, val_size); 54 + 124 55 reg += CS35L56_SDW_ADDR_OFFSET; 125 56 126 57 if (val_size == 4)