diff options
-rw-r--r-- | drivers/firmware/cirrus/cs_dsp.c | 104 | ||||
-rw-r--r-- | include/linux/firmware/cirrus/cs_dsp.h | 73 |
2 files changed, 177 insertions, 0 deletions
diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index b402f841d72c..81cc3d0f6eec 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -3180,6 +3180,110 @@ static const struct cs_dsp_ops cs_dsp_halo_ops = { .stop_core = cs_dsp_halo_stop_core, }; +/** + * cs_dsp_chunk_write() - Format data to a DSP memory chunk + * @ch: Pointer to the chunk structure + * @nbits: Number of bits to write + * @val: Value to write + * + * This function sequentially writes values into the format required for DSP + * memory, it handles both inserting of the padding bytes and converting to + * big endian. Note that data is only committed to the chunk when a whole DSP + * words worth of data is available. + * + * Return: Zero for success, a negative number on error. + */ +int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val) +{ + int nwrite, i; + + nwrite = min(CS_DSP_DATA_WORD_BITS - ch->cachebits, nbits); + + ch->cache <<= nwrite; + ch->cache |= val >> (nbits - nwrite); + ch->cachebits += nwrite; + nbits -= nwrite; + + if (ch->cachebits == CS_DSP_DATA_WORD_BITS) { + if (cs_dsp_chunk_end(ch)) + return -ENOSPC; + + ch->cache &= 0xFFFFFF; + for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= BITS_PER_BYTE) + *ch->data++ = (ch->cache & 0xFF000000) >> CS_DSP_DATA_WORD_BITS; + + ch->bytes += sizeof(ch->cache); + ch->cachebits = 0; + } + + if (nbits) + return cs_dsp_chunk_write(ch, nbits, val); + + return 0; +} +EXPORT_SYMBOL_GPL(cs_dsp_chunk_write); + +/** + * cs_dsp_chunk_flush() - Pad remaining data with zero and commit to chunk + * @ch: Pointer to the chunk structure + * + * As cs_dsp_chunk_write only writes data when a whole DSP word is ready to + * be written out it is possible that some data will remain in the cache, this + * function will pad that data with zeros upto a whole DSP word and write out. + * + * Return: Zero for success, a negative number on error. + */ +int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch) +{ + if (!ch->cachebits) + return 0; + + return cs_dsp_chunk_write(ch, CS_DSP_DATA_WORD_BITS - ch->cachebits, 0); +} +EXPORT_SYMBOL_GPL(cs_dsp_chunk_flush); + +/** + * cs_dsp_chunk_read() - Parse data from a DSP memory chunk + * @ch: Pointer to the chunk structure + * @nbits: Number of bits to read + * + * This function sequentially reads values from a DSP memory formatted buffer, + * it handles both removing of the padding bytes and converting from big endian. + * + * Return: A negative number is returned on error, otherwise the read value. + */ +int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits) +{ + int nread, i; + u32 result; + + if (!ch->cachebits) { + if (cs_dsp_chunk_end(ch)) + return -ENOSPC; + + ch->cache = 0; + ch->cachebits = CS_DSP_DATA_WORD_BITS; + + for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= BITS_PER_BYTE) + ch->cache |= *ch->data++; + + ch->bytes += sizeof(ch->cache); + } + + nread = min(ch->cachebits, nbits); + nbits -= nread; + + result = ch->cache >> ((sizeof(ch->cache) * BITS_PER_BYTE) - nread); + ch->cache <<= nread; + ch->cachebits -= nread; + + if (nbits) + result = (result << nbits) | cs_dsp_chunk_read(ch, nbits); + + return result; +} +EXPORT_SYMBOL_GPL(cs_dsp_chunk_read); + MODULE_DESCRIPTION("Cirrus Logic DSP Support"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 6ab230218df0..cad828e21c72 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -11,6 +11,7 @@ #ifndef __CS_DSP_H #define __CS_DSP_H +#include <linux/bits.h> #include <linux/device.h> #include <linux/firmware.h> #include <linux/list.h> @@ -34,6 +35,7 @@ #define CS_ADSP2_REGION_ALL (CS_ADSP2_REGION_0 | CS_ADSP2_REGION_1_9) #define CS_DSP_DATA_WORD_SIZE 3 +#define CS_DSP_DATA_WORD_BITS (3 * BITS_PER_BYTE) #define CS_DSP_ACKED_CTL_TIMEOUT_MS 100 #define CS_DSP_ACKED_CTL_N_QUICKPOLLS 10 @@ -252,4 +254,75 @@ struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp, const char *cs_dsp_mem_region_name(unsigned int type); +/** + * struct cs_dsp_chunk - Describes a buffer holding data formatted for the DSP + * @data: Pointer to underlying buffer memory + * @max: Pointer to end of the buffer memory + * @bytes: Number of bytes read/written into the memory chunk + * @cache: Temporary holding data as it is formatted + * @cachebits: Number of bits of data currently in cache + */ +struct cs_dsp_chunk { + u8 *data; + u8 *max; + int bytes; + + u32 cache; + int cachebits; +}; + +/** + * cs_dsp_chunk() - Create a DSP memory chunk + * @data: Pointer to the buffer that will be used to store data + * @size: Size of the buffer in bytes + * + * Return: A cs_dsp_chunk structure + */ +static inline struct cs_dsp_chunk cs_dsp_chunk(void *data, int size) +{ + struct cs_dsp_chunk ch = { + .data = data, + .max = data + size, + }; + + return ch; +} + +/** + * cs_dsp_chunk_end() - Check if a DSP memory chunk is full + * @ch: Pointer to the chunk structure + * + * Return: True if the whole buffer has been read/written + */ +static inline bool cs_dsp_chunk_end(struct cs_dsp_chunk *ch) +{ + return ch->data == ch->max; +} + +/** + * cs_dsp_chunk_bytes() - Number of bytes written/read from a DSP memory chunk + * @ch: Pointer to the chunk structure + * + * Return: Number of bytes read/written to the buffer + */ +static inline int cs_dsp_chunk_bytes(struct cs_dsp_chunk *ch) +{ + return ch->bytes; +} + +/** + * cs_dsp_chunk_valid_addr() - Check if an address is in a DSP memory chunk + * @ch: Pointer to the chunk structure + * + * Return: True if the given address is within the buffer + */ +static inline bool cs_dsp_chunk_valid_addr(struct cs_dsp_chunk *ch, void *addr) +{ + return (u8 *)addr >= ch->data && (u8 *)addr < ch->max; +} + +int cs_dsp_chunk_write(struct cs_dsp_chunk *ch, int nbits, u32 val); +int cs_dsp_chunk_flush(struct cs_dsp_chunk *ch); +int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits); + #endif |