Commit a4b97655 authored by Charles Keepax's avatar Charles Keepax Committed by Mark Brown

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: default avatarCharles Keepax <ckeepax@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20220722094851.92521-2-ckeepax@opensource.cirrus.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent dea99773
...@@ -3180,6 +3180,110 @@ static const struct cs_dsp_ops cs_dsp_halo_ops = { ...@@ -3180,6 +3180,110 @@ static const struct cs_dsp_ops cs_dsp_halo_ops = {
.stop_core = cs_dsp_halo_stop_core, .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_DESCRIPTION("Cirrus Logic DSP Support");
MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#ifndef __CS_DSP_H #ifndef __CS_DSP_H
#define __CS_DSP_H #define __CS_DSP_H
#include <linux/bits.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/list.h> #include <linux/list.h>
...@@ -34,6 +35,7 @@ ...@@ -34,6 +35,7 @@
#define CS_ADSP2_REGION_ALL (CS_ADSP2_REGION_0 | CS_ADSP2_REGION_1_9) #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_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_TIMEOUT_MS 100
#define CS_DSP_ACKED_CTL_N_QUICKPOLLS 10 #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, ...@@ -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); 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 #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment