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

ASoC: rt5677: Allow arbitrary block read/write via SPI

Added rt5677_spi_read() and refactored rt5677_spi_write() so that
an arbitrary block in the DSP address space can be read/written
via SPI. For example, this allows us to load an ELF DSP firmware
with sparse sections, and stream audio samples from DSP ring buffer.

Signed-off-by: Ben Zhang <benzh@chromium.org>
Acked-by: Oder Chiou <oder_chiou@realtek.com>
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Ben Zhang and committed by
Mark Brown
7d4d443e f8163c86

+184 -73
+179 -66
sound/soc/codecs/rt5677-spi.c
··· 31 31 32 32 #include "rt5677-spi.h" 33 33 34 - static struct spi_device *g_spi; 34 + #define RT5677_SPI_BURST_LEN 240 35 + #define RT5677_SPI_HEADER 5 36 + #define RT5677_SPI_FREQ 6000000 35 37 36 - /** 37 - * rt5677_spi_write - Write data to SPI. 38 - * @txbuf: Data Buffer for writing. 39 - * @len: Data length. 40 - * 41 - * 42 - * Returns true for success. 38 + /* The AddressPhase and DataPhase of SPI commands are MSB first on the wire. 39 + * DataPhase word size of 16-bit commands is 2 bytes. 40 + * DataPhase word size of 32-bit commands is 4 bytes. 41 + * DataPhase word size of burst commands is 8 bytes. 42 + * The DSP CPU is little-endian. 43 43 */ 44 - int rt5677_spi_write(u8 *txbuf, size_t len) 44 + #define RT5677_SPI_WRITE_BURST 0x5 45 + #define RT5677_SPI_READ_BURST 0x4 46 + #define RT5677_SPI_WRITE_32 0x3 47 + #define RT5677_SPI_READ_32 0x2 48 + #define RT5677_SPI_WRITE_16 0x1 49 + #define RT5677_SPI_READ_16 0x0 50 + 51 + static struct spi_device *g_spi; 52 + static DEFINE_MUTEX(spi_mutex); 53 + 54 + /* Select a suitable transfer command for the next transfer to ensure 55 + * the transfer address is always naturally aligned while minimizing 56 + * the total number of transfers required. 57 + * 58 + * 3 transfer commands are available: 59 + * RT5677_SPI_READ/WRITE_16: Transfer 2 bytes 60 + * RT5677_SPI_READ/WRITE_32: Transfer 4 bytes 61 + * RT5677_SPI_READ/WRITE_BURST: Transfer any multiples of 8 bytes 62 + * 63 + * For example, reading 260 bytes at 0x60030002 uses the following commands: 64 + * 0x60030002 RT5677_SPI_READ_16 2 bytes 65 + * 0x60030004 RT5677_SPI_READ_32 4 bytes 66 + * 0x60030008 RT5677_SPI_READ_BURST 240 bytes 67 + * 0x600300F8 RT5677_SPI_READ_BURST 8 bytes 68 + * 0x60030100 RT5677_SPI_READ_32 4 bytes 69 + * 0x60030104 RT5677_SPI_READ_16 2 bytes 70 + * 71 + * Input: 72 + * @read: true for read commands; false for write commands 73 + * @align: alignment of the next transfer address 74 + * @remain: number of bytes remaining to transfer 75 + * 76 + * Output: 77 + * @len: number of bytes to transfer with the selected command 78 + * Returns the selected command 79 + */ 80 + static u8 rt5677_spi_select_cmd(bool read, u32 align, u32 remain, u32 *len) 45 81 { 46 - int status; 82 + u8 cmd; 47 83 48 - status = spi_write(g_spi, txbuf, len); 84 + if (align == 2 || align == 6 || remain == 2) { 85 + cmd = RT5677_SPI_READ_16; 86 + *len = 2; 87 + } else if (align == 4 || remain <= 6) { 88 + cmd = RT5677_SPI_READ_32; 89 + *len = 4; 90 + } else { 91 + cmd = RT5677_SPI_READ_BURST; 92 + *len = min_t(u32, remain & ~7, RT5677_SPI_BURST_LEN); 93 + } 94 + return read ? cmd : cmd + 1; 95 + } 49 96 50 - if (status) 51 - dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status); 97 + /* Copy dstlen bytes from src to dst, while reversing byte order for each word. 98 + * If srclen < dstlen, zeros are padded. 99 + */ 100 + static void rt5677_spi_reverse(u8 *dst, u32 dstlen, const u8 *src, u32 srclen) 101 + { 102 + u32 w, i, si; 103 + u32 word_size = min_t(u32, dstlen, 8); 52 104 105 + for (w = 0; w < dstlen; w += word_size) { 106 + for (i = 0; i < word_size; i++) { 107 + si = w + word_size - i - 1; 108 + dst[w + i] = si < srclen ? src[si] : 0; 109 + } 110 + } 111 + } 112 + 113 + /* Read DSP address space using SPI. addr and len have to be 2-byte aligned. */ 114 + int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) 115 + { 116 + u32 offset; 117 + int status = 0; 118 + struct spi_transfer t[2]; 119 + struct spi_message m; 120 + /* +4 bytes is for the DummyPhase following the AddressPhase */ 121 + u8 header[RT5677_SPI_HEADER + 4]; 122 + u8 body[RT5677_SPI_BURST_LEN]; 123 + u8 spi_cmd; 124 + u8 *cb = rxbuf; 125 + 126 + if (!g_spi) 127 + return -ENODEV; 128 + 129 + if ((addr & 1) || (len & 1)) { 130 + dev_err(&g_spi->dev, "Bad read align 0x%x(%zu)\n", addr, len); 131 + return -EACCES; 132 + } 133 + 134 + memset(t, 0, sizeof(t)); 135 + t[0].tx_buf = header; 136 + t[0].len = sizeof(header); 137 + t[0].speed_hz = RT5677_SPI_FREQ; 138 + t[1].rx_buf = body; 139 + t[1].speed_hz = RT5677_SPI_FREQ; 140 + spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t)); 141 + 142 + for (offset = 0; offset < len; offset += t[1].len) { 143 + spi_cmd = rt5677_spi_select_cmd(true, (addr + offset) & 7, 144 + len - offset, &t[1].len); 145 + 146 + /* Construct SPI message header */ 147 + header[0] = spi_cmd; 148 + header[1] = ((addr + offset) & 0xff000000) >> 24; 149 + header[2] = ((addr + offset) & 0x00ff0000) >> 16; 150 + header[3] = ((addr + offset) & 0x0000ff00) >> 8; 151 + header[4] = ((addr + offset) & 0x000000ff) >> 0; 152 + 153 + mutex_lock(&spi_mutex); 154 + status |= spi_sync(g_spi, &m); 155 + mutex_unlock(&spi_mutex); 156 + 157 + /* Copy data back to caller buffer */ 158 + rt5677_spi_reverse(cb + offset, t[1].len, body, t[1].len); 159 + } 160 + return status; 161 + } 162 + EXPORT_SYMBOL_GPL(rt5677_spi_read); 163 + 164 + /* Write DSP address space using SPI. addr has to be 2-byte aligned. 165 + * If len is not 2-byte aligned, an extra byte of zero is written at the end 166 + * as padding. 167 + */ 168 + int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) 169 + { 170 + u32 offset, len_with_pad = len; 171 + int status = 0; 172 + struct spi_transfer t; 173 + struct spi_message m; 174 + /* +1 byte is for the DummyPhase following the DataPhase */ 175 + u8 buf[RT5677_SPI_HEADER + RT5677_SPI_BURST_LEN + 1]; 176 + u8 *body = buf + RT5677_SPI_HEADER; 177 + u8 spi_cmd; 178 + const u8 *cb = txbuf; 179 + 180 + if (!g_spi) 181 + return -ENODEV; 182 + 183 + if (addr & 1) { 184 + dev_err(&g_spi->dev, "Bad write align 0x%x(%zu)\n", addr, len); 185 + return -EACCES; 186 + } 187 + 188 + if (len & 1) 189 + len_with_pad = len + 1; 190 + 191 + memset(&t, 0, sizeof(t)); 192 + t.tx_buf = buf; 193 + t.speed_hz = RT5677_SPI_FREQ; 194 + spi_message_init_with_transfers(&m, &t, 1); 195 + 196 + for (offset = 0; offset < len_with_pad;) { 197 + spi_cmd = rt5677_spi_select_cmd(false, (addr + offset) & 7, 198 + len_with_pad - offset, &t.len); 199 + 200 + /* Construct SPI message header */ 201 + buf[0] = spi_cmd; 202 + buf[1] = ((addr + offset) & 0xff000000) >> 24; 203 + buf[2] = ((addr + offset) & 0x00ff0000) >> 16; 204 + buf[3] = ((addr + offset) & 0x0000ff00) >> 8; 205 + buf[4] = ((addr + offset) & 0x000000ff) >> 0; 206 + 207 + /* Fetch data from caller buffer */ 208 + rt5677_spi_reverse(body, t.len, cb + offset, len - offset); 209 + offset += t.len; 210 + t.len += RT5677_SPI_HEADER + 1; 211 + 212 + mutex_lock(&spi_mutex); 213 + status |= spi_sync(g_spi, &m); 214 + mutex_unlock(&spi_mutex); 215 + } 53 216 return status; 54 217 } 55 218 EXPORT_SYMBOL_GPL(rt5677_spi_write); 56 219 57 - /** 58 - * rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address. 59 - * @addr: Start address. 60 - * @txbuf: Data Buffer for writng. 61 - * @len: Data length, it must be a multiple of 8. 62 - * 63 - * 64 - * Returns true for success. 65 - */ 66 - int rt5677_spi_burst_write(u32 addr, const struct firmware *fw) 220 + int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw) 67 221 { 68 - u8 spi_cmd = RT5677_SPI_CMD_BURST_WRITE; 69 - u8 *write_buf; 70 - unsigned int i, end, offset = 0; 71 - 72 - write_buf = kmalloc(RT5677_SPI_BUF_LEN + 6, GFP_KERNEL); 73 - 74 - if (write_buf == NULL) 75 - return -ENOMEM; 76 - 77 - while (offset < fw->size) { 78 - if (offset + RT5677_SPI_BUF_LEN <= fw->size) 79 - end = RT5677_SPI_BUF_LEN; 80 - else 81 - end = fw->size % RT5677_SPI_BUF_LEN; 82 - 83 - write_buf[0] = spi_cmd; 84 - write_buf[1] = ((addr + offset) & 0xff000000) >> 24; 85 - write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16; 86 - write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8; 87 - write_buf[4] = ((addr + offset) & 0x000000ff) >> 0; 88 - 89 - for (i = 0; i < end; i += 8) { 90 - write_buf[i + 12] = fw->data[offset + i + 0]; 91 - write_buf[i + 11] = fw->data[offset + i + 1]; 92 - write_buf[i + 10] = fw->data[offset + i + 2]; 93 - write_buf[i + 9] = fw->data[offset + i + 3]; 94 - write_buf[i + 8] = fw->data[offset + i + 4]; 95 - write_buf[i + 7] = fw->data[offset + i + 5]; 96 - write_buf[i + 6] = fw->data[offset + i + 6]; 97 - write_buf[i + 5] = fw->data[offset + i + 7]; 98 - } 99 - 100 - write_buf[end + 5] = spi_cmd; 101 - 102 - rt5677_spi_write(write_buf, end + 6); 103 - 104 - offset += RT5677_SPI_BUF_LEN; 105 - } 106 - 107 - kfree(write_buf); 108 - 109 - return 0; 222 + return rt5677_spi_write(addr, fw->data, fw->size); 110 223 } 111 - EXPORT_SYMBOL_GPL(rt5677_spi_burst_write); 224 + EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware); 112 225 113 226 static int rt5677_spi_probe(struct spi_device *spi) 114 227 {
+3 -5
sound/soc/codecs/rt5677-spi.h
··· 12 12 #ifndef __RT5677_SPI_H__ 13 13 #define __RT5677_SPI_H__ 14 14 15 - #define RT5677_SPI_BUF_LEN 240 16 - #define RT5677_SPI_CMD_BURST_WRITE 0x05 17 - 18 - int rt5677_spi_write(u8 *txbuf, size_t len); 19 - int rt5677_spi_burst_write(u32 addr, const struct firmware *fw); 15 + int rt5677_spi_read(u32 addr, void *rxbuf, size_t len); 16 + int rt5677_spi_write(u32 addr, const void *txbuf, size_t len); 17 + int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw); 20 18 21 19 #endif /* __RT5677_SPI_H__ */
+2 -2
sound/soc/codecs/rt5677.c
··· 745 745 ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1, 746 746 codec->dev); 747 747 if (ret == 0) { 748 - rt5677_spi_burst_write(0x50000000, rt5677->fw1); 748 + rt5677_spi_write_firmware(0x50000000, rt5677->fw1); 749 749 release_firmware(rt5677->fw1); 750 750 } 751 751 752 752 ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2, 753 753 codec->dev); 754 754 if (ret == 0) { 755 - rt5677_spi_burst_write(0x60000000, rt5677->fw2); 755 + rt5677_spi_write_firmware(0x60000000, rt5677->fw2); 756 756 release_firmware(rt5677->fw2); 757 757 } 758 758