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

firmware: cs_dsp: Add memory chunk helpers

Add helpers that can be layered on top of a buffer read from or to be
written to the DSP to faciliate accessing datastructures within the DSP
memory. These functions handle adding the padding bytes for the DSP,
converting to big endian, and packing arbitrary length data.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20220722094851.92521-2-ckeepax@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Charles Keepax and committed by
Mark Brown
a4b97655 dea99773

+177
+104
drivers/firmware/cirrus/cs_dsp.c
··· 3180 3180 .stop_core = cs_dsp_halo_stop_core, 3181 3181 }; 3182 3182 3183 + /** 3184 + * cs_dsp_chunk_write() - Format data to a DSP memory chunk 3185 + * @ch: Pointer to the chunk structure 3186 + * @nbits: Number of bits to write 3187 + * @val: Value to write 3188 + * 3189 + * This function sequentially writes values into the format required for DSP 3190 + * memory, it handles both inserting of the padding bytes and converting to 3191 + * big endian. Note that data is only committed to the chunk when a whole DSP 3192 + * words worth of data is available. 3193 + * 3194 + * Return: Zero for success, a negative number on error. 3195 + */ 3196 + int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val) 3197 + { 3198 + int nwrite, i; 3199 + 3200 + nwrite = min(CS_DSP_DATA_WORD_BITS - ch->cachebits, nbits); 3201 + 3202 + ch->cache <<= nwrite; 3203 + ch->cache |= val >> (nbits - nwrite); 3204 + ch->cachebits += nwrite; 3205 + nbits -= nwrite; 3206 + 3207 + if (ch->cachebits == CS_DSP_DATA_WORD_BITS) { 3208 + if (cs_dsp_chunk_end(ch)) 3209 + return -ENOSPC; 3210 + 3211 + ch->cache &= 0xFFFFFF; 3212 + for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= BITS_PER_BYTE) 3213 + *ch->data++ = (ch->cache & 0xFF000000) >> CS_DSP_DATA_WORD_BITS; 3214 + 3215 + ch->bytes += sizeof(ch->cache); 3216 + ch->cachebits = 0; 3217 + } 3218 + 3219 + if (nbits) 3220 + return cs_dsp_chunk_write(ch, nbits, val); 3221 + 3222 + return 0; 3223 + } 3224 + EXPORT_SYMBOL_GPL(cs_dsp_chunk_write); 3225 + 3226 + /** 3227 + * cs_dsp_chunk_flush() - Pad remaining data with zero and commit to chunk 3228 + * @ch: Pointer to the chunk structure 3229 + * 3230 + * As cs_dsp_chunk_write only writes data when a whole DSP word is ready to 3231 + * be written out it is possible that some data will remain in the cache, this 3232 + * function will pad that data with zeros upto a whole DSP word and write out. 3233 + * 3234 + * Return: Zero for success, a negative number on error. 3235 + */ 3236 + int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch) 3237 + { 3238 + if (!ch->cachebits) 3239 + return 0; 3240 + 3241 + return cs_dsp_chunk_write(ch, CS_DSP_DATA_WORD_BITS - ch->cachebits, 0); 3242 + } 3243 + EXPORT_SYMBOL_GPL(cs_dsp_chunk_flush); 3244 + 3245 + /** 3246 + * cs_dsp_chunk_read() - Parse data from a DSP memory chunk 3247 + * @ch: Pointer to the chunk structure 3248 + * @nbits: Number of bits to read 3249 + * 3250 + * This function sequentially reads values from a DSP memory formatted buffer, 3251 + * it handles both removing of the padding bytes and converting from big endian. 3252 + * 3253 + * Return: A negative number is returned on error, otherwise the read value. 3254 + */ 3255 + int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits) 3256 + { 3257 + int nread, i; 3258 + u32 result; 3259 + 3260 + if (!ch->cachebits) { 3261 + if (cs_dsp_chunk_end(ch)) 3262 + return -ENOSPC; 3263 + 3264 + ch->cache = 0; 3265 + ch->cachebits = CS_DSP_DATA_WORD_BITS; 3266 + 3267 + for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= BITS_PER_BYTE) 3268 + ch->cache |= *ch->data++; 3269 + 3270 + ch->bytes += sizeof(ch->cache); 3271 + } 3272 + 3273 + nread = min(ch->cachebits, nbits); 3274 + nbits -= nread; 3275 + 3276 + result = ch->cache >> ((sizeof(ch->cache) * BITS_PER_BYTE) - nread); 3277 + ch->cache <<= nread; 3278 + ch->cachebits -= nread; 3279 + 3280 + if (nbits) 3281 + result = (result << nbits) | cs_dsp_chunk_read(ch, nbits); 3282 + 3283 + return result; 3284 + } 3285 + EXPORT_SYMBOL_GPL(cs_dsp_chunk_read); 3286 + 3183 3287 MODULE_DESCRIPTION("Cirrus Logic DSP Support"); 3184 3288 MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); 3185 3289 MODULE_LICENSE("GPL v2");
+73
include/linux/firmware/cirrus/cs_dsp.h
··· 11 11 #ifndef __CS_DSP_H 12 12 #define __CS_DSP_H 13 13 14 + #include <linux/bits.h> 14 15 #include <linux/device.h> 15 16 #include <linux/firmware.h> 16 17 #include <linux/list.h> ··· 35 34 #define CS_ADSP2_REGION_ALL (CS_ADSP2_REGION_0 | CS_ADSP2_REGION_1_9) 36 35 37 36 #define CS_DSP_DATA_WORD_SIZE 3 37 + #define CS_DSP_DATA_WORD_BITS (3 * BITS_PER_BYTE) 38 38 39 39 #define CS_DSP_ACKED_CTL_TIMEOUT_MS 100 40 40 #define CS_DSP_ACKED_CTL_N_QUICKPOLLS 10 ··· 253 251 int type, unsigned int id); 254 252 255 253 const char *cs_dsp_mem_region_name(unsigned int type); 254 + 255 + /** 256 + * struct cs_dsp_chunk - Describes a buffer holding data formatted for the DSP 257 + * @data: Pointer to underlying buffer memory 258 + * @max: Pointer to end of the buffer memory 259 + * @bytes: Number of bytes read/written into the memory chunk 260 + * @cache: Temporary holding data as it is formatted 261 + * @cachebits: Number of bits of data currently in cache 262 + */ 263 + struct cs_dsp_chunk { 264 + u8 *data; 265 + u8 *max; 266 + int bytes; 267 + 268 + u32 cache; 269 + int cachebits; 270 + }; 271 + 272 + /** 273 + * cs_dsp_chunk() - Create a DSP memory chunk 274 + * @data: Pointer to the buffer that will be used to store data 275 + * @size: Size of the buffer in bytes 276 + * 277 + * Return: A cs_dsp_chunk structure 278 + */ 279 + static inline struct cs_dsp_chunk cs_dsp_chunk(void *data, int size) 280 + { 281 + struct cs_dsp_chunk ch = { 282 + .data = data, 283 + .max = data + size, 284 + }; 285 + 286 + return ch; 287 + } 288 + 289 + /** 290 + * cs_dsp_chunk_end() - Check if a DSP memory chunk is full 291 + * @ch: Pointer to the chunk structure 292 + * 293 + * Return: True if the whole buffer has been read/written 294 + */ 295 + static inline bool cs_dsp_chunk_end(struct cs_dsp_chunk *ch) 296 + { 297 + return ch->data == ch->max; 298 + } 299 + 300 + /** 301 + * cs_dsp_chunk_bytes() - Number of bytes written/read from a DSP memory chunk 302 + * @ch: Pointer to the chunk structure 303 + * 304 + * Return: Number of bytes read/written to the buffer 305 + */ 306 + static inline int cs_dsp_chunk_bytes(struct cs_dsp_chunk *ch) 307 + { 308 + return ch->bytes; 309 + } 310 + 311 + /** 312 + * cs_dsp_chunk_valid_addr() - Check if an address is in a DSP memory chunk 313 + * @ch: Pointer to the chunk structure 314 + * 315 + * Return: True if the given address is within the buffer 316 + */ 317 + static inline bool cs_dsp_chunk_valid_addr(struct cs_dsp_chunk *ch, void *addr) 318 + { 319 + return (u8 *)addr >= ch->data && (u8 *)addr < ch->max; 320 + } 321 + 322 + int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val); 323 + int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch); 324 + int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits); 256 325 257 326 #endif