#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/list.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <linux/mfd/arizona/registers.h>
#include "arizona.h"
#include "wm_adsp.h"
#define adsp_crit(_dsp, fmt, ...) \
dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
#define adsp_err(_dsp, fmt, ...) \
dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
#define adsp_warn(_dsp, fmt, ...) \
dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
#define adsp_info(_dsp, fmt, ...) \
dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
#define adsp_dbg(_dsp, fmt, ...) \
dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
#define ADSP1_CONTROL_1 0x00
#define ADSP1_CONTROL_2 0x02
#define ADSP1_CONTROL_3 0x03
#define ADSP1_CONTROL_4 0x04
#define ADSP1_CONTROL_5 0x06
#define ADSP1_CONTROL_6 0x07
#define ADSP1_CONTROL_7 0x08
#define ADSP1_CONTROL_8 0x09
#define ADSP1_CONTROL_9 0x0A
#define ADSP1_CONTROL_10 0x0B
#define ADSP1_CONTROL_11 0x0C
#define ADSP1_CONTROL_12 0x0D
#define ADSP1_CONTROL_13 0x0F
#define ADSP1_CONTROL_14 0x10
#define ADSP1_CONTROL_15 0x11
#define ADSP1_CONTROL_16 0x12
#define ADSP1_CONTROL_17 0x13
#define ADSP1_CONTROL_18 0x14
#define ADSP1_CONTROL_19 0x16
#define ADSP1_CONTROL_20 0x17
#define ADSP1_CONTROL_21 0x18
#define ADSP1_CONTROL_22 0x1A
#define ADSP1_CONTROL_23 0x1B
#define ADSP1_CONTROL_24 0x1C
#define ADSP1_CONTROL_25 0x1E
#define ADSP1_CONTROL_26 0x20
#define ADSP1_CONTROL_27 0x21
#define ADSP1_CONTROL_28 0x22
#define ADSP1_CONTROL_29 0x23
#define ADSP1_CONTROL_30 0x24
#define ADSP1_CONTROL_31 0x26
/*
* ADSP1 Control 19
*/
#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
/*
* ADSP1 Control 30
*/
#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
#define ADSP1_START 0x0001 /* DSP1_START */
#define ADSP1_START_MASK 0x0001 /* DSP1_START */
#define ADSP1_START_SHIFT 0 /* DSP1_START */
#define ADSP1_START_WIDTH 1 /* DSP1_START */
/*
* ADSP1 Control 31
*/
#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
#define ADSP2_CONTROL 0x0
#define ADSP2_CLOCKING 0x1
#define ADSP2_STATUS1 0x4
#define ADSP2_WDMA_CONFIG_1 0x30
#define ADSP2_WDMA_CONFIG_2 0x31
#define ADSP2_RDMA_CONFIG_1 0x34
/*
* ADSP2 Control
*/
#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
#define ADSP2_START 0x0001 /* DSP1_START */
#define ADSP2_START_MASK 0x0001 /* DSP1_START */
#define ADSP2_START_SHIFT 0 /* DSP1_START */
#define ADSP2_START_WIDTH 1 /* DSP1_START */
/*
* ADSP2 clocking
*/
#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
/*
* ADSP2 Status 1
*/
#define ADSP2_RAM_RDY 0x0001
#define ADSP2_RAM_RDY_MASK 0x0001
#define ADSP2_RAM_RDY_SHIFT 0
#define ADSP2_RAM_RDY_WIDTH 1
struct wm_adsp_buf {
struct list_head list;
void *buf;
};
static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
struct list_head *list)
{
struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (buf == NULL)
return NULL;
buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
if (!buf->buf) {
kfree(buf);
return NULL;
}
if (list)
list_add_tail(&buf->list, list);
return buf;
}
static void wm_adsp_buf_free(struct list_head *list)
{
while (!list_empty(list)) {
struct wm_adsp_buf *buf = list_first_entry(list,
struct wm_adsp_buf,
list);
list_del(&buf->list);
kfree(buf->buf);
kfree(buf);
}
}
#define WM_ADSP_NUM_FW 4
#define WM_ADSP_FW_MBC_VSS 0
#define WM_ADSP_FW_TX 1
#define WM_ADSP_FW_TX_SPK 2
#define WM_ADSP_FW_RX_ANC 3
static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
[WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
[WM_ADSP_FW_TX] = "Tx",
[WM_ADSP_FW_TX_SPK] = "Tx Speaker",
[WM_ADSP_FW_RX_ANC] = "Rx ANC",
};
static struct {
const char *file;
} wm_adsp_fw[WM_ADSP_NUM_FW] = {
[WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
[WM_ADSP_FW_TX] = { .file = "tx" },
[WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
[WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
};
struct wm_coeff_ctl_ops {
int (*xget)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int (*xput)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int (*xinfo)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
};
struct wm_coeff_ctl {
const char *name;
struct wm_adsp_alg_region region;
struct wm_coeff_ctl_ops ops;
struct wm_adsp *adsp;
void *private;
unsigned int enabled:1;
struct list_head list;
void *cache;
size_t len;
unsigned int set:1;
struct snd_kcontrol *kcontrol;
};
static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *