Message ID | 20240620161745.2312359-2-jogletre@opensource.cirrus.com (mailing list archive) |
---|---|
State | Mainlined |
Commit | 205fdba5d0ffe1ad8de61763d74323e88b640d41 |
Headers | show |
Series | Add support for CS40L50 | expand |
Hi James, On Thu, Jun 20, 2024 at 04:17:41PM +0000, James Ogletree wrote: > A write sequence is a sequence of register addresses > and values executed by some Cirrus DSPs following > certain power state transitions. > > Add support for Cirrus drivers to update or add to a > write sequence present in firmware. > > Reviewed-by: Charles Keepax <ckeepax@opensource.cirrus.com> > Signed-off-by: James Ogletree <jogletre@opensource.cirrus.com> Reviewed-by: Jeff LaBundy <jeff@labundy.com> Thanks again for your great work here. This entire driver is well organized, scalable, and serves as a great template for future FF devices with similar use-cases. Nice job! Kind regards, Jeff LaBundy > --- > Note that this patch can be applied before the others. > > drivers/firmware/cirrus/cs_dsp.c | 278 +++++++++++++++++++++++++ > include/linux/firmware/cirrus/cs_dsp.h | 27 +++ > 2 files changed, 305 insertions(+) > > diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c > index 79d4254d1f9b..6d886ffea10f 100644 > --- a/drivers/firmware/cirrus/cs_dsp.c > +++ b/drivers/firmware/cirrus/cs_dsp.c > @@ -275,6 +275,12 @@ > #define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff > #define HALO_MPU_VIO_ERR_SRC_SHIFT 0 > > +/* > + * Write Sequence > + */ > +#define WSEQ_OP_MAX_WORDS 3 > +#define WSEQ_END_OF_SCRIPT 0xFFFFFF > + > struct cs_dsp_ops { > bool (*validate_version)(struct cs_dsp *dsp, unsigned int version); > unsigned int (*parse_sizes)(struct cs_dsp *dsp, > @@ -3339,6 +3345,278 @@ int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits) > } > EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_read, FW_CS_DSP); > > + > +struct cs_dsp_wseq_op { > + struct list_head list; > + u32 address; > + u32 data; > + u16 offset; > + u8 operation; > +}; > + > +static void cs_dsp_wseq_clear(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq) > +{ > + struct cs_dsp_wseq_op *op, *op_tmp; > + > + list_for_each_entry_safe(op, op_tmp, &wseq->ops, list) { > + list_del(&op->list); > + devm_kfree(dsp->dev, op); > + } > +} > + > +static int cs_dsp_populate_wseq(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq) > +{ > + struct cs_dsp_wseq_op *op = NULL; > + struct cs_dsp_chunk chunk; > + u8 *words; > + int ret; > + > + if (!wseq->ctl) { > + cs_dsp_err(dsp, "No control for write sequence\n"); > + return -EINVAL; > + } > + > + words = kzalloc(wseq->ctl->len, GFP_KERNEL); > + if (!words) > + return -ENOMEM; > + > + ret = cs_dsp_coeff_read_ctrl(wseq->ctl, 0, words, wseq->ctl->len); > + if (ret) { > + cs_dsp_err(dsp, "Failed to read %s: %d\n", wseq->ctl->subname, ret); > + goto err_free; > + } > + > + INIT_LIST_HEAD(&wseq->ops); > + > + chunk = cs_dsp_chunk(words, wseq->ctl->len); > + > + while (!cs_dsp_chunk_end(&chunk)) { > + op = devm_kzalloc(dsp->dev, sizeof(*op), GFP_KERNEL); > + if (!op) { > + ret = -ENOMEM; > + goto err_free; > + } > + > + op->offset = cs_dsp_chunk_bytes(&chunk); > + op->operation = cs_dsp_chunk_read(&chunk, 8); > + > + switch (op->operation) { > + case CS_DSP_WSEQ_END: > + op->data = WSEQ_END_OF_SCRIPT; > + break; > + case CS_DSP_WSEQ_UNLOCK: > + op->data = cs_dsp_chunk_read(&chunk, 16); > + break; > + case CS_DSP_WSEQ_ADDR8: > + op->address = cs_dsp_chunk_read(&chunk, 8); > + op->data = cs_dsp_chunk_read(&chunk, 32); > + break; > + case CS_DSP_WSEQ_H16: > + case CS_DSP_WSEQ_L16: > + op->address = cs_dsp_chunk_read(&chunk, 24); > + op->data = cs_dsp_chunk_read(&chunk, 16); > + break; > + case CS_DSP_WSEQ_FULL: > + op->address = cs_dsp_chunk_read(&chunk, 32); > + op->data = cs_dsp_chunk_read(&chunk, 32); > + break; > + default: > + ret = -EINVAL; > + cs_dsp_err(dsp, "Unsupported op: %X\n", op->operation); > + devm_kfree(dsp->dev, op); > + goto err_free; > + } > + > + list_add_tail(&op->list, &wseq->ops); > + > + if (op->operation == CS_DSP_WSEQ_END) > + break; > + } > + > + if (op && op->operation != CS_DSP_WSEQ_END) { > + cs_dsp_err(dsp, "%s missing end terminator\n", wseq->ctl->subname); > + ret = -ENOENT; > + } > + > +err_free: > + kfree(words); > + > + return ret; > +} > + > +/** > + * cs_dsp_wseq_init() - Initialize write sequences contained within the loaded DSP firmware > + * @dsp: Pointer to DSP structure > + * @wseqs: List of write sequences to initialize > + * @num_wseqs: Number of write sequences to initialize > + * > + * Return: Zero for success, a negative number on error. > + */ > +int cs_dsp_wseq_init(struct cs_dsp *dsp, struct cs_dsp_wseq *wseqs, unsigned int num_wseqs) > +{ > + int i, ret; > + > + lockdep_assert_held(&dsp->pwr_lock); > + > + for (i = 0; i < num_wseqs; i++) { > + ret = cs_dsp_populate_wseq(dsp, &wseqs[i]); > + if (ret) { > + cs_dsp_wseq_clear(dsp, &wseqs[i]); > + return ret; > + } > + } > + > + return 0; > +} > +EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_init, FW_CS_DSP); > + > +static struct cs_dsp_wseq_op *cs_dsp_wseq_find_op(u32 addr, u8 op_code, > + struct list_head *wseq_ops) > +{ > + struct cs_dsp_wseq_op *op; > + > + list_for_each_entry(op, wseq_ops, list) { > + if (op->operation == op_code && op->address == addr) > + return op; > + } > + > + return NULL; > +} > + > +/** > + * cs_dsp_wseq_write() - Add or update an entry in a write sequence > + * @dsp: Pointer to a DSP structure > + * @wseq: Write sequence to write to > + * @addr: Address of the register to be written to > + * @data: Data to be written > + * @op_code: The type of operation of the new entry > + * @update: If true, searches for the first entry in the write sequence with > + * the same address and op_code, and replaces it. If false, creates a new entry > + * at the tail > + * > + * This function formats register address and value pairs into the format > + * required for write sequence entries, and either updates or adds the > + * new entry into the write sequence. > + * > + * If update is set to true and no matching entry is found, it will add a new entry. > + * > + * Return: Zero for success, a negative number on error. > + */ > +int cs_dsp_wseq_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq, > + u32 addr, u32 data, u8 op_code, bool update) > +{ > + struct cs_dsp_wseq_op *op_end, *op_new = NULL; > + u32 words[WSEQ_OP_MAX_WORDS]; > + struct cs_dsp_chunk chunk; > + int new_op_size, ret; > + > + if (update) > + op_new = cs_dsp_wseq_find_op(addr, op_code, &wseq->ops); > + > + /* If entry to update is not found, treat it as a new operation */ > + if (!op_new) { > + op_end = cs_dsp_wseq_find_op(0, CS_DSP_WSEQ_END, &wseq->ops); > + if (!op_end) { > + cs_dsp_err(dsp, "Missing terminator for %s\n", wseq->ctl->subname); > + return -EINVAL; > + } > + > + op_new = devm_kzalloc(dsp->dev, sizeof(*op_new), GFP_KERNEL); > + if (!op_new) > + return -ENOMEM; > + > + op_new->operation = op_code; > + op_new->address = addr; > + op_new->offset = op_end->offset; > + update = false; > + } > + > + op_new->data = data; > + > + chunk = cs_dsp_chunk(words, sizeof(words)); > + cs_dsp_chunk_write(&chunk, 8, op_new->operation); > + > + switch (op_code) { > + case CS_DSP_WSEQ_FULL: > + cs_dsp_chunk_write(&chunk, 32, op_new->address); > + cs_dsp_chunk_write(&chunk, 32, op_new->data); > + break; > + case CS_DSP_WSEQ_L16: > + case CS_DSP_WSEQ_H16: > + cs_dsp_chunk_write(&chunk, 24, op_new->address); > + cs_dsp_chunk_write(&chunk, 16, op_new->data); > + break; > + default: > + ret = -EINVAL; > + cs_dsp_err(dsp, "Operation %X not supported\n", op_code); > + goto op_new_free; > + } > + > + new_op_size = cs_dsp_chunk_bytes(&chunk); > + > + if (!update) { > + if (wseq->ctl->len - op_end->offset < new_op_size) { > + cs_dsp_err(dsp, "Not enough memory in %s for entry\n", wseq->ctl->subname); > + ret = -E2BIG; > + goto op_new_free; > + } > + > + op_end->offset += new_op_size; > + > + ret = cs_dsp_coeff_write_ctrl(wseq->ctl, op_end->offset / sizeof(u32), > + &op_end->data, sizeof(u32)); > + if (ret) > + goto op_new_free; > + > + list_add_tail(&op_new->list, &op_end->list); > + } > + > + ret = cs_dsp_coeff_write_ctrl(wseq->ctl, op_new->offset / sizeof(u32), > + words, new_op_size); > + if (ret) > + goto op_new_free; > + > + return 0; > + > +op_new_free: > + devm_kfree(dsp->dev, op_new); > + > + return ret; > +} > +EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_write, FW_CS_DSP); > + > +/** > + * cs_dsp_wseq_multi_write() - Add or update multiple entries in a write sequence > + * @dsp: Pointer to a DSP structure > + * @wseq: Write sequence to write to > + * @reg_seq: List of address-data pairs > + * @num_regs: Number of address-data pairs > + * @op_code: The types of operations of the new entries > + * @update: If true, searches for the first entry in the write sequence with > + * the same address and op_code, and replaces it. If false, creates a new entry > + * at the tail > + * > + * This function calls cs_dsp_wseq_write() for multiple address-data pairs. > + * > + * Return: Zero for success, a negative number on error. > + */ > +int cs_dsp_wseq_multi_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq, > + const struct reg_sequence *reg_seq, int num_regs, > + u8 op_code, bool update) > +{ > + int i, ret; > + > + for (i = 0; i < num_regs; i++) { > + ret = cs_dsp_wseq_write(dsp, wseq, reg_seq[i].reg, > + reg_seq[i].def, op_code, update); > + if (ret) > + return ret; > + } > + > + return 0; > +} > +EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_multi_write, FW_CS_DSP); > + > 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 29cd11d5a3cf..4cef6fafa1d8 100644 > --- a/include/linux/firmware/cirrus/cs_dsp.h > +++ b/include/linux/firmware/cirrus/cs_dsp.h > @@ -42,6 +42,16 @@ > #define CS_DSP_ACKED_CTL_MIN_VALUE 0 > #define CS_DSP_ACKED_CTL_MAX_VALUE 0xFFFFFF > > +/* > + * Write sequence operation codes > + */ > +#define CS_DSP_WSEQ_FULL 0x00 > +#define CS_DSP_WSEQ_ADDR8 0x02 > +#define CS_DSP_WSEQ_L16 0x04 > +#define CS_DSP_WSEQ_H16 0x05 > +#define CS_DSP_WSEQ_UNLOCK 0xFD > +#define CS_DSP_WSEQ_END 0xFF > + > /** > * struct cs_dsp_region - Describes a logical memory region in DSP address space > * @type: Memory region type > @@ -255,6 +265,23 @@ 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_wseq - Describes a write sequence > + * @ctl: Write sequence cs_dsp control > + * @ops: Operations contained within > + */ > +struct cs_dsp_wseq { > + struct cs_dsp_coeff_ctl *ctl; > + struct list_head ops; > +}; > + > +int cs_dsp_wseq_init(struct cs_dsp *dsp, struct cs_dsp_wseq *wseqs, unsigned int num_wseqs); > +int cs_dsp_wseq_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq, u32 addr, u32 data, > + u8 op_code, bool update); > +int cs_dsp_wseq_multi_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq, > + const struct reg_sequence *reg_seq, int num_regs, > + u8 op_code, bool update); > + > /** > * struct cs_dsp_chunk - Describes a buffer holding data formatted for the DSP > * @data: Pointer to underlying buffer memory > -- > 2.34.1 >
diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 79d4254d1f9b..6d886ffea10f 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -275,6 +275,12 @@ #define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff #define HALO_MPU_VIO_ERR_SRC_SHIFT 0 +/* + * Write Sequence + */ +#define WSEQ_OP_MAX_WORDS 3 +#define WSEQ_END_OF_SCRIPT 0xFFFFFF + struct cs_dsp_ops { bool (*validate_version)(struct cs_dsp *dsp, unsigned int version); unsigned int (*parse_sizes)(struct cs_dsp *dsp, @@ -3339,6 +3345,278 @@ int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits) } EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_read, FW_CS_DSP); + +struct cs_dsp_wseq_op { + struct list_head list; + u32 address; + u32 data; + u16 offset; + u8 operation; +}; + +static void cs_dsp_wseq_clear(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq) +{ + struct cs_dsp_wseq_op *op, *op_tmp; + + list_for_each_entry_safe(op, op_tmp, &wseq->ops, list) { + list_del(&op->list); + devm_kfree(dsp->dev, op); + } +} + +static int cs_dsp_populate_wseq(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq) +{ + struct cs_dsp_wseq_op *op = NULL; + struct cs_dsp_chunk chunk; + u8 *words; + int ret; + + if (!wseq->ctl) { + cs_dsp_err(dsp, "No control for write sequence\n"); + return -EINVAL; + } + + words = kzalloc(wseq->ctl->len, GFP_KERNEL); + if (!words) + return -ENOMEM; + + ret = cs_dsp_coeff_read_ctrl(wseq->ctl, 0, words, wseq->ctl->len); + if (ret) { + cs_dsp_err(dsp, "Failed to read %s: %d\n", wseq->ctl->subname, ret); + goto err_free; + } + + INIT_LIST_HEAD(&wseq->ops); + + chunk = cs_dsp_chunk(words, wseq->ctl->len); + + while (!cs_dsp_chunk_end(&chunk)) { + op = devm_kzalloc(dsp->dev, sizeof(*op), GFP_KERNEL); + if (!op) { + ret = -ENOMEM; + goto err_free; + } + + op->offset = cs_dsp_chunk_bytes(&chunk); + op->operation = cs_dsp_chunk_read(&chunk, 8); + + switch (op->operation) { + case CS_DSP_WSEQ_END: + op->data = WSEQ_END_OF_SCRIPT; + break; + case CS_DSP_WSEQ_UNLOCK: + op->data = cs_dsp_chunk_read(&chunk, 16); + break; + case CS_DSP_WSEQ_ADDR8: + op->address = cs_dsp_chunk_read(&chunk, 8); + op->data = cs_dsp_chunk_read(&chunk, 32); + break; + case CS_DSP_WSEQ_H16: + case CS_DSP_WSEQ_L16: + op->address = cs_dsp_chunk_read(&chunk, 24); + op->data = cs_dsp_chunk_read(&chunk, 16); + break; + case CS_DSP_WSEQ_FULL: + op->address = cs_dsp_chunk_read(&chunk, 32); + op->data = cs_dsp_chunk_read(&chunk, 32); + break; + default: + ret = -EINVAL; + cs_dsp_err(dsp, "Unsupported op: %X\n", op->operation); + devm_kfree(dsp->dev, op); + goto err_free; + } + + list_add_tail(&op->list, &wseq->ops); + + if (op->operation == CS_DSP_WSEQ_END) + break; + } + + if (op && op->operation != CS_DSP_WSEQ_END) { + cs_dsp_err(dsp, "%s missing end terminator\n", wseq->ctl->subname); + ret = -ENOENT; + } + +err_free: + kfree(words); + + return ret; +} + +/** + * cs_dsp_wseq_init() - Initialize write sequences contained within the loaded DSP firmware + * @dsp: Pointer to DSP structure + * @wseqs: List of write sequences to initialize + * @num_wseqs: Number of write sequences to initialize + * + * Return: Zero for success, a negative number on error. + */ +int cs_dsp_wseq_init(struct cs_dsp *dsp, struct cs_dsp_wseq *wseqs, unsigned int num_wseqs) +{ + int i, ret; + + lockdep_assert_held(&dsp->pwr_lock); + + for (i = 0; i < num_wseqs; i++) { + ret = cs_dsp_populate_wseq(dsp, &wseqs[i]); + if (ret) { + cs_dsp_wseq_clear(dsp, &wseqs[i]); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_init, FW_CS_DSP); + +static struct cs_dsp_wseq_op *cs_dsp_wseq_find_op(u32 addr, u8 op_code, + struct list_head *wseq_ops) +{ + struct cs_dsp_wseq_op *op; + + list_for_each_entry(op, wseq_ops, list) { + if (op->operation == op_code && op->address == addr) + return op; + } + + return NULL; +} + +/** + * cs_dsp_wseq_write() - Add or update an entry in a write sequence + * @dsp: Pointer to a DSP structure + * @wseq: Write sequence to write to + * @addr: Address of the register to be written to + * @data: Data to be written + * @op_code: The type of operation of the new entry + * @update: If true, searches for the first entry in the write sequence with + * the same address and op_code, and replaces it. If false, creates a new entry + * at the tail + * + * This function formats register address and value pairs into the format + * required for write sequence entries, and either updates or adds the + * new entry into the write sequence. + * + * If update is set to true and no matching entry is found, it will add a new entry. + * + * Return: Zero for success, a negative number on error. + */ +int cs_dsp_wseq_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq, + u32 addr, u32 data, u8 op_code, bool update) +{ + struct cs_dsp_wseq_op *op_end, *op_new = NULL; + u32 words[WSEQ_OP_MAX_WORDS]; + struct cs_dsp_chunk chunk; + int new_op_size, ret; + + if (update) + op_new = cs_dsp_wseq_find_op(addr, op_code, &wseq->ops); + + /* If entry to update is not found, treat it as a new operation */ + if (!op_new) { + op_end = cs_dsp_wseq_find_op(0, CS_DSP_WSEQ_END, &wseq->ops); + if (!op_end) { + cs_dsp_err(dsp, "Missing terminator for %s\n", wseq->ctl->subname); + return -EINVAL; + } + + op_new = devm_kzalloc(dsp->dev, sizeof(*op_new), GFP_KERNEL); + if (!op_new) + return -ENOMEM; + + op_new->operation = op_code; + op_new->address = addr; + op_new->offset = op_end->offset; + update = false; + } + + op_new->data = data; + + chunk = cs_dsp_chunk(words, sizeof(words)); + cs_dsp_chunk_write(&chunk, 8, op_new->operation); + + switch (op_code) { + case CS_DSP_WSEQ_FULL: + cs_dsp_chunk_write(&chunk, 32, op_new->address); + cs_dsp_chunk_write(&chunk, 32, op_new->data); + break; + case CS_DSP_WSEQ_L16: + case CS_DSP_WSEQ_H16: + cs_dsp_chunk_write(&chunk, 24, op_new->address); + cs_dsp_chunk_write(&chunk, 16, op_new->data); + break; + default: + ret = -EINVAL; + cs_dsp_err(dsp, "Operation %X not supported\n", op_code); + goto op_new_free; + } + + new_op_size = cs_dsp_chunk_bytes(&chunk); + + if (!update) { + if (wseq->ctl->len - op_end->offset < new_op_size) { + cs_dsp_err(dsp, "Not enough memory in %s for entry\n", wseq->ctl->subname); + ret = -E2BIG; + goto op_new_free; + } + + op_end->offset += new_op_size; + + ret = cs_dsp_coeff_write_ctrl(wseq->ctl, op_end->offset / sizeof(u32), + &op_end->data, sizeof(u32)); + if (ret) + goto op_new_free; + + list_add_tail(&op_new->list, &op_end->list); + } + + ret = cs_dsp_coeff_write_ctrl(wseq->ctl, op_new->offset / sizeof(u32), + words, new_op_size); + if (ret) + goto op_new_free; + + return 0; + +op_new_free: + devm_kfree(dsp->dev, op_new); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_write, FW_CS_DSP); + +/** + * cs_dsp_wseq_multi_write() - Add or update multiple entries in a write sequence + * @dsp: Pointer to a DSP structure + * @wseq: Write sequence to write to + * @reg_seq: List of address-data pairs + * @num_regs: Number of address-data pairs + * @op_code: The types of operations of the new entries + * @update: If true, searches for the first entry in the write sequence with + * the same address and op_code, and replaces it. If false, creates a new entry + * at the tail + * + * This function calls cs_dsp_wseq_write() for multiple address-data pairs. + * + * Return: Zero for success, a negative number on error. + */ +int cs_dsp_wseq_multi_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq, + const struct reg_sequence *reg_seq, int num_regs, + u8 op_code, bool update) +{ + int i, ret; + + for (i = 0; i < num_regs; i++) { + ret = cs_dsp_wseq_write(dsp, wseq, reg_seq[i].reg, + reg_seq[i].def, op_code, update); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_multi_write, FW_CS_DSP); + 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 29cd11d5a3cf..4cef6fafa1d8 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -42,6 +42,16 @@ #define CS_DSP_ACKED_CTL_MIN_VALUE 0 #define CS_DSP_ACKED_CTL_MAX_VALUE 0xFFFFFF +/* + * Write sequence operation codes + */ +#define CS_DSP_WSEQ_FULL 0x00 +#define CS_DSP_WSEQ_ADDR8 0x02 +#define CS_DSP_WSEQ_L16 0x04 +#define CS_DSP_WSEQ_H16 0x05 +#define CS_DSP_WSEQ_UNLOCK 0xFD +#define CS_DSP_WSEQ_END 0xFF + /** * struct cs_dsp_region - Describes a logical memory region in DSP address space * @type: Memory region type @@ -255,6 +265,23 @@ 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_wseq - Describes a write sequence + * @ctl: Write sequence cs_dsp control + * @ops: Operations contained within + */ +struct cs_dsp_wseq { + struct cs_dsp_coeff_ctl *ctl; + struct list_head ops; +}; + +int cs_dsp_wseq_init(struct cs_dsp *dsp, struct cs_dsp_wseq *wseqs, unsigned int num_wseqs); +int cs_dsp_wseq_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq, u32 addr, u32 data, + u8 op_code, bool update); +int cs_dsp_wseq_multi_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq, + const struct reg_sequence *reg_seq, int num_regs, + u8 op_code, bool update); + /** * struct cs_dsp_chunk - Describes a buffer holding data formatted for the DSP * @data: Pointer to underlying buffer memory